Scikit-learn 秘籍 第三章 使用距離向量構(gòu)建模型

第三章 使用距離向量構(gòu)建模型

作者:Trent Hauck

譯者:飛龍

協(xié)議:CC BY-NC-SA 4.0

這一章中雀监,我們會涉及到聚類立哑。聚類通常和非監(jiān)督技巧組合到一起仔雷。這些技巧假設(shè)我們不知道結(jié)果變量水醋。這會使結(jié)果模糊管削,以及實踐客觀翔始。但是罗心,聚類十分有用里伯。我們會看到,我們可以使用聚類渤闷,將我們的估計在監(jiān)督設(shè)置中“本地化”疾瓮。這可能就是聚類非常高效的原因。它可以處理很大范圍的情況飒箭,通常狼电,結(jié)果也不怎么正常。

這一章中我們會瀏覽大量應(yīng)用弦蹂,從圖像處理到回歸以及離群點檢測肩碟。通過這些應(yīng)用,我們會看到聚類通惩勾唬可以通過概率或者優(yōu)化結(jié)構(gòu)來觀察削祈。不同的解釋會導(dǎo)致不同的權(quán)衡。我們會看到脑漫,如何訓(xùn)練模型髓抑,以便讓工具嘗試不同模型,在面對聚類問題的時候优幸。

3.1 使用 KMeans 對數(shù)據(jù)聚類

聚類是個非常實用的技巧吨拍。通常,我們在采取行動時需要分治网杆「危考慮公司的潛在客戶列表。公司可能需要將客戶按類型分組碳却,之后為這些分組劃分職責(zé)队秩。聚類可以使這個過程變得容易。

KMeans 可能是最知名的聚類算法之一追城,并且也是最知名的無監(jiān)督學(xué)習(xí)技巧之一刹碾。

準(zhǔn)備

首先,讓我們看一個非常簡單的聚類座柱,之后我們再討論 KMeans 如何工作迷帜。

>>> from sklearn.datasets import make_blobs 
>>> blobs, classes = make_blobs(500, centers=3)

同樣,由于我們繪制一些圖表色洞,導(dǎo)入matplotlib戏锹,像這樣:

>>> import matplotlib.pyplot as plt

操作步驟

我們打算瀏覽一個簡單的例子,它對偽造數(shù)據(jù)進(jìn)行聚類火诸。之后我們會稍微談?wù)撘幌陆跽耄琄Means 如何工作,來尋找最優(yōu)的塊數(shù)量。

看一看我們的數(shù)據(jù)塊奈搜,我們可以看到悉盆,有三個不同的簇。

>>> f, ax = plt.subplots(figsize=(7.5, 7.5)) 
>>> ax.scatter(blobs[:, 0], blobs[:, 1], color=rgb[classes]) 
>>> rgb = np.array(['r', 'g', 'b']) 
>>> ax.set_title("Blobs")

輸出如下:

現(xiàn)在我們可以使用 KMeans 來尋找這些簇的形心馋吗。第一個例子中焕盟,我們假裝知道有三個形心。

>>> from sklearn.cluster import KMeans 
>>> kmean = KMeans(n_clusters=3) 
>>> kmean.fit(blobs) 
KMeans(copy_x=True, init='k-means++', max_iter=300, n_clusters=3,
       n_init=10, n_jobs=1, precompute_distances=True,
       random_state=None, tol=0.0001, verbose=0)

>>> kmean.cluster_centers_ array([[ 0.47819567,  1.80819197],

[ 0.08627847,  8.24102715], 
[ 5.2026125 ,  7.86881767]])

>>> f, ax = plt.subplots(figsize=(7.5, 7.5)) 
>>> ax.scatter(blobs[:, 0], blobs[:, 1], color=rgb[classes]) 
>>> ax.scatter(kmean.cluster_centers_[:, 0],
               kmean.cluster_centers_[:, 1], marker='*', s=250,
               color='black', label='Centers')
>>> ax.set_title("Blobs") 
>>> ax.legend(loc='best')

下面的截圖展示了輸出:

其它屬性也很實用宏粤。例如脚翘,labels_屬性會產(chǎn)生每個點的預(yù)期標(biāo)簽。

>>> kmean.labels_[:5] 
array([1, 1, 2, 2, 1], dtype=int32) 

我們可以檢查绍哎,例如来农,labels_是否和類別相同,但是由于 KMeans 不知道類別是什么崇堰,它不能給兩個類別分配相同的索引值:

>>> classes[:5] 
array([0, 0, 2, 2, 0])

將類別中的1變成0來查看是否與labels_匹配沃于。

transform函數(shù)十分有用,它會輸出每個點到形心的距離赶袄。

>>> kmean.transform(blobs)[:5] 
array([[ 6.47297373,  1.39043536,  6.4936008 ],
       [ 6.78947843,  1.51914705,  3.67659072],
       [ 7.24414567,  5.42840092,  0.76940367],
       [ 8.56306214,  5.78156881,  0.89062961],
       [ 7.32149254,  0.89737788,  5.12246797]])

工作原理

KMeans 實際上是個非常簡單的算法揽涮,它使簇中的點到均值的距離的平方和最小尖奔。

首先它會設(shè)置一個預(yù)定義的簇數(shù)量K桦卒,之后執(zhí)行這些事情:

  • 將每個數(shù)據(jù)點分配到最近的簇中绊谭。
  • 通過計算初中每個數(shù)據(jù)點的均值,更新每個形心敬辣。

直到滿足特定條件。

