1. 程式人生 > >【SciKit-Learn學習筆記】8:k-均值演算法做文字聚類,聚類演算法效能評估

【SciKit-Learn學習筆記】8:k-均值演算法做文字聚類,聚類演算法效能評估

學習《scikit-learn機器學習》時的一些實踐。


原理見K-means和K-means++的演算法原理及sklearn庫中引數解釋、選擇

sklearn中的KMeans

from sklearn.datasets import make_blobs
from matplotlib import pyplot as plt
from sklearn.cluster import KMeans


def show_sample():
    """展示樣本點"""
    plt.figure(figsize=(6, 4), dpi=100)
    plt.xticks(
[]) plt.yticks([]) plt.scatter(X[:, 0], X[:, 1], s=20, marker='o') plt.show() def fit_plot_kmeans_model(k, X): """使樣本X聚k類,並繪製圖像""" kmeans = KMeans(n_clusters=k) kmeans.fit(X) # 這裡score得到的是一個負數,其絕對值越大表示成本越高 # sklearn中對該成本的計算為:樣例到其所屬的聚類中心點的距離總和(而不是平均值) plt.title("k={},成本={}"
.format(k, kmeans.score(X))) # 聚類得到的類別標籤,這裡都用從0開始的自然數表示 labels = kmeans.labels_ assert len(labels) == len(X) # 聚類中心 centers = kmeans.cluster_centers_ assert len(centers) == k markers = ['o', '^', '*', 's'] colors = ['r', 'b', 'y', 'k'] # 對每一個類別 for i in range(k)
: # 繪製該類對應的樣本 cluster = X[labels == i] plt.scatter(cluster[:, 0], cluster[:, 1], marker=markers[i], s=20, c=colors[i]) # 繪製聚類中心點 plt.scatter(centers[:, 0], centers[:, 1], marker='o', c='white', alpha=0.9, s=300) # 在中心點大白點(位置cnt)上繪製類別號i for i, cnt in enumerate(centers): plt.scatter(cnt[0], cnt[1], marker="$%d$" % i, s=50, c=colors[i]) if __name__ == '__main__': # 生成標準差為1的200個聚4類的2維樣本點,聚類中心隨機生成且每個維度都在-10到10的範圍,最終將生成的兩樣本打亂 X, y = make_blobs(n_samples=200, n_features=2, centers=4, cluster_std=1, center_box=(-10.0, 10.0), shuffle=True, random_state=1) # 聚類的類別數 n_clusters = [2, 3, 4] plt.figure(figsize=(10, 3), dpi=100) # plt.xticks([]) # plt.yticks(()) for i, k in enumerate(n_clusters): plt.subplot(1, len(n_clusters), i + 1) fit_plot_kmeans_model(k, X) plt.show()

在這裡插入圖片描述

k-均值演算法做文字聚類

from sklearn.datasets import load_files
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
from sklearn import metrics

# 讀取資料
docs = load_files("E:\Data\code\datasets\clustering\data")
data = docs.data
target_names = docs.target_names
print("summary: {} documents in {} categories.".format(len(data), len(target_names)))

summary: 3949 documents in 4 categories.

# 生成詞典並將文件轉化為TF-IDF向量
# 在生成詞典時,過濾超過max_df比例(或數目)或者min_df比例(或數目)的詞,最大保留20000個特徵,編碼ISO-8859-1
vectorizer = TfidfVectorizer(max_df=0.4, min_df=2, max_features=20000, encoding='latin-1')
X = vectorizer.fit_transform(data)
print("n_samples:{},n_features:{}".format(*X.shape))
print("0號樣本中的非零特徵數目:", X[0].getnnz())

n_samples:3949,n_features:20000
0號樣本中的非零特徵數目: 56

k = 4
# 引數n_init即是多次選取不同的初始化聚類中心,而最終輸出的是score最大(成本最小)的聚類
# 引數max_iter指定一次KMeans過程中最大的迴圈次數,即便聚類中心還可以移動,到達這個最大次數也結束
# 引數tol決定中心點移動距離小於多少時認定演算法已經收斂
kmeans = KMeans(n_clusters=k, max_iter=100, tol=0.01, verbose=0, n_init=3)
kmeans.fit(X)
print("k={},cost={}".format(k, "%.3f" % kmeans.inertia_))

