Algoritme clustering yang paling sering dipakai di industri juga termasuk yang paling sederhana: k-means membagi data menjadi k gumpalan, masing-masing diwakili satu titik pusat. Kisah kita berlanjut dengan 120 pelanggan toko ritel — fitur yang sama dengan chapter sebelumnya, jumlah data lebih besar. Targetnya konkret: memilih k yang tepat untuk segmentasi, memahami angka inertia_ yang dilaporkan model, dan mengenali jebakan solusi suboptimal yang bisa membuat hasil klaster tampak lebih buruk dari kemampuan algoritmenya.

Algoritme: Tetapkan, Perbarui, Ulangi #

Cara kerja k-means adalah perulangan dua langkah sederhana. Pertama, pilih k titik pusat awal (centroid). Lalu ulangi: tetapkan tiap titik data ke pusat terdekat, lalu pindahkan tiap pusat ke rata-rata seluruh anggotanya. Perulangan berhenti saat tidak ada titik yang berpindah klaster — pusat sudah stabil, dan itulah momen “konvergen”.

Dua karakter penting mengikuti desain ini. Pertama, k harus ditentukan di muka — algoritme tidak menemukan jumlah klaster sendiri. Kedua, hasil akhir bergantung pada posisi pusat awal: dua inisialisasi berbeda bisa berakhir pada partisi berbeda. Kita akan mengukur dan mengatasi masalah kedua secara eksplisit.

Inertia: Mengukur Kerapatan Klaster #

Kualitas satu solusi k-means diringkas oleh inertia: total jarak kuadrat dari tiap titik ke pusat klasternya.

text
inertia = Σi ||xi - μ(xi)||²

Notasi μ(xi) berarti pusat dari klaster tempat titik xi berada. Inertia kecil berarti klaster rapat — anggotanya dekat dengan pusatnya. Karena akumulasi jarak kuadrat dari seluruh titik, inertia peka terhadap skala fitur: jika kolom “nilai belanja” dinaikkan dari jutaan ke satuan rupiah, angkanya membengkak tanpa struktur data berubah sedikit pun. Chapter Skala Fitur dengan Scaling dan Normalisasi membahas solusinya; pada contoh kita dua fitur sudah berada pada rentang serupa sehingga perbandingannya adil.

Memilih k dengan Metode Elbow #

Inertia selalu turun setiap k ditambah — pusat baru pasti memperpendek jarak beberapa titik, sampai kasus ekstrem k = n saat tiap titik menjadi pusatnya sendiri dan inertia nol. Karena itu inertia saja tidak bisa memilih k. Yang kita cari adalah siku (elbow): titik saat penurunan inertia melambat tajam, artinya klaster tambahan tidak lagi membelah kelompok yang bermakna, hanya membelah-belah gumpalan yang sudah utuh.

Pada data 120 pelanggan kita, sweep k = 2..6 memperlihatkan siku di angka 3: penurunan dari k=2 ke k=3 sangat curam, sementara setelahnya landai. Angka ini kebetulan juga yang paling masuk akal secara bisnis — tiga segmen yang ditanam pembuat data. Elbow sering kabur di data nyata; indeks silhouette yang menilai pemisahan antar klaster menjadi pembanding kedua, dibahas tuntas di Chapter Evaluasi Clustering dengan Silhouette.

n_init dan Inisialisasi k-means++ #

KMeans pada scikit-learn menghadapi masalah pusat awal dengan dua mekanisme. k-means++ memilih pusat awal secara cerdas: pusat pertama diambil acak, pusat berikutnya cenderung dipilih dari titik yang jauh dari pusat yang sudah ada, sehingga kesebaran awalnya luas. n_init mengatur berapa kali algoritme dijalankan ulang dari awal; scikit-learn menyimpan run dengan inertia terkecil. Sejak versi 1.4, nilai bawaannya "auto": satu kali untuk init="k-means++" (sudah jarang terpeleset) dan sepuluh kali untuk init="random". Kita menuliskannya secara eksplisit demi kejelasan.

Mengapa restart diperlukan? Karena algoritme ini hanya menjamin solusi terbaik lokal: jika dua pusat awal kebetulan jatuh di gumpalan yang sama, satu gumpalan terbelah dua sementara dua gumpalan lain dipaksa berbagi satu pusat — konvergen, tetapi pada partisi yang buruk.

Asumsi yang Perlu Dijaga #

k-means secara diam-diam mengasumsikan klaster berbentuk gumpalan bulat berukuran serupa. Jarak dihitung lurus ke pusat, sehingga bentuk memanjang, lengkung seperti sabit, atau klaster dengan kepadatan berbeda jauh akan dipotong paksa. Data dua bulan sabit yang saling terkunci — kasus klasik — akan dibelah di tengah; Chapter DBSCAN memperlihatkan kegagalannya secara terukur lalu memperbaikinya. Untuk data besar, varian MiniBatchKMeans mempercepat pelatihan dengan sampel mini per iterasi.

Praktik: Elbow, Verifikasi Rumus, dan Efek Restart #

python
# k-Means: pusat klaster, inertia, elbow, dan jebakan local optimum.
# Kisah: 120 pelanggan toko ritel, fitur sama dengan chapter sebelumnya.

import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs

# Fitur: [kunjungan per bulan, rata-rata belanja per transaksi (juta Rp)].
PUSAT_SEG = [[2.0, 2.0], [7.0, 4.0], [5.0, 9.0]]
X, y_asli = make_blobs(
    n_samples=120, centers=PUSAT_SEG, cluster_std=1.0, random_state=42
)