3.2 優(yōu)化形心數(shù)量

形心難以解釋零院,并且也難以判斷是否數(shù)量正確溉跃。理解你的數(shù)據(jù)是否是未分類的十分重要,因為這會直接影響我們可用的評估手段告抄。

準(zhǔn)備

為無監(jiān)督學(xué)習(xí)評估模型表現(xiàn)是個挑戰(zhàn)撰茎。所以,在了解真實情況的時候打洼,sklearn擁有多種方式來評估聚類龄糊,但在不了解時就很少。

我們會以一個簡單的簇模型開始募疮,并評估它的相似性炫惩。這更多是出于機(jī)制的目的,因為測量一個簇的相似性在尋找簇數(shù)量的真實情況時顯然沒有用阿浓。

操作步驟

為了開始他嚷,我們會創(chuàng)建多個數(shù)據(jù)塊,它們可用于模擬數(shù)據(jù)簇。

>>> from sklearn.datasets import make_blobs 
>>> import numpy as np 
>>> blobs, classes = make_blobs(500, centers=3)

>>> from sklearn.cluster import KMeans 
>>> kmean = KMeans(n_clusters=3) 
>>> kmean.fit(blobs) 
KMeans(copy_x=True, init='k-means++', max_iter=300, n_clusters=3,
       n_init=10, n_jobs=1, precompute_distances=True,
       random_state=None, tol=0.0001, verbose=0) 

首先筋蓖,我們查看輪廓(Silhouette)距離卸耘。輪廓距離是簇內(nèi)不相似性、最近的簇間不相似性粘咖、以及這兩個值最大值的比值鹊奖。它可以看做簇間分離程度的度量。

讓我們看一看數(shù)據(jù)點到形心的距離分布涂炎,理解輪廓距離非常有用忠聚。

>>> from sklearn import metrics 
>>> silhouette_samples = metrics.silhouette_samples(blobs,
                         kmean.labels_) 
>>> np.column_stack((classes[:5], silhouette_samples[:5]))

array([[ 1.,  0.87617292],
       [ 1.,  0.89082363],
       [ 1.,  0.88544994],
       [ 1.,  0.91478369],
       [ 1.,  0.91308287]]) 
>>> f, ax = plt.subplots(figsize=(10, 5))

>>> ax.set_title("Hist of Silhouette Samples") 
>>> ax.hist(silhouette_samples) 

輸出如下:

要注意,通常接近 1 的系數(shù)越高唱捣,分?jǐn)?shù)就越高两蟀。

工作原理

輪廓系數(shù)的均值通常用于描述整個模型的擬合度。

>>> silhouette_samples.mean() 
0.57130462953339578

這十分普遍震缭,事實上赂毯,metrics模塊提供了一個函數(shù)來獲得剛才的值。

現(xiàn)在拣宰,讓我們訓(xùn)練多個簇的模型党涕,并看看平均得分是什么樣:

# first new ground truth 
>>> blobs, classes = make_blobs(500, centers=10) 
>>> sillhouette_avgs = []
# this could take a while 
>>> for k in range(2, 60):
       kmean = KMeans(n_clusters=k).fit(blobs)
       sillhouette_avgs.append(metrics.silhouette_score(blobs,
                               kmean.labels_))
>>> f, ax = plt.subplots(figsize=(7, 5)) 
>>> ax.plot(sillhouette_avgs) 

下面是輸出:

這個繪圖表明,輪廓均值隨著形心數(shù)量的變化情況巡社。我們可以看到最優(yōu)的數(shù)量是 3膛堤,根據(jù)所生成的數(shù)據(jù)。但是最優(yōu)的數(shù)量看起來是 6 或者 7晌该。這就是聚類的實際情況肥荔,十分普遍,我們不能獲得正確的簇數(shù)量朝群,我們只能估計簇數(shù)量的近似值燕耿。

3.3 評估聚類的正確性

我們之前討論了不知道真實情況的條件下的聚類評估。但是姜胖,我們還沒有討論簇已知條件下的 KMeans 評估誉帅。在許多情況下,這都是不可知的右莱,但是如果存在外部的標(biāo)注蚜锨,我們就會知道真實情況,或者至少是代理隧出。

準(zhǔn)備

所以踏志,讓我們假設(shè)有一個世界,其中我們有一些外部代理胀瞪,向我們提供了真實情況针余。

我們會創(chuàng)建一個簡單的數(shù)據(jù)集饲鄙,使用多種方式評估相對于真實慶康的正確性。之后討論它們圆雁。

操作步驟

在我們開始度量之前忍级,讓我們先查看數(shù)據(jù)集:

>>> f, ax = plt.subplots(figsize=(7, 5))

>>> colors = ['r', 'g', 'b']

>>> for i in range(3):
       p = blobs[ground_truth == i]
       ax.scatter(p[:,0], p[:,1], c=colors[i],
       label="Cluster {}".format(i))
       
>>> ax.set_title("Cluster With Ground Truth") 
>>> ax.legend()

>>> f.savefig("9485OS_03-16")

下面是輸出:

既然我們已經(jīng)訓(xùn)練了模型,讓我們看看簇的形心:

>>> f, ax = plt.subplots(figsize=(7, 5))

>>> colors = ['r', 'g', 'b']

>>> for i in range(3):
       p = blobs[ground_truth == i]
       ax.scatter(p[:,0], p[:,1], c=colors[i],        label="Cluster {}".format(i))
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
               kmeans.cluster_centers_[:, 1], s=100,
               color='black',
               label='Centers') 
