ML 6 — Học không giám sát: clustering, PCA & anomaly

13 thg 7, 2026 6 lượt xem
#machine-learning
#ai
#anomaly-detection
#pca
#clustering
#kmeans

ML 6 — Học không giám sát: clustering, PCA & anomaly

Ở các bài trước của series, ta luôn có một cột y — nhãn đúng để mô hình học theo và để đánh giá độ chính xác. Nhưng trong thực tế, phần lớn dữ liệu không có nhãn. Bạn có một triệu dòng giao dịch, hồ sơ khách hàng, log hệ thống — nhưng không ai đi gán tay "khách hàng này thuộc nhóm nào" hay "giao dịch này có gian lận không". Học không giám sát (unsupervised learning) là nhánh giải quyết đúng tình huống đó: tìm cấu trúc ẩn, gom nhóm tự nhiên, phát hiện điểm lạ — tất cả chỉ từ chính dữ liệu.

Bài này đi qua ba nhóm bài toán quan trọng nhất trong thực chiến: phân cụm (clustering), giảm chiều (dimensionality reduction) và phát hiện bất thường (anomaly detection), tất cả dùng scikit-learn.

Học không giám sát là gì?

Khác với học có giám sát (dự đoán y từ X), học không giám sát chỉ có X và mục tiêu là khám phá cấu trúc. Ba dạng chính:

  • Phân cụm (clustering): gom các mẫu giống nhau thành nhóm. Ví dụ: phân khúc khách hàng, gom nhóm sản phẩm.
  • Giảm chiều (dimensionality reduction): nén nhiều đặc trưng thành ít chiều hơn mà vẫn giữ được thông tin. Ví dụ: nén ảnh, trực quan hoá dữ liệu, tăng tốc mô hình.
  • Phát hiện bất thường (anomaly detection): tìm những mẫu "khác biệt" so với đám đông. Ví dụ: phát hiện gian lận, cảnh báo sự cố hệ thống.

Điểm khó nhất của học không giám sát là không có đáp án đúng để so. Không có accuracy, không có F1. Ta phải dùng các thước đo gián tiếp và — quan trọng hơn — phải diễn giải kết quả bằng kiến thức nghiệp vụ.

Sơ đồ trên là pipeline điển hình: scale → (PCA) → cluster → profile → hành động. Bước cuối cùng — biến cụm thành hành động kinh doanh — mới là thứ tạo giá trị.

K-Means: phân cụm phổ biến nhất

Ý tưởng

K-Means chia dữ liệu thành k cụm. Mỗi cụm có một tâm cụm (centroid). Thuật toán lặp: (1) gán mỗi điểm vào tâm gần nhất theo khoảng cách Euclid, (2) dịch chuyển tâm về trung bình các điểm trong cụm. Lặp đến khi ổn định. Mục tiêu là tối thiểu hoá tổng bình phương khoảng cách từ điểm tới tâm cụm của nó (inertia).

Hai điều bắt buộc: chọn k và scale

K-Means dùng khoảng cách, nên nếu một đặc trưng có thang đo lớn (ví dụ thu nhập tính bằng triệu đồng) và một đặc trưng thang nhỏ (số lần giao dịch/tháng), thì đặc trưng lớn sẽ áp đảo phép tính khoảng cách. Vì vậy phải scale dữ liệu trước — thường dùng StandardScaler (đưa mỗi cột về trung bình 0, phương sai 1). Đây là mắt xích nối trực tiếp với feature engineering: quên scale là lỗi kinh điển làm hỏng toàn bộ kết quả clustering.

Ngoài ra, K-Means cần bạn khai báo trước số cụm k. Đây là điểm yếu: bạn không biết trước nên chia thành mấy nhóm.

Chọn k: elbow và silhouette

Hai kỹ thuật phổ biến:

  • Elbow method: vẽ inertia theo k. Khi tăng k, inertia luôn giảm, nhưng đến một điểm sẽ giảm chậm lại — tạo hình "khuỷu tay" (elbow). Điểm khuỷu là gợi ý cho k.
  • Silhouette score: đo mức độ một điểm "khớp" với cụm của nó so với cụm gần nhất, giá trị từ -1 đến 1. Gần 1 nghĩa là cụm tách bạch tốt; gần 0 nghĩa là nằm ở ranh giới; âm nghĩa là có thể bị gán nhầm cụm. Ta chọn k cho silhouette cao nhất.

Hạn chế của K-Means

  • Giả định cụm có hình cầu và kích thước tương đương → xử lý kém với cụm hình dạng phức tạp (vòng cung, chuỗi dài).
  • Nhạy với khởi tạo: kết quả phụ thuộc vị trí tâm ban đầu (giảm bớt nhờ k-means++, mặc định trong scikit-learn).
  • Nhạy với outlier: một điểm cực trị có thể kéo lệch tâm cụm.
