Trong thế giới dữ liệu, K-means giống như một cuộc dạo chơi đi tìm bạn thân theo sở thích thầm kín mà không cần nói ra. Bạn cứ lặng lẽ xếp vào nhóm có đặc điểm giống mình, để rồi phát hiện: “Ơ, hóa ra mình cũng là dân mê màu xanh lá cây và bánh sầu riêng!” 😄🥮
🎯 K-means là gì?
Hãy tưởng tượng bạn đang ở một buổi tiệc sinh nhật cực đông vui. Mọi người đến từ đủ vùng miền, mặc quần áo với đủ màu sắc và độ hài hước. Chủ nhà không muốn khách bị lạc lõng, nên quyết định chia họ thành K nhóm cho dễ giao lưu. Nhưng thay vì hỏi từng người thích gì, chủ nhà… chơi lớn bằng cách nhìn đặc điểm bên ngoài và chia nhóm theo điểm giống nhau.
📍 Quá trình K-means diễn ra như sau:
Đặt K: Chủ nhà chọn số nhóm (ví dụ: 3 nhóm).
Đặt ngẫu nhiên “trưởng nhóm”: Gọi là centroid, đại diện cho trung bình của mỗi nhóm.
Gán người vào nhóm gần nhất: Ai giống với trưởng nhóm nào nhất thì về chung nhóm đó.
Cập nhật trưởng nhóm: Sau khi nhóm hình thành, tính lại trung bình và chọn trưởng nhóm mới.
Lặp lại bước 3 và 4 đến khi… không ai đổi nhóm nữa — lúc đó tiệc đã chia đúng kiểu! 🎉
😄 Tóm gọn
K-means là cuộc thi “tìm đồng đội theo phong cách dữ liệu”. Bạn không cần khai tên, sở thích hay tính cách — chỉ cần trông giống nhau là về chung hội 🤝
Mỗi lần chạy K-means, dữ liệu như đang chơi speed-dating với centroids: gặp nhanh, ghép nhóm nhanh, rồi… yên bề dữ liệu!
✅ K-Means – phiên bản nghiêm túc:
K-Means là một thuật toán phân cụm đơn giản nhưng hiệu quả. Nó hoạt động bằng cách:
- Chọn số cụm K (do người dùng xác định trước).
- Khởi tạo K centroid (trung tâm cụm), có thể chọn ngẫu nhiên.
- Gán mỗi điểm dữ liệu vào cụm có centroid gần nhất.
- Cập nhật lại các centroid dựa trên trung bình của các điểm thuộc cụm.
- Lặp lại bước 3 và 4 cho đến khi các centroid không thay đổi (hoặc thay đổi nhỏ hơn ngưỡng cho phép).
🧩 Mẹo nhỏ khi dùng K-Means
Vấn đề | Giải pháp |
---|---|
Khởi tạo không tốt → kết quả tệ | Dùng KMeans++ thay vì ngẫu nhiên |
Cụm không hình tròn | Dùng thuật toán khác như DBSCAN hoặc Gaussian Mixture |
Không biết chọn K | Dùng Elbow hoặc Silhouette Score |
🍡 Elbow là gì?
Khi bạn cho K-means chia nhóm, mỗi nhóm sẽ có mức độ “lộn xộn” được gọi là inertia – tức là tổng khoảng cách từ các điểm dữ liệu đến trung tâm cụm của mình. Nói đơn giản: inertia càng thấp thì chia nhóm càng gọn gàng.
Nhưng nếu bạn cứ tăng số nhóm lên mãi, inertia sẽ luôn giảm — kiểu như chia nhiều nhóm thì mỗi nhóm ít người, dĩ nhiên sẽ… gọn hơn.
🎯 Vậy chọn K sao cho khéo?
- Chạy K-means với nhiều giá trị K (ví dụ từ 1 đến 10)
- Tính inertia cho mỗi K
- Vẽ đồ thị với trục hoành là K, trục tung là inertia
💡 Khi bạn nhìn vào đồ thị (xem phía dưới), sẽ thấy một đoạn giảm mạnh inertia — rồi sau đó giảm chậm dần. Điểm K tại “góc khuỷu tay” — nơi đồ thị chuyển từ dốc đứng sang thoải — chính là số cụm hợp lý. Vì sau điểm này, thêm cụm không giúp cải thiện nhiều nữa, chỉ tốn công phân chia!
Hãy tưởng tượng bạn đang chia bánh cho hội bạn:
- Ban đầu chia làm 1 nhóm → ai cũng cãi nhau → rất lộn xộn
- Chia làm 2, rồi 3 → mọi người bắt đầu ổn
- Tới khi chia 5 nhóm → ai cũng có phần riêng → nhưng quá rắc rối
📉 Biểu đồ lúc này có “góc khuỷu” ở K=3 — là lúc chia nhóm hợp lý mà vẫn còn vui!
Python code
Đầu tiên, chúng ta sử dụng thư viện sklearn
để tạo một tập dữ liệu giả gồm 300 điểm thuộc 4 nhóm — giống như bạn đang mô phỏng các nhóm khách hàng đến mua trà sữa từ 4 cửa hàng khác nhau
from sklearn.cluster import KMeans import matplotlib.pyplot as plt from sklearn.datasets import make_blobs # Tạo dữ liệu giả – giống như khách đến mua trà sữa X, _ = make_blobs(n_samples=300, centers=4, random_state=42) X[:5]
Output:
array([[ -9.29768866, 6.47367855], [ -9.69874112, 6.93896737], [ -1.68665271, 7.79344248], [ -7.09730839, -5.78133274], [-10.87645229, 6.3154366 ]])
Tiếp theo, chúng ta áp dụng K-means để chia các khách “ghiền trà sữa” thành 4 nhóm theo gu riêng, rồi dự đoán mỗi người thuộc nhóm nào như thể đang chọn bàn ngồi hợp vibe trong quán 😄🍹.
# K = 4 nhóm gu trà sữa kmeans = KMeans(n_clusters=4, random_state=42) kmeans.fit(X) y_kmeans = kmeans.predict(X) # Vẽ biểu đồ cho vui mắt plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='rainbow') plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], c='black', s=200, marker='X') # trưởng nhóm plt.title('Phân nhóm khách trà sữa bằng K-Means') plt.show()
Output:

🤔 Sao biết chọn K là bao nhiêu?
Trong ví dụ trên, ta chọn K = 4 vì ta mô phỏng dữ liệu với K =4 nhưng những trường hợp khác thì sao? Bạn không thể cứ đoán mò mãi được.
=> Dùng phương pháp Elbow – kiểu như “nhìn biểu đồ mà đoán điểm gập khuỷu tay”. Do đóm ta vẽ biểu đồ phương pháp Elbow — giống như bạn đang thử nhiều cách chia nhóm khách uống trà sữa, rồi nhìn độ “lộn xộn” để chọn ra số nhóm K hợp lý nhất. Khi biểu đồ tạo thành “góc khuỷu tay” (elbow), đó là lúc mô hình phân nhóm đã đủ ngon rồi 😄📊🍹
inertia = [] for k in range(1, 10): kmeans = KMeans(n_clusters=k) kmeans.fit(X) inertia.append(kmeans.inertia_) plt.plot(range(1, 10), inertia, marker='o') plt.title('Elbow Method: Tìm K hợp lý') plt.xlabel('Số cụm (K)') plt.ylabel('Inertia (mức độ lộn xộn)') plt.show()
Output:

🧪 Ví dụ K-Means trong R
👇 Bước 1: Tạo dữ liệu mẫu
# Cài đặt gói nếu cần # install.packages("ggplot2") # install.packages("cluster") library(ggplot2) # Tạo dữ liệu giả định set.seed(42) n <- 300 x <- data.frame( x = c(rnorm(n/3, mean = 0), rnorm(n/3, mean = 5), rnorm(n/3, mean = 2)), y = c(rnorm(n/3, mean = 0), rnorm(n/3, mean = 5), rnorm(n/3, mean = 7)) ) # Vẽ dữ liệu gốc ggplot(x, aes(x = x, y = y)) + geom_point() + ggtitle("Dữ liệu trước khi phân cụm")
Output:

🧠 Bước 2: Thực hiện K-Means
# Chạy thuật toán K-Means với K = 3 k <- 3 kmeans_result <- kmeans(x, centers = k) # Gắn nhãn cụm vào dữ liệu xcluster)
🎨 Bước 3: Vẽ kết quả
ggplot(x, aes(x = x, y = y, color = cluster)) + geom_point(size = 2) + geom_point(data = as.data.frame(kmeans_result*** QuickLaTeX cannot compile formula: centers), aes(x = x, y = y), color = "black", size = 5, shape = 8) + ggtitle("Kết quả phân cụm với K-Means (R)")</pre> <!-- /wp:syntaxhighlighter/code --> <!-- wp:image {"id":355,"sizeSlug":"full","linkDestination":"none"} --> <figure class="wp-block-image size-full"><img src="https://vi.ksml4.com/wp-content/uploads/2025/07/KMeans-r-2.png" alt="" class="wp-image-355"/></figure> <!-- /wp:image --> <!-- wp:heading {"level":3} --> <h3 class="wp-block-heading">📌 Phương pháp Elbow để chọn K</h3> <!-- /wp:heading --> <!-- wp:syntaxhighlighter/code --> <pre class="wp-block-syntaxhighlighter-code"># Tính tổng sai số trong (WSS) theo từng K wss <- numeric(10) for (k in 1:10) { wss[k] <- sum(kmeans(x[,1:2], centers = k) *** Error message: Cannot connect to QuickLaTeX server: cURL error 28: Operation timed out after 40000 milliseconds with 0 bytes received Please make sure your server/PHP settings allow HTTP requests to external resources ("allow_url_fopen", etc.) These links might help in finding solution: http://wordpress.org/extend/plugins/core-control/ http://wordpress.org/support/topic/an-unexpected-http-error-occurred-during-the-api-request-on-wordpress-3?replies=37withinss) } # Vẽ Elbow plot plot(1:10, wss, type = "b", pch = 19, frame = FALSE, xlab = "Số cụm K", ylab = "Tổng sai số trong (WSS)", main = "Elbow Method: Tìm K hợp lý")

Thử phân cụm lại với K = 4
# Chạy thuật toán K-Means với K = 4 k <- 4 kmeans_result <- kmeans(x, centers = k) # Gắn nhãn cụm vào dữ liệu xcluster) ggplot(x, aes(x = x, y = y, color = cluster)) + geom_point(size = 2) + geom_point(data = as.data.frame(kmeans_result$centers), aes(x = x, y = y), color = "black", size = 5, shape = 8) + ggtitle("Ket qua phan cum voi K-Means (R)")