>>> ax.set_title("Cluster With Ground Truth") 
>>> ax.legend()
>>> f.savefig("9485OS_03-17") 

下面是輸出:

既然我們能夠?qū)⒕垲惐憩F(xiàn)看做分類練習(xí)伪朽,在其語境中有用的方法在這里也有用:

>>> for i in range(3):
       print (kmeans.labels_ == ground_truth)[ground_truth == i]
       .astype(int).mean()
       
0.0778443113772 
0.990990990991 
0.0570570570571

很顯然我們有一些錯亂的簇轴咱。所以讓我們將其捋直,之后我們查看準(zhǔn)確度烈涮。

>>> new_ground_truth = ground_truth.copy()
>>> new_ground_truth[ground_truth == 0] = 2 
>>> new_ground_truth[ground_truth == 2] = 0

>>> for i in range(3):
       print (kmeans.labels_ == new_ground_truth)[ground_truth == i]
       .astype(int).mean()
       
0.919161676647 
0.990990990991 
0.90990990991

所以我們 90% 的情況下都是正確的朴肺。第二個相似性度量是互信息( mutual information score)得分。

>>> from sklearn import metrics

>>> metrics.normalized_mutual_info_score(ground_truth, kmeans.labels_)

0.78533737204433651

分?jǐn)?shù)靠近 0坚洽,就說明標(biāo)簽的分配可能不是按照相似過程生成的戈稿。但是分?jǐn)?shù)靠近 1,就說明兩個標(biāo)簽有很強(qiáng)的一致性讶舰。

例如鞍盗,讓我們看一看互信息分?jǐn)?shù)自身的情況:

>>> metrics.normalized_mutual_info_score(ground_truth, ground_truth)

1.0 

通過名稱,我們可以分辨出可能存在未規(guī)范化的mutual_info_score

>>> metrics.mutual_info_score(ground_truth, kmeans.labels_)

0.78945287371677486 

這非常接近了跳昼。但是般甲,規(guī)范化的互信息是互信息除以每個真實值和標(biāo)簽的熵的乘積的平方根。

更多

有一個度量方式我們尚未討論鹅颊,并且不依賴于真實情況敷存,就是慣性(inertia)度量。當(dāng)前挪略,它作為一種度量并沒有詳細(xì)記錄历帚。但是滔岳,它是 KMeans 中最簡單的度量杠娱。

慣性是每個數(shù)據(jù)點和它所分配的簇的平方差之和。我們可以稍微使用 NumPy 來計算它:

>>> kmeans.inertia_ 

3.4 使用 MiniBatch KMeans 處理更多數(shù)據(jù)

KMeans 是一個不錯的方法谱煤,但是不適用于大量數(shù)據(jù)摊求。這是因為 KMenas 的復(fù)雜度。也就是說刘离,我們可以使用更低的算法復(fù)雜度來獲得近似解室叉。

準(zhǔn)備

MiniBatch Kmeans 是 KMeans 的更快實現(xiàn)。KMeans 的計算量非常大硫惕,問題是 NPH 的茧痕。

但是,使用 MiniBatch KMeans恼除,我們可以將 KMeans 加速幾個數(shù)量級踪旷。這通過處理多個子樣本來完成曼氛,它們叫做 MiniBatch。如果子樣本是收斂的令野,并且擁有良好的初始條件舀患,就得到了常規(guī) KMeans 的近似解。

操作步驟

讓我們對 MiniBatch 聚類做一個概要的性能分析气破。首先聊浅,我們觀察總體的速度差異,之后我們會觀察估計中的誤差现使。

>>> from sklearn.datasets import make_blobs 
>>> blobs, labels = make_blobs(int(1e6), 3)

>>> from sklearn.cluster import KMeans, MiniBatchKMeans

>>> kmeans = KMeans(n_clusters=3) >>> minibatch = MiniBatchKMeans(n_clusters=3)

要理解這些度量的目的是暴露問題低匙。所以,需要多加小心碳锈,來確保跑分的高精度性努咐。這個話題還有大量可用的信息。如果你真的希望了解殴胧,MiniBatch KMeans 為何在粒度上更具優(yōu)勢渗稍,最好還是要閱讀它們。

既然準(zhǔn)備已經(jīng)完成团滥,我們可以測量時間差異:

>>> %time kmeans.fit(blobs) #IPython Magic CPU times: user 8.17 s, sys: 881 ms, total: 9.05 s Wall time: 9.97 s

>>> %time minibatch.fit(blobs) CPU times: user 4.04 s, sys: 90.1 ms, total: 4.13 s Wall time: 4.69 s 

CPU 時間上有很大差異竿屹。聚類性能上的差異在下面展示:

>>> kmeans.cluster_centers_[0] array([ 1.10522173, -5.59610761, -8.35565134])

>>> minibatch.cluster_centers_[0] array([ 1.12071187, -5.61215116, -8.32015587]) 

我們可能要問的下一個問題就是,兩個形心距離多遠(yuǎn)灸姊。

>>> from sklearn.metrics import pairwise 
>>> pairwise.pairwise_distances(kmeans.cluster_centers_[0],
                                 minibatch.cluster_centers_[0])
array([[ 0.03305309]]) 

看起來十分接近了拱燃。對角線包含形心的差異:

>>> np.diag(pairwise.pairwise_distances(kmeans.cluster_centers_,
             minibatch.cluster_centers_)) 
array([ 0.04191979, 0.03133651,  0.04342707])

工作原理