# (minh hoạ) K-Means + chọn k bằng silhouette
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

# X: ma trận đặc trưng khách hàng (chưa scale)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)          # BẮT BUỘC scale trước

scores = {}
for k in range(2, 9):
    km = KMeans(n_clusters=k, n_init=10, random_state=42)
    labels = km.fit_predict(X_scaled)
    scores[k] = silhouette_score(X_scaled, labels)

best_k = max(scores, key=scores.get)
print("Silhouette theo k:", scores)
print("Chọn k =", best_k)

# Huấn luyện lại với k tốt nhất
final = KMeans(n_clusters=best_k, n_init=10, random_state=42)
cluster_id = final.fit_predict(X_scaled)     # nhãn cụm cho từng khách hàng

Hierarchical clustering (phân cụm phân cấp)

Thay vì cố định k, phân cụm phân cấp xây một cây phân cấp các nhóm. Phổ biến nhất là agglomerative (gộp từ dưới lên): ban đầu mỗi điểm là một cụm riêng, rồi lặp lại việc gộp hai cụm gần nhau nhất cho đến khi còn một cụm.

Kết quả được biểu diễn bằng dendrogram — biểu đồ hình cây cho thấy thứ tự và "khoảng cách" khi gộp. Bạn "cắt" dendrogram ở một độ cao để chọn số cụm — cho phép quyết định k sau khi nhìn cấu trúc, thay vì đoán trước như K-Means.

Cách đo khoảng cách giữa hai cụm gọi là linkage:

  • ward: tối thiểu hoá phương sai nội cụm (thường cho cụm cân đối, mặc định tốt).
  • complete: khoảng cách xa nhất giữa hai cụm.
  • average: khoảng cách trung bình.
  • single: khoảng cách gần nhất (dễ tạo chuỗi dài).

Khi nào dùng? Khi bạn muốn hiểu cấu trúc phân cấp (ví dụ phân loại sản phẩm theo tầng), khi tập dữ liệu không quá lớn (thuật toán tốn bộ nhớ/thời gian với dữ liệu lớn), hoặc khi muốn quan sát dendrogram trước khi chốt số cụm.

DBSCAN: phân cụm theo mật độ

DBSCAN (Density-Based Spatial Clustering of Applications with Noise) tiếp cận hoàn toàn khác: cụm là vùng có mật độ điểm cao, ngăn cách bởi vùng thưa. Ưu điểm nổi bật:

  • Tự tìm số cụm — không cần khai báo k.
  • Chịu được nhiễu: các điểm không thuộc vùng dày đặc nào được đánh dấu là noise (nhãn -1) — chính là ứng viên bất thường.
  • Bắt được hình dạng bất kỳ: vòng cung, hình xoắn... không cần cụm hình cầu.

Hai tham số then chốt:

  • eps: bán kính lân cận. Một điểm là "core point" nếu trong bán kính eps có ít nhất min_samples điểm.
  • min_samples: số điểm tối thiểu để hình thành vùng dày đặc. Quy tắc thực dụng: đặt tối thiểu bằng 2 × số chiều của dữ liệu.

Cách chọn eps phổ biến: vẽ khoảng cách tới điểm lân cận thứ k cho mọi điểm, sắp xếp tăng dần, tìm điểm "gấp khúc" (knee) trên đồ thị — đó là gợi ý cho eps.

DBSCAN vs K-Means:

Tiêu chíK-MeansDBSCAN
Số cụmPhải khai báo kTự tìm
Hình dạng cụmCầu, cân đốiBất kỳ
Xử lý nhiễuGán tất cả vào cụmĐánh dấu noise
Cụm mật độ khác nhauỔnKhó (một eps cho tất cả)
Tốc độ dữ liệu lớnNhanhChậm hơn

DBSCAN kém khi các cụm có mật độ rất khác nhau vì một cặp eps/min_samples duy nhất khó vừa cho mọi cụm.

Đánh giá cụm

Vì không có nhãn, ta dùng các thước đo nội tại (chỉ dựa trên X và nhãn cụm):

  • Silhouette score (-1 → 1): càng cao càng tốt, đo độ tách bạch giữa các cụm.
  • Davies-Bouldin index (≥ 0): đo tỉ lệ độ phân tán trong cụm so với khoảng cách giữa các cụm; càng thấp càng tốt (cụm gọn và tách xa nhau).

Khi may mắn có nhãn thật để đối chiếu (ví dụ có sẵn phân loại đúng để kiểm định thuật toán), dùng thước đo ngoại tại:

  • Adjusted Rand Index (ARI): so nhãn cụm với nhãn thật, đã hiệu chỉnh cho yếu tố ngẫu nhiên; 1 là khớp hoàn hảo, ~0 là ngẫu nhiên.

