Referensi cepat unsupervised learning. Clustering, reduksi dimensi, PCA, K-Means, DBSCAN, evaluasi cluster, dan preprocessing data. Buat AI engineer yang mengolah data tanpa label.
Unsupervised learning mencari struktur pada data yang tidak punya target y. Kamu memberi matriks fitur X, lalu algoritme mencoba menemukan kelompok, arah variasi, atau titik yang menyimpang. Hasilnya bukan jawaban mutlak. Ia hipotesis yang perlu kamu cek dengan pengetahuan domain.
Pakai supervised learning saat kamu punya label seperti churn atau fraud. Pakai unsupervised learning saat kamu ingin membagi pelanggan, menemukan dokumen mirip, mengurangi ribuan fitur menjadi beberapa komponen, atau menandai transaksi aneh sebelum label tersedia.
| Tujuan | Keluaran umum | Contoh metode |
|---|---|---|
| Segmentasi | label cluster | K-Means, Gaussian Mixture, DBSCAN |
| Visualisasi atau kompresi | fitur berdimensi lebih kecil | PCA, TruncatedSVD, UMAP |
| Deteksi anomali | skor atau label anomali | Isolation Forest, Local Outlier Factor |
| Pencarian pola item | komponen laten atau aturan | NMF, topic modeling, association rules |
Jangan menganggap cluster sebagai fakta bisnis hanya karena grafiknya terlihat rapi. Jika satu cluster sulit dijelaskan, bisa jadi fitur, skala, atau metode yang kamu pilih tidak cocok.
Setiap kolom memberi definisi jarak. Kolom pendapatan bulanan, jumlah pesanan, dan hari sejak pesanan terakhir masuk akal untuk segmentasi pelanggan. ID pelanggan tidak. ID acak membuat dua baris tampak jauh padahal tidak ada arti bisnisnya.
Hapus target yang baru kamu ketahui setelah kejadian, misalnya is_churned, saat tujuanmu mencari kelompok pelanggan sebelum churn. Itu kebocoran informasi. Periksa juga duplikasi, unit berbeda, nilai hilang, dan kategori dengan ejaan yang tidak konsisten.
import pandas as pd
features = ["orders_90d", "spend_90d", "days_since_last_order"]
X = customers[features].copy()
# Nilai hilang tidak boleh masuk mentah ke K-Means atau PCA.
X = X.fillna(X.median())K-Means, PCA, nearest neighbors, dan DBSCAN berbasis jarak. Fitur berskala besar akan mendominasi hasil. Jika spend_90d bernilai jutaan dan orders_90d bernilai puluhan, Euclidean distance lebih banyak berbicara tentang uang daripada frekuensi pesanan.
StandardScaler memusatkan fitur ke rata-rata 0 dan skala deviasi standar 1. RobustScaler lebih tahan terhadap outlier karena memakai median dan interquartile range. Untuk data sangat miring, pertimbangkan log1p sebelum scaling.
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
X["spend_90d"] = np.log1p(X["spend_90d"])
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)Jangan menormalisasi kolom biner secara refleks. Untuk data campuran numerik dan kategorikal, ColumnTransformer lebih aman daripada memaksa semua kolom ke satu preprocessing yang sama.
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
preprocess = ColumnTransformer([
("num", StandardScaler(), ["orders_90d", "spend_90d"]),
("cat", OneHotEncoder(handle_unknown="ignore"), ["city", "channel"]),
])OneHotEncoder cocok untuk kategori nominal seperti kota atau channel akuisisi. Hati-hati pada kolom dengan ribuan nilai unik, misalnya nama produk atau email. One hot encoding dapat membuat matriks sangat lebar dan jarak antarbari menjadi kurang bermakna.
Untuk teks, gunakan TfidfVectorizer lalu cosine similarity atau TruncatedSVD. Pada data sparse seperti TF-IDF, jangan gunakan StandardScaler(with_mean=True) karena pemusatan akan mengubah matriks sparse menjadi padat dan boros memori.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import TruncatedSVD
vectorizer = TfidfVectorizer(stop_words="english", max_df=0.8, min_df=3)
X_tfidf = vectorizer.fit_transform(documents)
X_reduced = TruncatedSVD(n_components=100, random_state=42).fit_transform(X_tfidf)Pemilihan metrik jarak menentukan bentuk cluster yang bisa ditemukan.
| Metrik | Rumus ringkas | Cocok untuk |
|---|---|---|
| Euclidean | akar jumlah kuadrat selisih | data numerik berskala serupa |
| Manhattan | jumlah nilai absolut selisih | data numerik yang lebih tahan terhadap satu selisih besar |
| Cosine distance | 1 - cosine_similarity | dokumen, embedding, vektor arah |
| Hamming | jumlah posisi yang berbeda | fitur biner atau kategori yang sudah diencode |
| Jaccard distance | 1 - intersection / union | set tag, fitur biner yang sparse |
Euclidean distance sensitif pada outlier. Cosine similarity tidak peduli panjang vektor, hanya arah. Untuk embedding teks yang sudah L2 normalized, cosine dan Euclidean punya hubungan monotonic, tapi tetap gunakan metrik yang sesuai dengan cara embedding itu dilatih.
from sklearn.metrics.pairwise import cosine_similarity
similarities = cosine_similarity(embeddings)
nearest_index = similarities[0].argsort()[-6:-1][::-1]
print(nearest_index)K-Means membagi data menjadi k cluster dengan centroid. Algoritme bergantian antara dua langkah: tiap titik masuk ke centroid terdekat, lalu tiap centroid dipindah ke rata-rata titik anggotanya. Proses berhenti saat penugasan atau centroid stabil.
K-Means mencari nilai inertia kecil, yaitu jumlah kuadrat jarak setiap titik ke centroid clusternya. Metode ini paling masuk akal saat cluster cenderung bulat, padat, dan ukurannya mirip. Ia buruk untuk bentuk bulan sabit, kepadatan yang berbeda jauh, atau data dengan banyak outlier.
from sklearn.cluster import KMeans
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
model = Pipeline([
("scale", StandardScaler()),
("cluster", KMeans(n_clusters=4, n_init="auto", random_state=42)),
])
labels = model.fit_predict(X)
customers["cluster"] = labels
centers = model.named_steps["cluster"].cluster_centers_random_state membuat inisialisasi dapat diulang. n_init="auto" meminta scikit-learn menjalankan inisialisasi sesuai strategi yang dipakai. Untuk data penting, coba beberapa seed dan lihat apakah anggota cluster stabil. Satu nilai inertia tidak cukup untuk percaya diri.
Elbow method mencari titik saat tambahan cluster hanya sedikit menurunkan inertia. Namun elbow kadang tidak jelas. Silhouette score, stabilitas antarsampel, biaya operasional, dan keterbacaan segmen sama pentingnya.
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
scores = []
for k in range(2, 11):
labels = KMeans(n_clusters=k, n_init="auto", random_state=42).fit_predict(X_scaled)
scores.append({"k": k, "silhouette": silhouette_score(X_scaled, labels)})
print(pd.DataFrame(scores))Jangan mulai dari k=1 untuk silhouette. Metrik itu hanya terdefinisi jika ada minimal dua label dan jumlah label lebih kecil dari jumlah sampel.
Centroid dari data yang sudah distandardisasi sulit dibaca langsung. Profilkan label dengan data pada skala asli. Beri nama segmentasi setelah melihat angkanya, bukan sebelum proses clustering.
profile = (
customers.groupby("cluster")
.agg(
customers=("customer_id", "size"),
avg_orders=("orders_90d", "mean"),
avg_spend=("spend_90d", "mean"),
avg_recency=("days_since_last_order", "mean"),
)
.round(1)
)
print(profile)Agglomerative clustering mulai dari tiap titik sebagai cluster sendiri, lalu menggabungkan pasangan terdekat sampai jumlah cluster yang kamu tentukan. Hasilnya dapat ditampilkan sebagai dendrogram. Ini berguna saat kamu ingin melihat hubungan bertingkat, misalnya kategori produk.
linkage menentukan jarak antarcluster. ward cenderung membuat cluster padat dan hanya cocok untuk Euclidean distance. complete memakai jarak titik paling jauh. average memakai rata-rata jarak pasangan titik.
from sklearn.cluster import AgglomerativeClustering
hierarchical = AgglomerativeClustering(
n_clusters=4,
linkage="ward",
)
labels = hierarchical.fit_predict(X_scaled)Kelemahannya: biaya komputasi dapat besar saat sampel bertambah. Untuk jutaan baris, mulai dari MiniBatchKMeans atau lakukan sampling saat eksplorasi.
DBSCAN mencari area padat. Ia memakai eps sebagai radius tetangga dan min_samples sebagai jumlah titik minimum untuk menganggap area padat. Titik inti punya cukup tetangga. Titik batas menempel pada titik inti. Titik yang tidak masuk cluster diberi label -1 sebagai noise.
DBSCAN tidak meminta nilai k di awal dan bisa menemukan bentuk cluster tidak bulat. Tapi satu nilai eps sulit bekerja jika kepadatan tiap kelompok jauh berbeda. HDBSCAN sering lebih nyaman untuk data dengan kepadatan bervariasi, walau bukan bagian dari scikit-learn inti.
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
X_scaled = StandardScaler().fit_transform(X)
model = DBSCAN(eps=0.45, min_samples=8)
labels = model.fit_predict(X_scaled)
noise_rate = (labels == -1).mean()
print(f"Noise: {noise_rate:.1%}")Untuk menaksir eps, hitung jarak ke tetangga ke-min_samples, urutkan, lalu lihat titik tekuk pada grafik k-distance. Ini hanya titik awal. Uji parameter dan cek apakah cluster masih bisa dijelaskan.
from sklearn.neighbors import NearestNeighbors
import matplotlib.pyplot as plt
neighbors = NearestNeighbors(n_neighbors=8).fit(X_scaled)
distances, _ = neighbors.kneighbors(X_scaled)
k_distances = np.sort(distances[:, -1])
plt.plot(k_distances)
plt.ylabel("Jarak tetangga ke-8")
plt.xlabel("Sampel setelah diurutkan")
plt.show()Jangan memasukkan label noise ke silhouette score secara buta. Ukur dengan dan tanpa noise, lalu jelaskan keputusanmu. Menghapus noise bisa menaikkan skor sambil menyembunyikan masalah parameter.
Gaussian Mixture Model atau GMM menganggap data berasal dari campuran beberapa distribusi Gaussian. Berbeda dengan K-Means yang memberi satu label keras, GMM memberi probabilitas anggota setiap cluster. Ini berguna saat batas kelompok tidak tegas.
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(
n_components=4,
covariance_type="full",
n_init=5,
random_state=42,
)
gmm.fit(X_scaled)
labels = gmm.predict(X_scaled)
probabilities = gmm.predict_proba(X_scaled)
customers["cluster"] = labels
customers["cluster_confidence"] = probabilities.max(axis=1)covariance_type="full" memberi tiap komponen matriks kovarians sendiri. Lebih fleksibel, juga lebih mahal. diag menganggap fitur tidak berkorelasi dalam komponen. Pilih jumlah komponen dengan BIC atau AIC, lalu cek hasilnya secara domain.
rows = []
for k in range(1, 11):
model = GaussianMixture(n_components=k, n_init=3, random_state=42).fit(X_scaled)
rows.append({"k": k, "aic": model.aic(X_scaled), "bic": model.bic(X_scaled)})
print(pd.DataFrame(rows))Nilai AIC dan BIC lebih kecil lebih baik untuk perbandingan model pada data yang sama. Mereka bukan skor kualitas bisnis.
Principal Component Analysis atau PCA memutar ruang fitur ke arah dengan variasi terbesar. Komponen pertama menangkap variasi paling besar, komponen kedua menangkap variasi terbesar yang tegak lurus komponen pertama, lalu seterusnya. PCA sering dipakai sebelum K-Means untuk mengurangi noise dan mempercepat perhitungan.
PCA mengasumsikan hubungan linear dan sensitif terhadap skala, jadi standardisasi biasanya perlu. explained_variance_ratio_ memberi proporsi variasi yang ditangkap setiap komponen.
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
("scale", StandardScaler()),
("pca", PCA(n_components=0.9, random_state=42)),
])
X_pca = pipeline.fit_transform(X)
print(X_pca.shape)
print(pipeline.named_steps["pca"].explained_variance_ratio_)n_components=0.9 memilih komponen minimum yang menjelaskan setidaknya 90 persen variasi. Angka itu bukan aturan wajib. Untuk visualisasi dua dimensi, kamu sengaja membuang informasi. Jangan menilai kualitas cluster hanya dari plot 2D.
TruncatedSVD mirip PCA tetapi cocok untuk matriks sparse seperti TF-IDF karena tidak memusatkan data. t-SNE dan UMAP bagus untuk visualisasi struktur lokal, bukan alat default untuk feature engineering produksi. Posisi global pada plot t-SNE dapat menipu, ukuran cluster pada gambar juga tidak menyatakan ukuran populasi.
from sklearn.decomposition import TruncatedSVD
from sklearn.manifold import TSNE
X_svd = TruncatedSVD(n_components=100, random_state=42).fit_transform(X_tfidf)
X_plot = TSNE(n_components=2, perplexity=30, random_state=42).fit_transform(X_svd)Jika kamu memakai t-SNE setelah clustering, warnai titik dengan label yang sudah dihitung dari ruang fitur asli. Jangan menjalankan K-Means pada koordinat t-SNE lalu menganggap hasilnya final.
Silhouette coefficient tiap titik memakai a, rata-rata jarak ke titik dalam cluster sendiri, dan b, rata-rata jarak ke cluster terdekat lain.
silhouette = (b - a) / max(a, b)
Nilai mendekati 1 berarti titik dekat dengan clusternya dan jauh dari cluster lain. Nilai sekitar 0 berarti area tumpang tindih. Nilai negatif memberi sinyal titik lebih mirip cluster lain.
| Metrik | Arah baik | Catatan |
|---|---|---|
| Inertia | lebih kecil | selalu turun saat k naik |
| Silhouette | lebih besar | bias ke cluster padat dan terpisah |
| Davies-Bouldin | lebih kecil | membandingkan sebaran dengan jarak centroid |
| Calinski-Harabasz | lebih besar | rasio dispersi antarcluster terhadap dalam cluster |
from sklearn.metrics import (
calinski_harabasz_score,
davies_bouldin_score,
silhouette_score,
)
print("silhouette", silhouette_score(X_scaled, labels))
print("davies_bouldin", davies_bouldin_score(X_scaled, labels))
print("calinski_harabasz", calinski_harabasz_score(X_scaled, labels))Metrik internal mengukur geometri, bukan kegunaan. Dua cluster dengan silhouette tinggi tetap tidak berguna jika tim marketing tidak bisa mengambil tindakan berbeda untuk masing-masing kelompok.
Jika kamu punya label referensi untuk audit, jangan melatih dengan label itu lalu mengklaim unsupervised. Gunakan label hanya sesudah clustering untuk memeriksa kecocokan. Adjusted Rand Index atau ARI mengukur kesamaan dua partisi dengan koreksi peluang.
from sklearn.metrics import adjusted_rand_score
# audit_label tidak masuk ke X dan tidak dipakai saat fit.
ari = adjusted_rand_score(audit_label, labels)
print(ari)Anomali adalah observasi yang jauh atau jarang relatif terhadap data lain. Label anomali tidak selalu berarti fraud. Ia berarti data layak diperiksa.
from sklearn.ensemble import IsolationForest
anomaly_model = IsolationForest(
contamination=0.02,
random_state=42,
)
customers["anomaly"] = anomaly_model.fit_predict(X_scaled)
customers["anomaly_score"] = -anomaly_model.score_samples(X_scaled)
review_queue = customers.query("anomaly == -1").sort_values("anomaly_score", ascending=False)contamination adalah asumsi proporsi outlier untuk menentukan ambang. Jangan isi angka ini tanpa dasar. Jika fraud asli hanya muncul setelah investigasi, simpan keputusan reviewer untuk membangun dataset berlabel di iterasi berikutnya.
from sklearn.pipeline import Pipeline
from sklearn.cluster import KMeans
segmenter = Pipeline([
("preprocess", preprocess),
("cluster", KMeans(n_clusters=4, n_init="auto", random_state=42)),
])
segmenter.fit(train_customers)
new_labels = segmenter.predict(new_customers)Simpan versi fitur, parameter, tanggal data, dan aturan preprocessing. Tanpa itu, kamu tidak bisa menjelaskan kenapa pelanggan pindah cluster pada run berikutnya.
k yang memberi silhouette tertinggi selalu jawaban terbaik.| Istilah | Arti praktis |
|---|---|
| Centroid | Titik rata-rata anggota cluster pada K-Means. |
| Cluster | Kelompok observasi yang mirip menurut fitur dan metrik jarak yang kamu pilih. |
| Covariance | Ukuran bagaimana dua fitur berubah bersama. GMM memakai ini untuk bentuk komponen. |
| Curse of dimensionality | Jarak antar titik makin sulit dibedakan saat jumlah fitur sangat besar. |
| Density | Banyaknya titik dalam area lokal. DBSCAN memakai konsep ini untuk cluster. |
| Embedding | Vektor numerik yang mengkodekan makna atau karakteristik objek. |
| Inertia | Jumlah kuadrat jarak titik ke centroid clusternya dalam K-Means. |
| Noise | Titik yang DBSCAN tidak masukkan ke cluster, diberi label -1. |
| PCA | Metode linear untuk memadatkan fitur ke principal component. |
| Silhouette score | Ukuran seberapa dekat titik dengan clusternya dibanding cluster terdekat lain. |
| Standardisasi | Mengubah fitur agar rata-rata 0 dan deviasi standar 1. |
Login atau daftar akun gratis untuk membaca cheat sheet ini.