這里的批次就是關(guān)鍵。批次被迭代來尋找批次均值力惯。對于下一次迭代來說碗誉,前一個批次的均值根據(jù)當(dāng)前迭代來更新。有多種選項父晶,用于控制 KMeans 的通用行為哮缺,和決定 MiniBatch KMeans 的參數(shù)。

batch_size參數(shù)決定批次應(yīng)為多大甲喝。只是玩玩的話尝苇,我們可以運行 MiniBatch,但是埠胖,此時我們將批次數(shù)量設(shè)置為和數(shù)據(jù)集大小相同糠溜。

>>> minibatch = MiniBatchKMeans(batch_size=len(blobs)) 
>>> %time minibatch.fit(blobs) 
CPU times: user 34.6 s, sys: 3.17 s, total: 37.8 s Wall time: 44.6 s 

顯然,這就違背了問題的核心直撤,但是這的確展示了重要東西非竿。選擇差勁的初始條件可能影響我們的模型,特別是聚類模型的收斂谋竖。使用 MiniBatch KMeans红柱,全局最優(yōu)是否能達(dá)到侮东,是不一定的。

3.5 使用 KMeans 聚類來量化圖像

圖像處理是個重要的話題豹芯,其中聚類有一些應(yīng)用悄雅。值得指出的是,Python 中有幾種非常不錯的圖像處理庫铁蹈。Scikit-image 是 Scikit-learn 的“姐妹”項目宽闲。如果你打算做任何復(fù)雜的事情,都值得看一看它握牧。

準(zhǔn)備

我們在這篇秘籍中會有一些樂趣容诬。目標(biāo)是使用聚類來把圖像變模糊。

首先沿腰,我們要利用 SciPy 來讀取圖像览徒。圖像翻譯為三維數(shù)組,xy坐標(biāo)描述了高度和寬度颂龙,第三個維度表示每個圖像的 RGB 值习蓬。

# in your terminal 
$ wget http://blog.trenthauck.com/assets/headshot.jpg

操作步驟

現(xiàn)在,讓我們在 Python 中讀取圖像:

>>> from scipy import ndimage 
>>> img = ndimage.imread("headshot.jpg") 
>>> plt.imshow(img)

下面就是圖像:

嘿措嵌,這就是(年輕時期的)作者躲叼。

既然我們已經(jīng)有了圖像,讓我們檢查它的維度:

>>> img.shape 
(420, 420, 3) 

為了實際量化圖像企巢,我們需要將其轉(zhuǎn)換為二維數(shù)組枫慷,長為420x420,寬為 RGB 值浪规。思考它的更好的方法或听,是擁有一堆三維空間中的數(shù)據(jù)點,并且對點進(jìn)行聚類來降低圖像中的不同顏色的數(shù)量 -- 這是一個簡單的量化方式笋婿。

首先誉裆,讓我們使數(shù)組變形,它是個 NumPy 數(shù)組萌抵,所以非常簡單:

>>> x, y, z = img.shape 
>>> long_img = img.reshape(x*y, z) 
>>> long_img.shape (176400, 3) 

現(xiàn)在我們開始聚類過程找御。首先,讓我們導(dǎo)入聚類模塊绍填,并創(chuàng)建 KMeans 對象。我們傳入n_clusters=5栖疑,使我們擁有 5 個簇讨永,或者實際上是 5 個不同顏色。

這是個不錯的秘籍遇革,我們使用前面提到的輪廓距離:

>>> from sklearn import cluster 
>>> k_means = cluster.KMeans(n_clusters=5) 
>>> k_means.fit(long_img)

既然我們已經(jīng)訓(xùn)練了 KMeans 對象卿闹,讓我們看看我們的眼色:

>>> centers = k_means.cluster_centers_ 
>>> centers 
array([[ 142.58775848, 206.12712986,  226.04416873],
       [  86.29356543,  68.86312505,   54.04770507],
       [ 194.36182899,  172.19845258,  149.65603813],
       [  24.67768412,   20.45778933,   16.19698314],
       [ 149.27801776,  132.19850659,  115.32729167]])

工作原理

既然我們擁有了形心揭糕,我們需要的下一個東西就是標(biāo)簽。它會告訴我們锻霎,哪個點關(guān)聯(lián)哪個簇著角。

>>> labels = k_means.labels_ 
>>> labels[:5] array([1, 1, 1, 1, 1], dtype=int32)

這個時候,我們需要最簡的 NumPy 操作旋恼,之后是一個變形吏口,我們就擁有的新的圖像:

>>> plt.imshow(centers[labels].reshape(x, y, z)) 

下面就是產(chǎn)生的圖像:

3.6 尋找特征空間中的最接近對象

有時,最簡單的事情就是求出兩個對象之間的距離冰更。我們剛好需要尋找一些距離的度量产徊,計算成對(Pairwise)距離,并將結(jié)果與我們的預(yù)期比較蜀细。

準(zhǔn)備

Scikit-learn 中舟铜,有個叫做sklearn.metrics.pairwise的底層工具。它包含一些服務(wù)函數(shù)奠衔,計算矩陣X中向量之間的距離谆刨,或者XY中的向量距離。

這對于信息檢索來說很實用归斤。例如痴荐,提供一組客戶信息,帶有屬性X官册,我們可能希望選取有個客戶代表生兆,并找到與這個客戶最接近的客戶。實際上膝宁,我們可能希望將客戶按照相似性度量的概念鸦难,使用距離函數(shù)來排序。相似性的質(zhì)量取決于特征空間選取员淫,以及我們在空間上所做的任何變換合蔽。