k=4,cost=3816.220

# 檢視1000~1009這10個文件的聚類結果及其本來的檔名,可以看一下目錄一樣的也就是同一類的
print(kmeans.labels_[1000:1010])
print(docs.filenames[1000:1010])

[1 1 1 2 0 2 2 1 2 2]
[‘E:\Data\code\datasets\clustering\data\sci.crypt\10888-15289’
‘E:\Data\code\datasets\clustering\data\sci.crypt\11490-15880’
‘E:\Data\code\datasets\clustering\data\sci.crypt\11270-15346’
‘E:\Data\code\datasets\clustering\data\sci.electronics\12383-53525’
‘E:\Data\code\datasets\clustering\data\sci.space\13826-60862’
‘E:\Data\code\datasets\clustering\data\sci.electronics\11631-54106’
‘E:\Data\code\datasets\clustering\data\sci.space\14235-61437’
‘E:\Data\code\datasets\clustering\data\sci.crypt\11508-15928’
‘E:\Data\code\datasets\clustering\data\sci.space\13593-60824’
‘E:\Data\code\datasets\clustering\data\sci.electronics\12304-52801’]

# 檢視每種類別文件中,影響最大(即那個維度數值最大)的10個單詞
# 對得到的每個聚類中心點進行排序得到排序索引,這裡"::-1"使其按照從大到小排序
order_centroids = kmeans.cluster_centers_.argsort()[:, ::-1]
# 顯然越排在前面的對應的索引值所對應的單詞影響越大
# 取出詞典中的詞
terms = vectorizer.get_feature_names()
# 對每個聚類結果i
for i in range(k):
    print("Cluster %d" % i, end='')
    # 取出第i行(也就是第i個聚類中心點)前10重要的詞的索引
    for ind in order_centroids[i, :10]:
        # 在詞典term中可以按這個索引拿到對應的詞
        print(" %s" % terms[ind], end='')
    print()

Cluster 0 henry toronto zoo spencer hst zoology mission utzoo orbit space
Cluster 1 key clipper chip encryption government keys will escrow we algorithm
Cluster 2 space by any my will know like some nasa we
Cluster 3 my she msg pitt he your has do her gordon

# 評價聚類表現
label_true = docs.target  # 標記的類別
label_pred = kmeans.labels_  # 聚類得到的類別
print("齊次性: %.3f" % metrics.homogeneity_score(label_true, label_pred))
print("完整性: %.3f" % metrics.completeness_score(label_true, label_pred))
print("V-measure: %.3f" % metrics.v_measure_score(label_true, label_pred))
print("Adjust Rand Index: %.3f" % metrics.adjusted_rand_score(label_true, label_pred))
print("輪廓係數: %.3f" % metrics.silhouette_score(X, label_pred, sample_size=1000))

齊次性: 0.352
完整性: 0.481
V-measure: 0.406
Adjust Rand Index: 0.250
輪廓係數: 0.005

聚類演算法效能評估

聚類因為得到的類別和標註類別未必有什麼關係(也許有一定程度的對應關係,如上面的文字聚類和實際標籤的比較),不能用分類的MSE損失等方法來對其評估。

而sklearn裡聚類模型的score值也沒有一個確切的範圍,不像分類模型中總是從0到1之間,所以單純的看這個score值也沒有太大用處。

以下方法中,只有輪廓係數是不基於標註標籤對聚類效能做評估的方法,其它方法都使用了標註標籤。

Adjust Rand Index

該方法可以衡量兩個序列相似性,針對兩個結構相同的序列其值接近1,主要優點是對類別標籤不敏感

from sklearn import metrics
import numpy as np

# 隨機序列
label_true = np.random.randint(1, 4, 6)
label_pred = np.random.randint(1, 4, 6)
print("Adjust Rand Index for 隨機序列: %.3f" % metrics.adjusted_rand_score(label_true, label_pred))

Adjust Rand Index for 隨機序列: -0.296

# 結構相同,這個例子裡能看出"對類別標籤不敏感"
label_true = [1, 1, 3, 3, 2, 2]
label_pred = [3, 3, 2, 2, 1, 1]
print("Adjust Rand Index for 結構相同的序列: %.3f" % metrics.adjusted_rand_score(label_true, label_pred))