# 1) Elbow: latih ulang untuk k = 2..6, catat inertia tiap k.
print("Sweep k=2..6 (init k-means++, n_init='auto'):")
print("  k   inertia     anggota per klaster")
for k in range(2, 7):
    km = KMeans(n_clusters=k, n_init="auto", random_state=42).fit(X)
    print(f"  {k}   {km.inertia_:>8.3f}   {np.bincount(km.labels_).tolist()}")

# 2) Model akhir k=3: pusat klaster vs pusat segmen sebenarnya.
km3 = KMeans(n_clusters=3, n_init="auto", random_state=42).fit(X)
pusat_asli = np.array([X[y_asli == c].mean(axis=0) for c in range(3)])
print(f"\nModel k=3: inertia = {km3.inertia_:.3f}")
print("Pemetaan klaster ke segmen (jarak pusat terdekat):")
for c in range(3):
    jarak = np.linalg.norm(pusat_asli - km3.cluster_centers_[c], axis=1)
    seg = int(np.argmin(jarak))
    p = km3.cluster_centers_[c]
    print(f"  klaster {c} pusat ({p[0]:.3f}, {p[1]:.3f}) "
          f"-> segmen {seg} (jarak {jarak[seg]:.3f})")

# 3) Verifikasi: hitung inertia secara manual dari definisinya.
#    inertia = total jarak kuadrat tiap titik ke pusat klasternya.
galat = X - km3.cluster_centers_[km3.labels_]
inertia_manual = float((galat ** 2).sum())
print(f"\nInertia manual (rumus) = {inertia_manual:.6f}")
print(f"Inertia model           = {km3.inertia_:.6f}")
print(f"Cocok? {abs(inertia_manual - km3.inertia_) < 1e-9}")

# 4) Jebakan local optimum: init acak SEKALI vs sepuluh kali restart.
jelek = KMeans(n_clusters=3, init="random", n_init=1, random_state=3).fit(X)
baik = KMeans(n_clusters=3, init="random", n_init=10, random_state=3).fit(X)
print("\nInit 'random', random_state=3 (bandingkan dua kebijakan restart):")
print(f"  n_init=1  -> inertia {jelek.inertia_:>8.3f}, "
      f"anggota {np.bincount(jelek.labels_).tolist()}")
print(f"  n_init=10 -> inertia {baik.inertia_:>8.3f}, "
      f"anggota {np.bincount(baik.labels_).tolist()}")

Output yang Dihasilkan:

text
Sweep k=2..6 (init k-means++, n_init='auto'):
  k   inertia     anggota per klaster
  2    815.524   [78, 42]
  3    220.353   [41, 39, 40]
  4    191.065   [41, 39, 26, 14]
  5    167.398   [23, 39, 26, 14, 18]
  6    145.861   [18, 39, 26, 14, 16, 7]

Model k=3: inertia = 220.353
Pemetaan klaster ke segmen (jarak pusat terdekat):
  klaster 0 pusat (6.923, 4.139) -> segmen 1 (jarak 0.075)
  klaster 1 pusat (4.955, 9.296) -> segmen 2 (jarak 0.080)
  klaster 2 pusat (1.827, 1.925) -> segmen 0 (jarak 0.000)

Inertia manual (rumus) = 220.353378
Inertia model           = 220.353378
Cocok? True

Init 'random', random_state=3 (bandingkan dua kebijakan restart):
  n_init=1  -> inertia  791.460, anggota [24, 78, 18]
  n_init=10 -> inertia  220.353, anggota [40, 39, 41]

Mari kita bedah hasil eksekusinya:

  1. Baris 17–19 (sweep k): inertia anjlok dari 815,524 (k=2) ke 220,353 (k=3) — penurunan 73% — lalu melandai ke 191, 167, 146. Siku di k=3 terlihat telak. Perhatikan pula kolom anggota: pada k=2 satu klaster menampung 78 pelanggan (dua segmen dipaksa berbagi), sedangkan pada k=3 pembagiannya 41/39/40, nyaris menyamai 40/40/40 yang ditanam.
  2. Baris 22–31 (pemetaan pusat): klaster 0 berpusat di (6,923; 4,139), paling dekat dengan segmen 1 — bukan segmen 0. Sekali lagi nomor klaster terbukti sembarang; yang bermakna adalah posisi pusatnya, dan ketiga posisi itu hanya meleset 0,000–0,080 dari pusat segmen sebenarnya.
  3. Baris 35–39 (verifikasi rumus): X - km3.cluster_centers_[km3.labels_] menghitung vektor galat tiap titik terhadap pusatnya; dikuadratkan lalu dijumlahkan seluruhnya, hasilnya 220,353378 — identik dengan km3.inertia_ hingga enam desimal. Angka API bukan kotak gelap: ia persis rumus yang kita tulis sendiri.
  4. Baris 42–48 (efek restart): dengan init="random" dan n_init=1, algoritme terjebak pada inertia 791,460 — dua pusat sama-sama melayani gumpalan besar (78 pelanggan memadat di satu klaster) sementara gumpalan lain terbelah 24/18. Sepuluh kali restart menemukan kembali solusi 220,353. Inisialisasi bukan detail kecil; ia menentukan partisi akhir.

k-means menuntut kita memutuskan k sebelum melihat hasil. Bagaimana jika kita ingin sebaliknya — membangun seluruh tangga pengelompokan dulu, baru memotongnya pada ketinggian yang kita inginkan? Itu tepatnya cara kerja Hierarchical Clustering, chapter berikutnya.

Referensi Resmi #