操作步驟

我們會使用pairwise_distances函數(shù)來判斷對象的接近程度。要記住介返,接近程度就像我們用于聚類/分類的距離函數(shù)拴事。

首先,讓我們從metric模塊導(dǎo)入pairwise_distances函數(shù)圣蝎,并創(chuàng)建用于操作的數(shù)據(jù)集:

>>> from sklearn.metrics import pairwise 
>>> from sklearn.datasets import make_blobs 
>>> points, labels = make_blobs() 

用于檢查距離的最簡單方式是pairwise_distances

>>> distances = pairwise.pairwise_distances(points) 

distances是個 NxN的矩陣刃宵,對角線為 0。在最簡單的情況中徘公,讓我們先看看每個點到第一個點的距離:

>>> np.diag(distances) [:5] 
array([ 0.,  0.,  0.,  0.,  0.])

現(xiàn)在我們可以查找最接近于第一個點的點:

>>> distances[0][:5] 
array([  0., 11.82643041,1.23751545, 1.17612135, 14.61927874])

將點按照接近程度排序牲证,很容易使用np.argsort做到:

>>> ranks = np.argsort(distances[0]) 
>>> ranks[:5] 
array([ 0, 27, 98, 23, 67]) 

argsort的好處是,現(xiàn)在我們可以排序我們的points矩陣关面,來獲得真實的點坦袍。

>>> points[ranks][:5] 
array([[ 8.96147382, -1.90405304],
       [ 8.75417014, -1.76289919],
       [ 8.78902665, -2.27859923],
       [ 8.59694131, -2.10057667],
       [ 8.70949958, -2.30040991]]) 

觀察接近的點是什么樣子十厢,可能十分有用。結(jié)果在意料之中:

工作原理

給定一些距離函數(shù)捂齐,每個點都以成對函數(shù)來度量蛮放。通常為歐幾里得距離,它是:

詳細(xì)來說奠宜,它計算了兩個向量每個分量的差包颁,計算它們的平方,求和挎塌,之后計算它的平方根徘六。這看起來很熟悉,因為在計算均方誤差的時候榴都,我們使用的東西很相似待锈。如果我們計算了平方根,就一樣了嘴高。實際上竿音,經(jīng)常使用的度量是均方根誤差(RMSE),它就是距離函數(shù)的應(yīng)用拴驮。

在 Python 中春瞬,這看起來是:

>>> def euclid_distances(x, y):
       return np.power(np.power(x - y, 2).sum(), .5) 
>>> euclid_distances(points[0], points[1])
11.826430406213145 

Scikit-learn 中存在一些其他函數(shù),但是 Scikit-learn 也會使用 SciPy 的距離函數(shù)套啤。在本書編寫之時宽气,Scikit-learn 距離函數(shù)支持稀疏矩陣。距離函數(shù)的更多信息請查看 SciPy 文檔潜沦。

  • cityblock
  • cosine
  • euclidean
  • l1
  • l2
  • manhattan

我們現(xiàn)在可以解決問題了萄涯。例如,如果我們站在原點處的格子上唆鸡,并且線是街道涝影,為了到達(dá)點(5,5),我們需要走多遠(yuǎn)呢争占?

>>> pairwise.pairwise_distances([[0, 0], [5, 5]], metric='cityblock')[0] 
array([  0.,  10.])

更多

使用成對距離燃逻,我們可以發(fā)現(xiàn)位向量之間的相似性。這是漢明距離的事情臂痕,它定義為:

使用下列命令:

>>> X = np.random.binomial(1, .5, size=(2, 4)).astype(np.bool) 
>>> X 
array([[False,  True, False, False],
       [False, False, False,  True]], dtype=bool)
>>> pairwise.pairwise_distances(X, metric='hamming') 
array([[ 0. ,  0.25],
       [ 0.25,  0. ]]) 

3.7 使用高斯混合模型的概率聚類

在 KMeans 中伯襟,我們假設(shè)簇的方差是相等的。這會導(dǎo)致空間的細(xì)分刻蟹,這決定了簇如何被分配逗旁。但是,如果有一種場景舆瘪,其中方差不是相等的片效,并且每個簇中的點擁有一個與之相關(guān)的概率,會怎么樣英古?

準(zhǔn)備

有一種更加概率化的方式淀衣,用于查看 KMeans 聚類。KMeans 聚類相當(dāng)于將協(xié)方差矩陣S應(yīng)用于高斯混合模型召调,這個矩陣可以分解為單位矩陣成誤差膨桥。對于每個簇,協(xié)方差結(jié)構(gòu)是相同的唠叛。這就產(chǎn)生了球形聚類只嚣。

但是,如果我們允許S變化艺沼,就可以估計 GMM册舞,并將其用于預(yù)測。我們會以單變量的角度看到它的原理障般,之后擴(kuò)展為多個維度调鲸。

操作步驟

首先,我們需要創(chuàng)建一些數(shù)據(jù)挽荡。例如藐石,讓我們模擬女性和男性的身高。我們會在整個秘籍中使用這個例子定拟。這是個簡單的例子于微,但是會展示出我們在 N 維空間中想要完成的東西,這比較易于可視化:

>>> import numpy as np 
>>> N = 1000

>>> in_m = 72 
>>> in_w = 66

>>> s_m = 2 
>>> s_w = s_m

>>> m = np.random.normal(in_m, s_m, N) 
>>> w = np.random.normal(in_w, s_w, N) 
>>> from matplotlib import pyplot as plt 
>>> f, ax = plt.subplots(figsize=(7, 5))

