K-means聚類的原理。
K-Means演算法的目標是將原始資料分為K簇,每一簇都有一箇中心點,這也是簇中點的均值點,簇中所有的點到所屬的簇的中心點的距離都比到其他簇的中心點更近。
K-means聚類的演算法流程。
1、隨機確定K個點作為質心。
2、找到離每個點最近的質心,將這個點分配到這個質心代表的簇裡。
3、再對每個簇進行計算,以點簇的均值點作為新的質心。
4、如果新的質心和上一輪的不一樣,則迭代進行2-3步驟,直到質心位置穩定。
程式碼介紹
使用 make_blobs 函式生成一個二維的隨機資料集,包含120個樣本和3箇中心。生成的資料將用於後續的聚類分析。
資料生成後,透過散點圖視覺化這些點,展示不同的聚類中心。
定義了一個函式 calculate_sse,用於計算給定聚類數 k 時的誤差平方和(SSE)。該函式使用K-Means演算法進行聚類,並返回聚類後的SSE值。
# 匯入必要的庫
import numpy as np # 用於數值計算
import matplotlib.pyplot as plt # 用於資料視覺化
from sklearn.datasets import make_blobs # 用於生成聚類資料
from sklearn.cluster import KMeans # 用於K-Means聚類演算法
# 1. 生成資料
X, y_true = make_blobs(n_samples=120, n_features=2, centers=3, random_state=42) # 生成120個樣本,2個特徵,3個聚類中心
# 視覺化生成的資料
# 設定matplotlib的字型為中文和負號的顯示
plt.rcParams["font.sans-serif"] = ["SimHei"] # 使繪圖時中文正確顯示
plt.rcParams["axes.unicode_minus"] = False # 使繪圖時負號正確顯示
plt.scatter(X[:, 0], X[:, 1], s=30) # 繪製散點圖
plt.title("初始資料") # 圖表標題
plt.xlabel("特徵 1") # x軸標籤
plt.ylabel("特徵 2") # y軸標籤
plt.show() # 顯示圖形
# 2. 定義計算SSE的函式
def calculate_sse(X, k):
kmeans = KMeans(n_clusters=k, random_state=42) # 初始化K-Means演算法,指定聚類數量k
kmeans.fit(X) # 進行聚類
sse = kmeans.inertia_ # 獲取聚類的誤差平方和(SSE)
return sse # 返回SSE值
# 3. 執行聚類並記錄SSE
sse_list = [] # 初始化一個列表用於儲存不同K值的SSE
k_values = range(1, 11) # 定義K的範圍,從1到10
n_trials = 5 # 每個K值進行5次實驗以降低隨機性影響
for k in k_values: # 遍歷每一個K值
sse_trials = [] # 記錄每個K值的SSE試驗結果
for _ in range(n_trials): # 進行n_trials次實驗
sse = calculate_sse(X, k) # 計算當前K值下的SSE
sse_trials.append(sse) # 將結果儲存到列表
avg_sse = np.mean(sse_trials) # 計算當前K值的平均SSE
sse_list.append(avg_sse) # 將平均SSE儲存到sse_list中
# 4. 輸出K值與平均SSE關係資料
print("K值與平均SSE關係:") # 列印說明
for k, sse in zip(k_values, sse_list): # 遍歷K值及其對應的平均SSE
print(f"K = {k}, 平均 SSE = {sse:.2f}") # 格式化輸出K值及其對應的平均SSE
# 將輸出儲存到檔案
with open("sse_results.txt", "w") as file: # 開啟檔案以寫入
file.write("K值與平均SSE關係:\n") # 寫入檔案標題
for k, sse in zip(k_values, sse_list): # 遍歷K值及其對應的平均SSE
file.write(f"K = {k}, 平均 SSE = {sse:.2f}\n") # 儲存K值和平均SSE到檔案中
print("SSE資料已儲存到 sse_results.txt 檔案中") # 輸出儲存成功的提示資訊
# 5. 繪製K值與平均SSE關係圖
plt.plot(k_values, sse_list, marker='o') # 繪製K值與平均SSE的折線圖,並使用圓點標記
plt.title("平均SSE vs 簇數量(K)") # 圖表標題
plt.xlabel("簇個數(K)") # x軸標籤
plt.ylabel("平均 SSE") # y軸標籤
plt.xticks(k_values) # 設定x軸刻度
plt.grid() # 新增網格
plt.show() # 顯示圖形
# 6. 使用最佳K值重新進行聚類並視覺化結果
best_k = 3 # 假設最優的K值為3
kmeans = KMeans(n_clusters=best_k, random_state=42) # 初始化K-Means演算法
y_kmeans = kmeans.fit_predict(X) # 進行聚類並返回每個樣本的聚類標籤
# 視覺化聚類結果
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=30, cmap='viridis') # 繪製聚類結果的散點圖,顏色代表不同聚類
centers = kmeans.cluster_centers_ # 獲取聚類中心
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.75, marker='X') # 繪製聚類中心
plt.title(f"K={best_k}的K- means聚類") # 圖表標題
plt.xlabel("特徵 1") # x軸標籤
plt.ylabel("特徵 2") # y軸標籤
plt.show() # 顯示聚類結果圖
執行結果:
為了確定最佳的聚類數量K,程式碼計算從1到10的不同K值對應的平均SSE。每個K值重複進行5次實驗,以減少隨機性對結果的影響。
計算好的平均SSE值被儲存在一個列表中,並最終輸出至控制檯以顯示K值與SSE的關係。
將K值與平均SSE的關係寫入到一個文字檔案中,方便後續檢視和分析。
使用折線圖將K值與對應的平均SSE視覺化,直觀展示聚類數量對聚類效果的影響。
由左至右k=1,2,3,4,5,6,7,SSE會隨著K的變大而減小。可以看出在k= 3之後,隨著k的增大,SSE的下降減緩了,再增加K得到的聚合回報變小,也就是k=3應該為最佳聚類數。
結合其他k值的影像,當K=2時,資料點的分類其實是不充分的。而當K=4時,資料點上方十分密集的資料點卻經常因為右上角噪音點的影響被從中間分成兩簇,這是不符合實際情況的,當k>3時,聚類結果就會顯得愈加雜亂。結合影像,K=3仍然是最好的選擇。
由圖得出最佳的K值為3,重新使用K-Means演算法進行聚類,並視覺化聚類結果。每個聚類用不同的顏色標記,並且繪製出聚類中心,以便清晰瞭解聚類的效果。