如何使用 Keras 實現無監督聚類
雷鋒網 AI 研習社按:本文為雷鋒字幕組編譯的技術博客,原標題 A、Word2Vec?—?a baby step in Deep Learning but a giant leap towards Natural Language Processing,作者為機器學習工程師 Suvro Banerjee
翻譯 | 程煒 李昊洋 整理 | 孔令雙
https://medium.com/@chengweizhang2012/how-to-do-unsupervised-clustering-with-keras-9e1284448437
由於深度學習演算法在表達非線性表徵上的卓越能力,它非常適合完成輸入到有標籤的數據集輸出的映射。這種任務叫做分類。它需要有人對數據進行標註。無論是對 X 光圖像還是對新聞報道的主題進行標註,在數據集增大的時候,依靠人類進行干預的做法都是費時費力的。
聚類分析,或者稱作聚類是一種無監督的機器學習技術。它不需要有標籤的數據集。它可以根據數據成員的相似性對它們進行分組。
你為什麼需要關注它呢?讓我來講講幾個理由。
聚類的應用
推薦系統,通過學慣用戶的購買歷史,聚類模型可以根據相似性對用戶進行區分。它可以幫助你找到志趣相投的用戶,以及相關商品。
在生物學上,序列聚類演算法試圖將相關的生物序列進行分組。它根據氨基酸含量對蛋白進行聚類。
圖像和視頻聚類分析根據相似性對它們進行分組。
在醫療資料庫中,對每個病人來說,真正有價值的測試(比如葡萄糖,膽固醇)都是不同的。首先對病人進行聚類分析可以幫助我們對真正有價值的特徵進行分類,從而減少特徵分散。它可以增加分類任務的準確性,比如在癌症病人生存預測上。
在一般用途上,它可以生成一個數據的匯總信息用於分類,模式發現,假設生成,以及測試。
無論如何,對於數據科學家來說,聚類都是非常有價值的工具。
如何才是好的聚類
一個好的聚類方法應該生成高質量的分類,它有如下特點:
群組內部的高相似性:群組內的緊密聚合
群組之間的低相似性:群組之間各不相同
為 K-Means 演算法設置一個基線
傳統的 K-Means 演算法速度快,並且可以廣泛應用於解決各種問題。但是,它的距離度量受限於原始的數據空間。因此在輸入數據維度較高時,它的效率就會降低,比如說圖像集。
讓我們來訓練一個 K-Means 模型對 MNIST 手寫字體進行聚類分析到 10 個群組中。
fromsklearn.clusterimportKMeans
fromkeras.datasetsimportmnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x = np.concatenate((x_train, x_test))
y = np.concatenate((y_train, y_test))
x = x.reshape((x.shape[],-1))
x = np.divide(x,255.)
# 10 clusters
n_clusters = len(np.unique(y))
# Runs in parallel 4 CPUs
kmeans = KMeans(n_clusters=n_clusters, n_init=20, n_jobs=4)
# Train K-Means.
y_pred_kmeans = kmeans.fit_predict(x)
# Evaluate the K-Means clustering accuracy.
metrics.acc(y, y_pred_kmeans)
評估得到 K-Means 聚類演算法的準確度在 53.2%。後面我們會將它與深度嵌入聚類模型進行比較。
一個自動編碼器,通過前訓練,學習無標籤數據集初始壓縮後的表徵。
建立在編碼器之上的聚類層將輸出送給一個群組。基於當前評估得到的 K-Means 聚類中心,聚類層完成權重值的初始化。
訓練聚類模型,同時改善聚類層和編碼器。
在找源代碼嗎?到我的 Github 上看看。
https://github.com/Tony607/Keras_Deep_Clustering
前訓練自動編碼器
自動編碼器是一個數據壓縮演算法。它由編碼器和解碼器兩個主要部分構成。編碼器的工作是將輸入數據壓縮成較低維度的特徵。比如,一個 28x28 的 MNIST 圖像總共有 784 個像素。編碼器可以將它壓縮成 10 個浮點數組成的數組。我們將這些浮點數作為圖像的特徵。另一方面,解碼器將壓縮後的特徵作為輸入,通過它重建出與原始圖像儘可能相近似的圖像。實際上,自動編碼器是一個無監督學習演算法。在訓練過程中,它只需要圖像本身,而不需要標籤。
自動編碼器
自動編碼器是一個全連接對稱模型。之所以是對稱的,是因為圖像的壓縮和解壓過程是一組完全相反的對應過程。
全連接自動編碼器
我們將會對自動編碼器進行 300 輪訓練,並保存下模型權重值。
autoencoder.fit(x,x, batch_size=256, epochs=300) #, callbacks=cb)
autoencoder.save_weights("./results/ae_weights.h5")
聚類模型
通過訓練自動編碼器,我們已經使編碼器學會了將每幅圖像壓縮成 10 個浮點數。你可能會想,因為輸入維度減少到 10, K-Means 演算法應該可以以此開始聚類?是的,我們將會使用 K-Means 演算法生成聚類中心。它是 10 維特徵向量空間的 10 個群組的中心。但是我們還要建立我們的自定義聚類層,將輸入特徵轉化為群組標籤概率。
這個概率是由t-分布計算得來。 T-分布,和t-分布鄰域嵌入演算法一樣,測度了內含點和中心點之間的相似度。.正如你所猜測的那樣,聚類層的作用類似於用於聚類的K-means,並且該層的權重表示可以通過訓練K均值來初始化的聚類質心。
如果您是在Keras中創建自定義圖層的新手,那麼您可以實施三種強制方法。
build(input_shape),在這裡你定義圖層的權重,在我們的例子中是10-D特徵空間中的10個簇,即10x10個權重變數。
call(x),層邏輯所在的地方,即從特徵映射到聚類標籤魔術的地方。
compute_output_shape(input_shape),在這裡指定從輸入形狀到輸出形狀的形狀轉換邏輯。
Here is the custom clustering layer code:
classClusteringLayer(Layer):
"""
Clustering layer converts input sample (feature) to soft label.
# Example
```
model.add(ClusteringLayer(n_clusters=10))
```
# Arguments
n_clusters: number of clusters.
weights: list of Numpy array with shape `(n_clusters, n_features)` witch represents the initial cluster centers.
alpha: degrees of freedom parameter in Student"s t-distribution. Default to 1.0.
# Input shape
2D tensor with shape: `(n_samples, n_features)`.
# Output shape
2D tensor with shape: `(n_samples, n_clusters)`.
"""
def__init__(self, n_clusters, weights=None, alpha=1.0, **kwargs):
if"input_shape"notinkwargsand"input_dim"inkwargs:
kwargs["input_shape"] = (kwargs.pop("input_dim"),)
super(ClusteringLayer,self).__init__(**kwargs)
self.n_clusters = n_clusters
self.alpha = alpha
self.initial_weights = weights
self.input_spec = InputSpec(ndim=2)
defbuild(self, input_shape):
assert len(input_shape) ==2
input_dim = input_shape[1]
self.input_spec = InputSpec(dtype=K.floatx(), shape=(None, input_dim))
self.clusters =self.add_weight((self.n_clusters, input_dim), initializer="glorot_uniform", name="clusters")
ifself.initial_weights isnotNone:
self.set_weights(self.initial_weights)
delself.initial_weights
self.built = True
defcall(self, inputs, **kwargs):
""" student t-distribution, as same as used in t-SNE algorithm.
q_ij = 1/(1+dist(x_i, μ_j)^2), then normalize it.
q_ij can be interpreted as the probability of assigning sample i to cluster j.
(i.e., a soft assignment)
Arguments:
inputs: the variable containing data, shape=(n_samples, n_features)
Return:
q: student"s t-distribution, or soft labels for each sample. shape=(n_samples, n_clusters)
"""
q =1.0/ (1.0+ (K.sum(K.square(K.expand_dims(inputs, axis=1) -self.clusters), axis=2) /self.alpha))
q **= (self.alpha +1.0) /2.0
q = K.transpose(K.transpose(q) / K.sum(q, axis=1))# Make sure each sample"s 10 values add up to 1.
returnq
defcompute_output_shape(self, input_shape):
assert input_shapeandlen(input_shape) ==2
returninput_shape[],self.n_clusters
defget_config(self):
config = {"n_clusters":self.n_clusters}
base_config =super(ClusteringLayer,self).get_config()
returndict(list(base_config.items()) + list(config.items()))
接下來,我們在預先訓練的編碼器之後堆疊聚類層以形成聚類模型。 對於聚類層,我們初始化它的權重,聚類中心使用k-means對所有圖像的特徵向量進行訓練。
clustering_layer= ClusteringLayer(n_clusters, name="clustering")(encoder.output)
model = Model(inputs=encoder.input, outputs=clustering_layer)
# Initialize cluster centers using k-means.
kmeans = KMeans(n_clusters=n_clusters, n_init=20)
y_pred = kmeans.fit_predict(encoder.predict(x))
model.get_layer(name="clustering").set_weights([kmeans.cluster_centers_])
聚類模型結構
訓練聚類模型
輔助目標分布和KL散度損失
下一步是同時改進聚類分配和特徵表示。 為此,我們將定義一個基於質心的目標概率分布,並根據模型聚類結果將KL偏差最小化。
我們希望目標分配具有以下屬性:
加強預測,即提高群集純度。
更加重視高可信度地分配的數據點。
防止大集群扭曲隱藏的特徵空間。
通過首先將q(編碼特徵向量)提升到第二冪然後按每個簇的頻率進行歸一化來計算目標分布。
deftarget_distribution(q):
weight = q **2/ q.sum()
return(weight.T / weight.sum(1)).T
有必要通過在輔助目標分布的幫助下從高置信度分配中學習來迭代地細化群集。 在特定次數的迭代之後,更新目標分布,並且訓練聚類模型以最小化目標分布與聚類輸出之間的KL散度損失。 培訓策略可以被看作是一種自我訓練的形式。 就像在自我訓練中一樣,我們採用初始分類器和未標記的數據集,然後用分類器標記數據集以訓練其高置信度的預測。
損失函數,KL散度或Kullback-Leibler散度是衡量兩種不同分布之間行為差異的指標。 我們希望將其最小化,以便目標分布儘可能接近聚類輸出分布。
在以下代碼片段中,目標分布每180次訓練迭代更新一次。
model.compile(optimizer=SGD(0.01,0.9), loss="kld")
maxiter =8000
update_interval =140
forite inrange(int(maxiter)):
ifite % update_interval ==:
q = model.predict(x,verbose=)
p= target_distribution(q) #updatethe auxiliary target distributionp
# evaluate the clustering performance
y_pred = q.argmax(1)
ifyisnot None:
acc = np.round(metrics.acc(y, y_pred),5)
idx = index_array[index* batch_size:min((index+1) * batch_size,x.shape[])]
model.train_on_batch(x=x[idx],y=p[idx])
index=index+1if(index+1) * batch_size
每次更新後,您將看到聚類準確度穩步提高。
評估指標
該度量標準表明它已達到96.2%的聚類精度,考慮到輸入是未標記的圖像,這非常好。 讓我們仔細研究它的精確度。
該度量需要從無監督演算法和地面實況分配中獲取一個集群分配,然後找到它們之間的最佳匹配。
最好的映射可以通過在scikit學習庫中實現的匈牙利演算法有效地計算為linear_assignment。
查看混淆矩陣更直接。
混亂矩陣
在這裡,您可以手動快速匹配聚類分配,例如,聚類1與真實標籤7或手寫數字「7」和虎鉗簽證相匹配。
下面顯示的混淆矩陣繪製代碼片段。
importseabornassns
importsklearn.metrics
importmatplotlib.pyplotasplt
sns.set(font_scale=3)
confusion_matrix = sklearn.metrics.confusion_matrix(y, y_pred)
plt.figure(figsize=(16,14))
sns.heatmap(confusion_matrix, annot=True, fmt="d", annot_kws={"size":20});
plt.title("Confusion matrix", fontsize=30)
plt.ylabel("True label", fontsize=25)
plt.xlabel("Clustering label", fontsize=25)
plt.show()
應用卷積自動編碼器(實驗)
由於我們正在處理圖像數據集,所以值得一試卷積自動編碼器,而不是僅使用完全連接的圖層構建。
值得一提的是,為了重建圖像,您可以選擇去卷積層(Keras中的Conv2DTranspose)或上採樣(UpSampling2D)層以減少偽像問題。卷積自動編碼器的實驗結果可以在我的GitHub上找到。
結論和進一步閱讀
自動編碼器在降維和參數初始化方面發揮了重要作用,然後針對目標分布對定製的聚類層進行訓練以進一步提高精度。
進一步閱讀
在Keras建立自動編碼器 - 官方Keras博客
用於聚類分析的無監督深嵌入 - 激勵我寫這篇文章。
完整的源代碼在我的GitHub上,一直讀到筆記本的最後,因為您會發現另一種可以同時減少聚類和自動編碼器丟失的另一種方法,這種方法被證明對於提高卷積聚類模型的聚類準確性非常有用。
CCF-GAIR 2018 在即,
AI 研習社送福利了!
TAG:AI研習社 |