>>> ax.set_title("Histogram of Heights") 
>>> ax.hist(m, alpha=.5, label="Men"); 
>>> ax.hist(w, alpha=.5, label="Women"); 
>>> ax.legend() 

下面是輸出:

下面青自,我們的興趣是株依,對分組二次抽樣,訓(xùn)練分布性穿,之后預(yù)測剩余分組勺三。

>>> random_sample = np.random.choice([True, False], size=m.size) 
>>> m_test = m[random_sample] 
>>> m_train = m[~random_sample]

>>> w_test = w[random_sample] 
>>> w_train = w[~random_sample] 

現(xiàn)在我們需要獲得男性和女性高度的經(jīng)驗分布,基于訓(xùn)練集:

>>> from scipy import stats 
>>> m_pdf = stats.norm(m_train.mean(), m_train.std()) 
>>> w_pdf = stats.norm(w_train.mean(), w_train.std())

對于測試集需曾,我們要計算吗坚,基于數(shù)據(jù)點從每個分布中生成的概率,并且最可能的分布會分配合適的標(biāo)簽呆万。當(dāng)然商源,我們會看到有多么準(zhǔn)確。

>>> m_pdf.pdf(m[0]) 
0.043532673457165431
>>> w_pdf.pdf(m[0]) 
9.2341848872766183e-07 

要注意概率中的差異谋减。

假設(shè)當(dāng)男性的概率更高時牡彻,我們會猜測,但是如果女性的概率更高,我們會覆蓋它庄吼。

>>> guesses_m = np.ones_like(m_test) 
>>> guesses_m[m_pdf.pdf(m_test) < w_pdf.pdf(m_test)] = 0

顯然缎除,問題就是我們有多么準(zhǔn)確。由于正確情況下guesses_m為 1总寻,否則為 0器罐,我們計算向量的均值來獲取準(zhǔn)確度。

>>> guesses_m.mean() 
0.93775100401606426 

不是太糟〗バ校現(xiàn)在轰坊,來看看我們在女性的分組中做的有多好,使用下面的命令:

>>> guesses_w = np.ones_like(w_test) 
>>> guesses_w[m_pdf.pdf(w_test) > w_pdf.pdf(w_test)] = 0 
>>> guesses_w.mean() 0.93172690763052213

讓我們允許兩組間的方差不同祟印。首先肴沫,創(chuàng)建一些新的數(shù)組:

>>> s_m = 1 
>>> s_w = 4

>>> m = np.random.normal(in_m, s_m, N) 
>>> w = np.random.normal(in_w, s_w, N) 

之后,創(chuàng)建訓(xùn)練集:

>>> m_test = m[random_sample] 
>>> m_train = m[~random_sample]

>>> w_test = w[random_sample] 
>>> w_train = w[~random_sample] 
>>> f, ax = plt.subplots(figsize=(7, 5)) 
>>> ax.set_title("Histogram of Heights") 
>>> ax.hist(m_train, alpha=.5, label="Men"); 
>>> ax.hist(w_train, alpha=.5, label="Women"); 
>>> ax.legend() 

讓我們看看男性和女性之間的方差差異:

現(xiàn)在我們可以創(chuàng)建相同的 PDF:

>>> m_pdf = stats.norm(m_train.mean(), m_train.std()) 
>>> w_pdf = stats.norm(w_train.mean(), w_train.std()) 

下面是輸出:

你可以在多維空間中想象他:

>>> class_A = np.random.normal(0, 1, size=(100, 2)) 
>>> class_B = np.random.normal(4, 1.5, size=(100, 2)) 
>>> f, ax = plt.subplots(figsize=(7, 5))

>>> ax.scatter(class_A[:,0], class_A[:,1], label='A', c='r')
>>> ax.scatter(class_B[:,0], class_B[:,1], label='B')

下面是輸出:

工作原理

好的蕴忆,所以既然我們看過了颤芬,我們基于分布對點分類的方式,讓我們看看如何在 Scikit 中首先:

>>> from sklearn.mixture import GMM 
>>> gmm = GMM(n_components=2) 
>>> X = np.row_stack((class_A, class_B)) 
>>> y = np.hstack((np.ones(100), np.zeros(100)))

由于我們是小巧的數(shù)據(jù)科學(xué)家孽文,我們創(chuàng)建訓(xùn)練集:

>>> train = np.random.choice([True, False], 200) 
>>> gmm.fit(X[train]) GMM(covariance_type='diag', init_params='wmc', min_covar=0.001,
     n_components=2, n_init=1, n_iter=100, params='wmc',
     random_state=None,   thresh=0.01)

訓(xùn)練和預(yù)測的完成方式驻襟,和 Scikit-learn 的其它對象相同。

>>> gmm.fit(X[train])
>>> gmm.predict(X[train])[:5] 
array([0, 0, 0, 0, 0]) 

既然模型已經(jīng)訓(xùn)練了芋哭,有一些值得一看的其它方法沉衣。

例如,使用score_examples减牺,我們實際上可以為每個標(biāo)簽獲得每個樣例的可能性豌习。

3.8 將 KMeans 用于離群點檢測

這一章中,我們會查看 Kmeans 離群點檢測的機(jī)制和正義拔疚。它對于隔離一些類型的錯誤很實用肥隆,但是使用時應(yīng)多加小心。

準(zhǔn)備