Nhưng thước đo con số không đủ. Bước quan trọng nhất là profiling cụm: với mỗi cụm, tính trung bình/phân bố các đặc trưng gốc để trả lời "cụm này là ai?". Ví dụ: "Cụm 2 = khách hàng tuổi cao, số dư lớn, ít giao dịch online". Chỉ khi diễn giải được cụm bằng ngôn ngữ nghiệp vụ thì mới hành động được (gửi campaign gì, chăm sóc ra sao). Một mô hình silhouette cao nhưng không diễn giải nổi thì vô dụng.

PCA: giảm chiều

PCA (Principal Component Analysis) tìm các trục mới (thành phần chính) là tổ hợp tuyến tính của các đặc trưng gốc, sắp theo thứ tự giữ nhiều phương sai nhất. Giữ lại vài thành phần đầu là ta nén dữ liệu mà mất ít thông tin.

Lợi ích:

  • Nén đặc trưng: từ 50 chiều xuống còn 5-10 chiều.
  • Khử tương quan: các thành phần chính vuông góc (không tương quan) với nhau.
  • Trực quan hoá: chiếu dữ liệu về 2-3 chiều để vẽ.
  • Giảm nhiễu & tăng tốc: ít chiều hơn giúp mô hình phía sau (kể cả clustering) chạy nhanh và bớt "lời nguyền số chiều".

Thuộc tính explained_variance_ratio_ cho biết mỗi thành phần giữ bao nhiêu phần trăm phương sai. Thường ta chọn số thành phần sao cho tổng phương sai giữ lại ≈ 90-95%.

PCA cũng cần scale trước — vì nó dựa trên phương sai, đặc trưng thang lớn sẽ chiếm ưu thế nếu không chuẩn hoá.

PCA khác feature selection: feature selection giữ nguyên một tập con các đặc trưng gốc (vẫn diễn giải được từng cột), còn PCA tạo đặc trưng mới là trộn của nhiều cột — nén tốt hơn nhưng khó diễn giải hơn.

# (minh hoạ) PCA giảm chiều trước khi cluster
from sklearn.decomposition import PCA

pca = PCA(n_components=0.95)          # giữ 95% phương sai
X_pca = pca.fit_transform(X_scaled)   # nhớ: X_scaled đã được scale

print("Số chiều sau PCA:", X_pca.shape[1])
print("Phương sai từng thành phần:", pca.explained_variance_ratio_.round(3))
print("Tổng phương sai giữ lại:", pca.explained_variance_ratio_.sum().round(3))

Phát hiện bất thường (anomaly detection)

Mục tiêu: tìm các mẫu hiếm và khác biệt. Ba phương pháp thông dụng trong scikit-learn:

  • Isolation Forest: xây nhiều cây bằng cách chọn ngẫu nhiên một đặc trưng và một ngưỡng cắt. Điểm bất thường dễ bị "cô lập" hơn — cần ít lần cắt hơn để tách riêng, nên có độ sâu đường đi (path length) ngắn. Rất hiệu quả, mở rộng tốt với dữ liệu lớn.
  • Local Outlier Factor (LOF): so mật độ cục bộ quanh một điểm với mật độ của các điểm lân cận. Điểm ở vùng thưa hơn hẳn xung quanh bị coi là outlier. Tốt khi mật độ dữ liệu không đồng đều.
  • One-Class SVM: học biên bao quanh vùng dữ liệu "bình thường"; điểm nằm ngoài biên là bất thường. Mạnh nhưng nhạy tham số và chậm với dữ liệu lớn.

Điểm chung: các mô hình này cho ra điểm số bất thường, và bạn phải chọn ngưỡng để quyết định đâu là "lạ". Isolation Forest dùng tham số contamination để ước lượng tỉ lệ bất thường kỳ vọng; đặt quá cao sẽ báo động giả nhiều, quá thấp sẽ bỏ sót. Ngưỡng nên được hiệu chỉnh theo khả năng xử lý của đội nghiệp vụ (điều tra được bao nhiêu case/ngày).

# (minh hoạ) Isolation Forest phát hiện giao dịch bất thường
from sklearn.ensemble import IsolationForest

iso = IsolationForest(
    n_estimators=200,
    contamination=0.01,   # ~1% giao dịch nghi ngờ
    random_state=42,
)
iso.fit(X_scaled)

pred = iso.predict(X_scaled)       #  1 = bình thường, -1 = bất thường
scores = iso.score_samples(X_scaled)  # điểm càng thấp càng "lạ"

anomalies = X[pred == -1]
print("Số giao dịch nghi ngờ:", (pred == -1).sum())

Ứng dụng ngân hàng

