一、為什么需要 K-Means?
在監督學習中,我們總把數據寫成
(x, y)
,讓模型學習 x → y
的映射。
但現實中很多數據根本沒有標簽 y
,例如:
啤酒:熱量、鈉含量、酒精度、價格
用戶:訪問時長、點擊次數、消費金額
我們只想知道“這些樣本天然能分成幾類?”
這就是無監督學習——聚類。
K-Means 就是最經典、最易懂、跑得最快的聚類算法之一。
二、K-Means 的思想
隨機撒 k 個“種子”當類中心,
把每個樣本分給最近的種子,
再把種子移到新類的中心,
重復直到種子不再動。
三、算法流程圖解
選 k:先決定想聚幾類。
初始化:隨機或 k-means++ 選 k 個質心。
分配:每個樣本找最近的質心,形成 k 個簇。
更新:把每個簇的均值當成新質心。
收斂:質心移動小于閾值或達到最大迭代次數。
四、Python 實戰:啤酒聚類
項目目的:
在沒有人工標簽的情況下,把 20 種啤酒自動分成若干類別,并告訴你到底分幾類最合適
1. 數據準備
我們有一份啤酒數據,只有 4 個數值特征:
表格
啤酒 | 熱量(cal) | 鈉(mg) | 酒精(%) | 價格($) |
---|---|---|---|---|
A | 150 | 15 | 4.5 | 2.3 |
B | 100 | 10 | 3.0 | 1.8 |
… | … | … | … | … |
2.讀取 & 選特征
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as pltdata = pd.read_table("data.txt", sep=r'\s+', engine='python')
x = data[['calories', 'sodium', 'alcohol', 'cost']]
3.選 k:輪廓系數法
輪廓系數 (Silhouette Coefficient)
對每個樣本計算:
a
:到同簇其它點的平均距離b
:到最近外簇的平均距離s = (b - a) / max(a, b)
取值范圍:
[-1, 1]
≈ 1:聚得緊湊且遠離它簇
≈ 0:邊界模糊
< 0:可能分錯了簇
用平均輪廓系數挑 k
# 1. 準備一個空列表,用來存放不同 k 值對應的輪廓系數
scores = []# 2. 依次嘗試 k=2,3,...,9,看看聚成幾類效果最好
for k in range(2, 10):# 2-1 用當前 k 值做 K-Means 聚類# random_state=42 保證結果可重復labels = KMeans(n_clusters=k, random_state=42).fit(x).labels_# 2-2 計算聚類結果的平均輪廓系數(-1~1,越大越緊湊)scores.append(silhouette_score(x, labels))# 3. 把 k 與對應的輪廓系數畫成折線圖,方便肉眼找“峰值”
plt.plot(range(2, 10), scores, marker='o')# 4. 給圖加上坐標軸和標題
plt.xlabel("k") # 橫軸:聚類個數
plt.ylabel("Silhouette Score") # 縱軸:平均輪廓系數
plt.title("選擇最佳 k") # 圖標題
plt.show() # 顯示圖形
規則:選“峰值”對應的 k。
k=2 → 0.69 ← 最高
k=3 → 0.67
k=4 → 0.65
那么 k=2?最合適。
④ 正式聚類 & 結果保存
k-means算法中的重要參數:
參數 | 作用 | 常用值 |
---|---|---|
n_clusters | 類個數 | 根據業務或輪廓系數 |
init | 質心初始化 | 'k-means++' (默認)更快更穩 |
n_init | 隨機初始化跑幾次 | 10(默認) |
random_state | 復現實驗 | 任意整數 |
best_k = 2
km = KMeans(n_clusters=best_k, init='k-means++', n_init=10, random_state=42)
data['cluster'] = km.fit_predict(x)
print(data.head())
現在每一行都多了一個標簽 cluster = 0 1
,后續可以做市場細分、推薦策略等。
具體如圖所示:
20種啤酒被聚類成 0,1兩類