這個秘籍中稚失,我們會使用 KMeans栋艳,對簇中的點執(zhí)行離群點檢測。要注意句各,提及離群點和離群點檢測時有很多“陣營”吸占。以便面,我們可能通過移除離群點凿宾,來移除由數(shù)據(jù)生成過程生成的點矾屯。另一方面,離群點可能來源于測量誤差或一些其它外部因素初厚。

這就是爭議的重點件蚕。這篇秘籍的剩余部分有關(guān)于尋找離群點。我們的假設(shè)是,我們移除離群點的選擇是合理的排作。

離群點檢測的操作是牵啦,查找簇的形心,之后通過點到形心的距離來識別潛在的離群點纽绍。

操作步驟

首先蕾久,我們會生成 100 個點的單個數(shù)據(jù)塊势似,之后我們會識別 5 個離形心最遠(yuǎn)的點拌夏。它們就是潛在的離群點。

>>> from sklearn.datasets import make_blobs 
>>> X, labels = make_blobs(100, centers=1) 
>>> import numpy as np 

非常重要的是履因,Kmeans 聚類只有一個形心障簿。這個想法類似于用于離群點檢測的單類 SVM。

>>> from sklearn.cluster import KMeans 
>>> kmeans = KMeans(n_clusters=1) 
>>> kmeans.fit(X)

現(xiàn)在栅迄,讓我們觀察繪圖站故。對于那些遠(yuǎn)離中心的點,嘗試猜測哪個點會識別為五個離群點之一:

>>> f, ax = plt.subplots(figsize=(7, 5)) 
>>> ax.set_title("Blob") 
>>> ax.scatter(X[:, 0], X[:, 1], label='Points') 
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
                kmeans.cluster_centers_[:, 1],
                label='Centroid',
                color='r') 
>>> ax.legend()

下面就是輸出:

現(xiàn)在毅舆,讓我們識別五個最接近的點:

>>> distances = kmeans.transform(X) 
# argsort returns an array of indexes which will sort the array in ascending order 
# so we reverse it via [::-1] and take the top five with [:5] 
>>> sorted_idx = np.argsort(distances.ravel())[::-1][:5]

現(xiàn)在西篓,讓我們看看哪個點離得最遠(yuǎn):

>>> f, ax = plt.subplots(figsize=(7, 5)) 
>>> ax.set_title("Single Cluster") 
>>> ax.scatter(X[:, 0], X[:, 1], label='Points') 
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
                kmeans.cluster_centers_[:, 1],
                label='Centroid', color='r') 
>>> ax.scatter(X[sorted_idx][:, 0], X[sorted_idx][:, 1],
                label='Extreme Value', edgecolors='g',
                facecolors='none', s=100) 
>>> ax.legend(loc='best') 

下面是輸出:

如果我們喜歡的話,移除這些點很容易憋活。

>>> new_X = np.delete(X, sorted_idx, axis=0)

同樣岂津,移除這些點之后,形心明顯變化了悦即。

>>> new_kmeans = KMeans(n_clusters=1) 
>>> new_kmeans.fit(new_X) 

讓我們將舊的和新的形心可視化:

>>> f, ax = plt.subplots(figsize=(7, 5)) 
>>> ax.set_title("Extreme Values Removed") 
>>> ax.scatter(new_X[:, 0], new_X[:, 1], label='Pruned Points') 
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
               kmeans.cluster_centers_[:, 1], label='Old Centroid',
               color='r', s=80, alpha=.5) 
>>> ax.scatter(new_kmeans.cluster_centers_[:, 0],
               new_kmeans.cluster_centers_[:, 1], label='New Centroid',
               color='m', s=80, alpha=.5) 
>>> ax.legend(loc='best') 

下面是輸出:

顯然,形心沒有移動多少,僅僅移除五個極端點時俱病,我們的預(yù)期就是這樣胜宇。這個過程可以重復(fù),知道我們對數(shù)據(jù)表示滿意作瞄。

工作原理

我們已經(jīng)看到茶宵,高斯分布和 KMeans 聚類之間有本質(zhì)聯(lián)系。讓我們基于形心和樣本的協(xié)方差矩陣創(chuàng)建一個經(jīng)驗高斯分布宗挥,并且查看每個點的概率 -- 理論上是我們溢出的五個點乌庶。這剛好展示了,我們實際上溢出了擁有最低可能性的值属韧。距離和可能性之間的概念十分重要安拟,并且在你的機(jī)器學(xué)習(xí)訓(xùn)練中會經(jīng)常出現(xiàn)。

使用下列命令來創(chuàng)建經(jīng)驗高斯分布:

>>> from scipy import stats 
>>> emp_dist = stats.multivariate_normal(
               kmeans.cluster_centers_.ravel()) 
>>> lowest_prob_idx = np.argsort(emp_dist.pdf(X))[:5] 
>>> np.all(X[sorted_idx] == X[lowest_prob_idx]) True 

3.9 將 KNN 用于回歸

回歸在這本書的其它地方有所設(shè)計宵喂,但是我們可能打算在特征空間的“口袋”中運行回歸糠赦。我們可以認(rèn)為,我們的數(shù)據(jù)集要經(jīng)過多道數(shù)據(jù)處理工序。如果是這樣拙泽,只訓(xùn)練相似數(shù)據(jù)點是個不錯的想法淌山。

準(zhǔn)備

我們的老朋友,回歸顾瞻,可以用于聚類的上下文中泼疑。回歸顯然是個監(jiān)督學(xué)習(xí)技巧荷荤,所以我們使用 KNN 而不是 KMeans退渗。