Học không giám sát cực kỳ giá trị trong ngân hàng vì phần lớn dữ liệu (giao dịch, hành vi khách) không có nhãn sẵn:

  • Phân khúc khách hàng: gom khách theo hành vi giao dịch, số dư, độ gắn bó để cá nhân hoá sản phẩm. Đây chính là nền của loyalty segmentation theo RFM — K-Means trên ba trục Recency/Frequency/Monetary là công thức kinh điển.
  • Phát hiện giao dịch bất thường: Isolation Forest / LOF trên đặc trưng giao dịch (số tiền, tần suất, địa điểm, giờ giao dịch) để đánh cờ nghi vấn gian lận, chuyển sang đội điều tra.

Use case thực tế

1. Phân khúc khách hàng để marketing. Ngân hàng có 500 nghìn khách nhưng không biết chia nhóm thế nào để gửi campaign. Pipeline: tính đặc trưng RFM + độ tuổi + kênh giao dịch ưa thích → StandardScaler → K-Means với k chọn bằng silhouette → profiling từng cụm. Kết quả có thể là: "Trẻ - giao dịch online nhiều - số dư thấp" (đẩy app & thẻ tín dụng nhập môn), "Trung niên - số dư cao - ít online" (mời gói tiết kiệm/đầu tư qua nhân viên). Mỗi cụm nhận một thông điệp riêng → tỉ lệ chuyển đổi tăng rõ so với gửi đại trà.

2. Phát hiện giao dịch bất thường. Với dòng giao dịch không nhãn gian lận, huấn luyện Isolation Forest trên các đặc trưng đã scale, đặt contamination khớp năng lực điều tra. Các giao dịch bị đánh -1 được đẩy vào hàng đợi review. Đây là lớp sàng lọc đầu tiên; những case xác nhận đúng có thể trở thành nhãn cho một mô hình có giám sát mạnh hơn về sau — vòng lặp cải tiến dần.

Cạm bẫy thường gặp

  • Quên scale: lỗi phổ biến nhất. K-Means, DBSCAN, PCA đều dựa trên khoảng cách/phương sai — không scale thì đặc trưng thang lớn áp đảo, kết quả vô nghĩa.
  • Diễn giải cụm tùy tiện: gán tên cụm theo cảm tính mà không profiling bằng dữ liệu. Luôn kiểm chứng bằng thống kê đặc trưng gốc.
  • Chọn k bừa: đặt k theo linh cảm thay vì dùng elbow/silhouette. Với DBSCAN thì chọn eps/min_samples không có cơ sở.
  • Tin vào một con số duy nhất: silhouette cao chưa đảm bảo cụm hữu ích về nghiệp vụ. Kết hợp nhiều thước đo và luôn hỏi "cụm này hành động được không?".
  • Bỏ qua ngưỡng trong anomaly: contamination sai làm ngập báo động giả hoặc bỏ sót. Hiệu chỉnh theo thực tế vận hành.

Ghi nhớ

  • Học không giám sát tìm cấu trúc từ dữ liệu không nhãn: phân cụm, giảm chiều, phát hiện bất thường.
  • K-Means nhanh, cần chọn k (elbow/silhouette) và phải scale; giả định cụm hình cầu, nhạy outlier.
  • Hierarchical cho cấu trúc phân cấp qua dendrogram, chốt k sau; hợp dữ liệu nhỏ.
  • DBSCAN tự tìm số cụm, bắt hình dạng bất kỳ, tách nhiễu; điều chỉnh eps/min_samples.
  • Đánh giá cụm bằng silhouette, Davies-Bouldin (và ARI khi có nhãn), nhưng profiling mới quyết định giá trị.
  • PCA nén chiều giữ phương sai, khử tương quan, cần scale; khác feature selection.
  • Isolation Forest / LOF / One-Class SVM phát hiện bất thường; luôn phải chọn ngưỡng.
  • Nối tiếp: đưa mô hình vào vận hành ở bài MLOps & triển khai.

Nguồn tham khảo: scikit-learn — IsolationForest, scikit-learn — PCA, scikit-learn — davies_bouldin_score, Clustering Metrics — GeeksforGeeks.

Bài viết liên quan

Phân biệt AI/ML/DL, các kiểu học máy, quy trình ML end-to-end và thuật ngữ nền tảng.

13 thg 7, 2026 14

Chất lượng dữ liệu, xử lý thiếu/ngoại lai, mã hoá, chuẩn hoá, tạo đặc trưng và tránh data leakage.

13 thg 7, 2026 13

Mô hình ngôn ngữ lớn hoạt động ra sao, token, context window, tham số sinh, và kỹ thuật viết prompt.

13 thg 7, 2026 13

Retrieval-Augmented Generation, vector database, tool calling, agent và MCP để xây ứng dụng LLM thực tế.

13 thg 7, 2026 11