Adjust Rand Index for 結構相同的序列: 1.000

Homogeneity(齊次性)

Homogeneity表徵每個聚類類別中的元素只由同種標註標籤的元素組成的程度。

from sklearn import metrics
import numpy as np

label_true = [1, 1, 2, 2]
label_pred = [2, 2, 1, 1]
print("齊次性值 for 結構相同: %.3f" % metrics.homogeneity_score(label_true, label_pred))

齊次性值 for 結構相同: 1.000

label_true = [1, 1, 2, 2]
label_pred = [0, 1, 2, 3]
print("齊次性值 for 每個類別內只由一種原類別元素組成: %.3f" % metrics.homogeneity_score(label_true, label_pred))

齊次性值 for 每個類別內只由一種原類別元素組成: 1.000

label_true = [1, 1, 2, 2]
label_pred = [1, 2, 1, 2]
print("齊次性值 for 每個類別內不由一種原類別元素組成: %.3f" % metrics.homogeneity_score(label_true, label_pred))

齊次性值 for 每個類別內不由一種原類別元素組成: 0.000

label_true = np.random.randint(1, 4, 6)
label_pred = np.random.randint(1, 4, 6)
print("齊次性值 for 隨機序列: %.3f" % metrics.homogeneity_score(label_true, label_pred))

齊次性值 for 隨機序列: 0.685

Completeness(完整性)

Completeness表徵同種標註標籤的元素全部分配到同種聚類類別中的程度。

from sklearn import metrics
import numpy as np

label_true = [1, 1, 2, 2]
label_pred = [2, 2, 1, 1]
print("完整性值 for 結構相同: %.3f" % metrics.completeness_score(label_true, label_pred))

完整性值 for 結構相同: 1.000

label_true = [0, 1, 2, 2]
label_pred = [2, 0, 1, 1]
print("完整性值 for 原類別相同的都分到一個聚類中: %.3f" % metrics.completeness_score(label_true, label_pred))

完整性值 for 原類別相同的都分到一個聚類中: 1.000

label_true = [0, 1, 2, 2]
label_pred = [2, 0, 1, 2]
print("完整性值 for 原類別相同的未分到一個聚類中: %.3f" % metrics.completeness_score(label_true, label_pred))

完整性值 for 原類別相同的未分到一個聚類中: 0.667

label_true = np.random.randint(1, 4, 6)
label_pred = np.random.randint(1, 4, 6)
print("完整性值 for 隨機序列: %.3f" % metrics.completeness_score(label_true, label_pred))

完整性值 for 隨機序列: 0.457

V-measure

V-measure結合了Homogeneity和Completeness這一組互為補充的評價指標。若將聚類結果和標註標籤反轉,得到的V-measure值是相同的。

from sklearn import metrics
import numpy as np

label_true = [1, 1, 2, 2]
label_pred = [2, 2, 1, 1]
print("V-measure for 結構相同: %.3f" % metrics.v_measure_score(label_true, label_pred))

V-measure for 結構相同: 1.000

label_true = [0, 1, 2, 3]
label_pred = [1, 1, 2, 2]
print("V-measure for 不齊次,但完整: %.3f" % metrics.v_measure_score(label_true, label_pred))
print("V-measure for 齊次,但不完整: %.3f" % metrics.v_measure_score(label_pred, label_true))

V-measure for 不齊次,但完整: 0.667
V-measure for 齊次,但不完整: 0.667

label_true = [1, 1, 2, 2]
label_pred = [1, 2, 1, 2]
print("V-measure for 既不齊次,也不完整: %.3f" % metrics.v_measure_score(label_true, label_pred))

V-measure for 既不齊次,也不完整: 0.000

label_true = np.random.randint(1, 4, 6)
label_pred = np.random.randint(1, 4, 6)
print("V-measure for 隨機序列: %.3f" % metrics.v_measure_score(label_true, label_pred))

V-measure for 隨機序列: 0.457

輪廓係數

使用metrics.silhouette_score(樣本集,聚類標籤,)計算。

參考

聚類︱python實現 六大 分群質量評估指標(蘭德係數、互資訊、輪廓係數)