對于 KNN 回歸來說,我們使用特征空間中的 K 個最近點蕴纳,來構(gòu)建回歸会油,而不像常規(guī)回歸那樣使用整個特征空間。

操作步驟

對于這個秘籍古毛,我們使用iris數(shù)據(jù)集翻翩。如果我們打算預(yù)測一些東西,例如每朵花的花瓣寬度稻薇,根據(jù)iris物種來聚類可能會給我們更好的結(jié)果嫂冻。KNN 回歸不會根據(jù)物種來聚類,但我們的假設(shè)是塞椎,相同物種的 X 會接近桨仿,或者這個案例中,是花瓣長度忱屑。

對于這個秘籍蹬敲,我們使用iris數(shù)據(jù)集:

>>> from sklearn import datasets 
>>> iris = datasets.load_iris() 
>>> iris.feature_names ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)',  'petal width (cm)'] 

我們嘗試基于萼片長度和寬度來預(yù)測花瓣長度。我們同時訓(xùn)練一個線性回歸莺戒,來對比觀察 KNN 回歸有多好伴嗡。

>>> from sklearn.linear_model import LinearRegression 
>>> lr = LinearRegression() 
>>> lr.fit(X, y) 
>>> print "The MSE is: {:.2}".format(np.power(y - lr.predict(X),
           2).mean()) 
The MSE is: 0.15 

現(xiàn)在,對于 KNN 回歸从铲,使用下列代碼:

>>> from sklearn.neighbors import KNeighborsRegressor 
>>> knnr = KNeighborsRegressor(n_neighbors=10) 
>>> knnr.fit(X, y) 
>>> print "The MSE is: {:.2}".format(np.power(y - knnr.predict(X),
           2).mean()) 
The MSE is: 0.069

讓我們看看瘪校,當(dāng)我們讓它使用最接近的 10 個點用于回歸時,KNN 回歸會做什么名段?

>>> f, ax = plt.subplots(nrows=2, figsize=(7, 10))
>>> ax[0].set_title("Predictions")
>>> ax[0].scatter(X[:, 0], X[:, 1], s=lr.predict(X)*80, label='LR
    Predictions', color='c', edgecolors='black') 
>>> ax[1].scatter(X[:, 0], X[:, 1], s=knnr.predict(X)*80, label='k-NN
    Predictions', color='m', edgecolors='black')
>>> ax[0].legend() 
>>> ax[1].legend() 

輸出如下:

很顯然阱扬,預(yù)測大部分都是接近的。但是讓我們與實際情況相比伸辟,看看 Setosa 物種的預(yù)測:

>>> setosa_idx = np.where(iris.target_names=='setosa') 
>>> setosa_mask = iris.target == setosa_idx[0] 
>>> y[setosa_mask][:5] array([ 0.2,  0.2,  0.2,  0.2,  0.2]) 
>>> knnr.predict(X)[setosa_mask][:5] 
array([ 0.28,  0.17,  0.21,  0.2 ,  0.31])
>>> lr.predict(X)[setosa_mask][:5] 
array([ 0.44636645, 0.53893889, 0.29846368, 0.27338255, 0.32612885]) 

再次觀察繪圖麻惶,Setosa 物種(左上方的簇)被線性回歸估計過高,但是 KNN 非常接近真實值信夫。

工作原理

KNN 回歸非常簡單窃蹋,它計算被測試點的 K 個最接近點的均值卡啰。

讓我們手動預(yù)測單個點:

>>> example_point = X[0

現(xiàn)在,我們需要獲取離我們的our_example_point最近的 10 個點:

>>> from sklearn.metrics import pairwise 
>>> distances_to_example = pairwise.pairwise_distances(X)[0] 
>>> ten_closest_points = X[np.argsort(distances_to_example)][:10] 
>>> ten_closest_y = y[np.argsort(distances_to_example)][:10]
>>> ten_closest_y.mean() 
0.28000 

我們可以看到它非常接近預(yù)期警没。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匈辱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杀迹,更是在濱河造成了極大的恐慌亡脸,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件树酪,死亡現(xiàn)場離奇詭異浅碾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嗅回,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門及穗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绵载,你說我怎么就攤上這事】涟祝” “怎么了娃豹?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長购裙。 經(jīng)常有香客問我懂版,道長,這世上最難降的妖魔是什么躏率? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任躯畴,我火速辦了婚禮,結(jié)果婚禮上薇芝,老公的妹妹穿的比我還像新娘蓬抄。我一直安慰自己,他們只是感情好夯到,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布嚷缭。 她就那樣靜靜地躺著,像睡著了一般耍贾。 火紅的嫁衣襯著肌膚如雪阅爽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天荐开,我揣著相機(jī)與錄音付翁,去河邊找鬼。 笑死晃听,一個胖子當(dāng)著我的面吹牛百侧,可吹牛的內(nèi)容都是我干的着帽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼移层,長吁一口氣:“原來是場噩夢啊……” “哼仍翰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起观话,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤予借,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后频蛔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灵迫,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年晦溪,在試婚紗的時候發(fā)現(xiàn)自己被綠了瀑粥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡三圆,死狀恐怖狞换,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情舟肉,我是刑警寧澤修噪,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站路媚,受9級特大地震影響黄琼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜整慎,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一脏款、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧裤园,春花似錦撤师、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至强法,卻和暖如春万俗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饮怯。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工闰歪, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蓖墅。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓库倘,卻偏偏與公主長得像临扮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子教翩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容