天天看點

經典算法筆記:無監督算法(聚類、降維)

本文是吳恩達老師的機器學習課程[1]的筆記和代碼複現部分(聚類、降維)。

作者:黃海廣[2]

備注:筆記和作業(含資料、原始作業檔案)、視訊都在github[3]中下載下傳。

我将陸續将課程筆記和課程代碼釋出在公衆号“機器學習初學者”,敬請關注。這個是第四部分:無監督學習,是原教程的第8周,包含了筆記和作業代碼(原課程作業是 OCTAVE的,這裡是複現的 python 代碼)

第一部分:回歸

第二部分:邏輯回歸

第三部分:支援向量機

本文作業代碼[4]可以下載下傳完整版

筆記的markdown 檔案[5]

筆記的pdf 檔案[6]

筆記部分目錄

十三、聚類(Clustering)

13.1 無監督學習:簡介

參考視訊: 13 - 1 - Unsupervised Learning_ Introduction (3 min).mkv

在這個視訊中,我将開始介紹聚類算法。這将是一個激動人心的時刻,因為這是我們學習的第一個非監督學習算法。我們将要讓計算機學習無标簽資料,而不是此前的标簽資料。

那麼,什麼是非監督學習呢?在課程的一開始,我曾簡單的介紹過非監督學習,然而,我們還是有必要将其與監督學習做一下比較。

在一個典型的監督學習中,我們有一個有标簽的訓練集,我們的目标是找到能夠區分正樣本和負樣本的決策邊界,在這裡的監督學習中,我們有一系列标簽,我們需要據此拟合一個假設函數。與此不同的是,在非監督學習中,我們的資料沒有附帶任何标簽,我們拿到的資料就是這樣的:

經典算法筆記:無監督算法(聚類、降維)

在這裡我們有一系列點,卻沒有标簽。是以,我們的訓練集可以寫成隻有

,

…..一直到

。我們沒有任何标簽

。是以,圖上畫的這些點沒有标簽資訊。也就是說,在非監督學習中,我們需要将一系列無标簽的訓練資料,輸入到一個算法中,然後我們告訴這個算法,快去為我們找找這個資料的内在結構給定資料。我們可能需要某種算法幫助我們尋找一種結構。圖上的資料看起來可以分成兩個分開的點集(稱為簇),一個能夠找到我圈出的這些點集的算法,就被稱為聚類算法。

經典算法筆記:無監督算法(聚類、降維)

這将是我們介紹的第一個非監督學習算法。當然,此後我們還将提到其他類型的非監督學習算法,它們可以為我們找到其他類型的結構或者其他的一些模式,而不隻是簇。

我們将先介紹聚類算法。此後,我們将陸續介紹其他算法。那麼聚類算法一般用來做什麼呢?

經典算法筆記:無監督算法(聚類、降維)

在這門課程的早些時候,我曾經列舉過一些應用:比如市場分割。也許你在資料庫中存儲了許多客戶的資訊,而你希望将他們分成不同的客戶群,這樣你可以對不同類型的客戶分别銷售産品或者分别提供更适合的服務。社交網絡分析:事實上有許多研究人員正在研究這樣一些内容,他們關注一群人,關注社交網絡,例如Facebook,Google+,或者是其他的一些資訊,比如說:你經常跟哪些人聯系,而這些人又經常給哪些人發郵件,由此找到關系密切的人群。是以,這可能需要另一個聚類算法,你希望用它發現社交網絡中關系密切的朋友。我有一個朋友正在研究這個問題,他希望使用聚類算法來更好的組織計算機叢集,或者更好的管理資料中心。因為如果你知道資料中心中,那些計算機經常協作工作。那麼,你可以重新配置設定資源,重新布局網絡。由此優化資料中心,優化資料通信。

最後,我實際上還在研究如何利用聚類算法了解星系的形成。然後用這個知識,了解一些天文學上的細節問題。好的,這就是聚類算法。這将是我們介紹的第一個非監督學習算法。在下一個視訊中,我們将開始介紹一個具體的聚類算法。

13.2 K-均值算法

參考視訊: 13 - 2 - K-Means Algorithm (13 min).mkv

K-均值是最普及的聚類算法,算法接受一個未标記的資料集,然後将資料聚類成不同的組。

K-均值是一個疊代算法,假設我們想要将資料聚類成 n 個組,其方法為:

首先選擇

個随機的點,稱為聚類中心(cluster centroids);

對于資料集中的每一個資料,按照距離

個中心點的距離,将其與距離最近的中心點關聯起來,與同一個中心點關聯的所有點聚成一類。

計算每一個組的平均值,将該組所關聯的中心點移動到平均值的位置。

重複步驟 2-4 直至中心點不再變化。

下面是一個聚類示例:

經典算法筆記:無監督算法(聚類、降維)

疊代 1 次

經典算法筆記:無監督算法(聚類、降維)

疊代 3 次

經典算法筆記:無監督算法(聚類、降維)

疊代 10 次

μ

,

μ

,...,

μ

 來表示聚類中心,用

,

,...,

來存儲與第

個執行個體資料最近的聚類中心的索引,K-均值算法的僞代碼如下:

Repeat {
for i = 1 to m
   c(i) := index (form 1 to K) of cluster centroid closest to x(i)


for k = 1 to K
   μk := average (mean) of points assigned to cluster k
}      

算法分為兩個步驟,第一個for循環是指派步驟,即:對于每一個樣例

,計算其應該屬于的類。第二個for循環是聚類中心的移動,即:對于每一個類

,重新計算該類的質心。

K-均值算法也可以很便利地用于将資料分為許多不同組,即使在沒有非常明顯區分的組群的情況下也可以。下圖所示的資料集包含身高和體重兩項特征構成的,利用K-均值算法将資料分為三類,用于幫助确定将要生産的 T-恤衫的三種尺寸。

經典算法筆記:無監督算法(聚類、降維)

13.3 優化目标

參考視訊: 13 - 3 - Optimization Objective (7 min).mkv

K-均值最小化問題,是要最小化所有的資料點與其所關聯的聚類中心點之間的距離之和,是以 K-均值的代價函數(又稱畸變函數 Distortion function)為:

其中

代表與

最近的聚類中心點。我們的的優化目标便是找出使得代價函數最小的 

,

,...,

,

,...,

經典算法筆記:無監督算法(聚類、降維)

回顧剛才給出的:K-均值疊代算法,我們知道,第一個循環是用于減小

引起的代價,而第二個循環則是用于減小

引起的代價。疊代的過程一定會是每一次疊代都在減小代價函數,不然便是出現了錯誤。

13.4 随機初始化

參考視訊: 13 - 4 - Random Initialization (8 min).mkv

在運作 K-均值算法的之前,我們首先要随機初始化所有的聚類中心點,下面介紹怎樣做:

  1. 我們應該選擇$K
  2. 随機選擇

    個訓練執行個體,然後令

    個聚類中心分别與這

    個訓練執行個體相等

K-均值的一個問題在于,它有可能會停留在一個局部最小值處,而這取決于初始化的情況。

經典算法筆記:無監督算法(聚類、降維)

為了解決這個問題,我們通常需要多次運作K-均值算法,每一次都重新進行随機初始化,最後再比較多次運作K-均值的結果,選擇代價函數最小的結果。這種方法在

較小的時候(2--10)還是可行的,但是如果

較大,這麼做也可能不會有明顯地改善。

13.5 選擇聚類數

參考視訊: 13 - 5 - Choosing the Number of Clusters (8 min).mkv

沒有所謂最好的選擇聚類數的方法,通常是需要根據不同的問題,人工進行選擇的。選擇的時候思考我們運用K-均值算法聚類的動機是什麼,然後選擇能最好服務于該目的标聚類數。

當人們在讨論,選擇聚類數目的方法時,有一個可能會談及的方法叫作“肘部法則”。關于“肘部法則”,我們所需要做的是改變

值,也就是聚類類别數目的總數。我們用一個聚類來運作K 均值聚類方法。這就意味着,所有的資料都會分到一個聚類裡,然後計算成本函數或者計算畸變函數

代表聚類數字。

經典算法筆記:無監督算法(聚類、降維)

我們可能會得到一條類似于這樣的曲線。像一個人的肘部。這就是“肘部法則”所做的,讓我們來看這樣一個圖,看起來就好像有一個很清楚的肘在那兒。好像人的手臂,如果你伸出你的胳膊,那麼這就是你的肩關節、肘關節、手。這就是“肘部法則”。你會發現這種模式,它的畸變值會迅速下降,從 1 到 2,從 2 到 3 之後,你會在 3 的時候達到一個肘點。在此之後,畸變值就下降的非常慢,看起來就像使用 3 個聚類來進行聚類是正确的,這是因為那個點是曲線的肘點,畸變值下降得很快,

之後就下降得很慢,那麼我們就選

。當你應用“肘部法則”的時候,如果你得到了一個像上面這樣的圖,那麼這将是一種用來選擇聚類個數的合理方法。

例如,我們的 T-恤制造例子中,我們要将使用者按照身材聚類,我們可以分成 3 個尺寸:

,也可以分成 5 個尺寸

,這樣的選擇是建立在回答“聚類後我們制造的 T-恤是否能較好地适合我們的客戶”這個問題的基礎上作出的。

聚類參考資料:

1.相似度/距離計算方法總結

(1). 闵可夫斯基距離Minkowski/(其中歐式距離:

)

(2). 傑卡德相似系數(Jaccard):

(3). 餘弦相似度(cosine similarity):

維向量

的夾角記做

,根據餘弦定理,其餘弦值為:

(4). Pearson 皮爾遜相關系數: 

Pearson 相關系數即将

坐标向量各自平移到原點後的夾角餘弦。

2.聚類的衡量名額

(1). 均一性:

類似于精确率,一個簇中隻包含一個類别的樣本,則滿足均一性。其實也可以認為就是正确率(每個 聚簇中正确分類的樣本數占該聚簇總樣本數的比例和)

(2). 完整性:

類似于召回率,同類别樣本被歸類到相同簇中,則滿足完整性;每個聚簇中正确分類的樣本數占該 類型的總樣本數比例的和

(3). V-measure:

均一性和完整性的權重平均

(4). 輪廓系數

樣本

的輪廓系數:

簇内不相似度:計算樣本

到同簇其它樣本的平均距離為

,應盡可能小。

簇間不相似度:計算樣本

到其它簇

的所有樣本的平均距離

,應盡可能大。

輪廓系數:

值越接近 1 表示樣本

聚類越合理,越接近-1,表示樣本

應該分類到 另外的簇中,近似為 0,表示樣本

應該在邊界上;所有樣本的

的均值被成為聚類結果的輪廓系數。

(5). ARI

資料集

共有

個元素, 兩個聚類結果分别是:

的元素個數為:

記:

十四、降維(Dimensionality Reduction)

14.1 動機一:資料壓縮

參考視訊: 14 - 1 - Motivation I_ Data Compression (10 min).mkv

這個視訊,我想開始談論第二種類型的無監督學習問題,稱為降維。有幾個不同的的原因使你可能想要做降維。一是資料壓縮,後面我們會看了一些視訊後,資料壓縮不僅允許我們壓縮資料,因而使用較少的計算機記憶體或磁盤空間,但它也讓我們加快我們的學習算法。

但首先,讓我們談論降維是什麼。作為一種生動的例子,我們收集的資料集,有許多,許多特征,我繪制兩個在這裡。

經典算法筆記:無監督算法(聚類、降維)

假設我們未知兩個的特征:

:長度:用厘米表示;

:是用英寸表示同一物體的長度。

是以,這給了我們高度備援表示,也許不是兩個分開的特征

,這兩個基本的長度度量,也許我們想要做的是減少資料到一維,隻有一個數測量這個長度。這個例子似乎有點做作,這裡厘米英寸的例子實際上不是那麼不切實際的,兩者并沒有什麼不同。

将資料從二維降至一維:假使我們要采用兩種不同的儀器來測量一些東西的尺寸,其中一個儀器測量結果的機關是英寸,另一個儀器測量的結果是厘米,我們希望将測量的結果作為我們機器學習的特征。現在的問題的是,兩種儀器對同一個東西測量的結果不完全相等(由于誤差、精度等),而将兩者都作為特征有些重複,因而,我們希望将這個二維的資料降至一維。

從這件事情我看到的東西發生在工業上的事。如果你有幾百個或成千上萬的特征,它是它這往往容易失去你需要的特征。有時可能有幾個不同的工程團隊,也許一個工程隊給你二百個特征,第二工程隊給你另外三百個的特征,第三工程隊給你五百個特征,一千多個特征都在一起,它實際上會變得非常困難,去跟蹤你知道的那些特征,你從那些工程隊得到的。其實不想有高度備援的特征一樣。

經典算法筆記:無監督算法(聚類、降維)

多年我一直在研究直升飛機自動駕駛。諸如此類。如果你想測量——如果你想做,你知道,做一個調查或做這些不同飛行員的測試——你可能有一個特征:

,這也許是他們的技能(直升機飛行員),也許

可能是飛行員的愛好。這是表示他們是否喜歡飛行,也許這兩個特征将高度相關。你真正關心的可能是這條紅線的方向,不同的特征,決定飛行員的能力。

經典算法筆記:無監督算法(聚類、降維)

将資料從三維降至二維:這個例子中我們要将一個三維的特征向量降至一個二維的特征向量。過程是與上面類似的,我們将三維向量投射到一個二維的平面上,強迫使得所有的資料都在同一個平面上,降至二維的特征向量。

經典算法筆記:無監督算法(聚類、降維)

這樣的處理過程可以被用于把任何次元的資料降到任何想要的次元,例如将 1000 維的特征降至 100 維。

正如我們所看到的,最後,這将使我們能夠使我們的一些學習算法運作也較晚,但我們會在以後的視訊提到它。

14.2 動機二:資料可視化

參考視訊: 14 - 2 - Motivation II_ Visualization (6 min).mkv

在許多及其學習問題中,如果我們能将資料可視化,我們便能尋找到一個更好的解決方案,降維可以幫助我們。

經典算法筆記:無監督算法(聚類、降維)

假使我們有有關于許多不同國家的資料,每一個特征向量都有 50 個特征(如GDP,人均GDP,平均壽命等)。如果要将這個 50 維的資料可視化是不可能的。使用降維的方法将其降至 2 維,我們便可以将其可視化了。

經典算法筆記:無監督算法(聚類、降維)

這樣做的問題在于,降維的算法隻負責減少維數,新産生的特征的意義就必須由我們自己去發現了。

14.3 主成分分析問題

參考視訊: 14 - 3 - Principal Component Analysis Problem Formulation (9 min). mkv

主成分分析(PCA)是最常見的降維算法。

在PCA中,我們要做的是找到一個方向向量(Vector direction),當我們把所有的資料都投射到該向量上時,我們希望投射平均均方誤差能盡可能地小。方向向量是一個經過原點的向量,而投射誤差是從特征向量向該方向向量作垂線的長度。

經典算法筆記:無監督算法(聚類、降維)

下面給出主成分分析問題的描述:

問題是要将

維資料降至

維,目标是找到向量

,

,...,

使得總的投射誤差最小。主成分分析與線性回顧的比較:

主成分分析與線性回歸是兩種不同的算法。主成分分析最小化的是投射誤差(Projected Error),而線性回歸嘗試的是最小化預測誤差。線性回歸的目的是預測結果,而主成分分析不作任何預測。

經典算法筆記:無監督算法(聚類、降維)

上圖中,左邊的是線性回歸的誤差(垂直于橫軸投影),右邊則是主要成分分析的誤差(垂直于紅線投影)。

PCA将

個特征降維到

個,可以用來進行資料壓縮,如果 100 維的向量最後可以用 10 維來表示,那麼壓縮率為 90%。同樣圖像處理領域的KL 變換使用PCA做圖像壓縮。但PCA 要保證降維後,還要保證資料的特性損失最小。

PCA技術的一大好處是對資料進行降維的處理。我們可以對新求出的“主元”向量的重要性進行排序,根據需要取前面最重要的部分,将後面的維數省去,可以達到降維進而簡化模型或是對資料進行壓縮的效果。同時最大程度的保持了原有資料的資訊。

PCA技術的一個很大的優點是,它是完全無參數限制的。在PCA的計算過程中完全不需要人為的設定參數或是根據任何經驗模型對計算進行幹預,最後的結果隻與資料相關,與使用者是獨立的。

但是,這一點同時也可以看作是缺點。如果使用者對觀測對象有一定的先驗知識,掌握了資料的一些特征,卻無法通過參數化等方法對處理過程進行幹預,可能會得不到預期的效果,效率也不高。

14.4 主成分分析算法

參考視訊: 14 - 4 - Principal Component Analysis Algorithm (15 min).mkv

PCA 減少

維到

維:

第一步是均值歸一化。我們需要計算出所有特征的均值,然後令 

μ

。如果特征是在不同的數量級上,我們還需要将其除以标準差 

第二步是計算協方差矩陣(covariance matrix)

: 

第三步是計算協方差矩陣

的特征向量(eigenvectors):

在 Octave 裡我們可以利用奇異值分解(singular value decomposition)來求解,​

​[U, S, V]= svd(sigma)​

​。

經典算法筆記:無監督算法(聚類、降維)
經典算法筆記:無監督算法(聚類、降維)

對于一個 

次元的矩陣,上式中的

是一個具有與資料之間最小投射誤差的方向向量構成的矩陣。如果我們希望将資料從

維降至

維,我們隻需要從

中選取前

個向量,獲得一個

次元的矩陣,我們用

表示,然後通過如下計算獲得要求的新特征向量

:

其中

維的,是以結果為

次元。注,我們不對方差特征進行處理。

14.5 選擇主成分的數量

參考視訊: 14 - 5 - Choosing The Number Of Principal Components (13 min).mkv

主要成分分析是減少投射的平均均方誤差:

訓練集的方差為:

我們希望在平均均方誤差與訓練集方差的比例盡可能小的情況下選擇盡可能小的

值。

如果我們希望這個比例小于 1%,就意味着原本資料的偏差有 99%都保留下來了,如果我們選擇保留 95%的偏差,便能非常顯著地降低模型中特征的次元了。

我們可以先令

,然後進行主要成分分析,獲得

,然後計算比例是否小于 1%。如果不是的話再令

,如此類推,直到找到可以使得比例小于 1%的最小

還有一些更好的方式來選擇

,當我們在Octave中調用“svd”函數的時候,我們獲得三個參數:​

​[U, S, V] = svd(sigma)​

​。

經典算法筆記:無監督算法(聚類、降維)

其中的

是一個

的矩陣,隻有對角線上有值,而其它單元都是 0,我們可以使用這個矩陣來計算平均均方誤差與訓練集方差的比例:

也就是:

在壓縮過資料後,我們可以采用如下方法來近似地獲得原有的特征:

14.6 重建的壓縮表示

參考視訊: 14 - 6 - Reconstruction from Compressed Representation (4 min).mkv

在以前的視訊中,我談論PCA作為壓縮算法。在那裡你可能需要把 1000 維的資料壓縮 100 維特征,或具有三維資料壓縮到一二維表示。是以,如果這是一個壓縮算法,應該能回到這個壓縮表示,回到你原有的高維資料的一種近似。

是以,給定的

,這可能 100 維,怎麼回到你原來的表示

,這可能是 1000 維的數組?

經典算法筆記:無監督算法(聚類、降維)

PCA算法,我們可能有一個這樣的樣本。如圖中樣本

,

。我們做的是,我們把這些樣本投射到圖中這個一維平面。然後現在我們需要隻使用一個實數,比如

,指定這些點的位置後他們被投射到這一個三維曲面。給定一個點

,我們怎麼能回去這個原始的二維空間呢?

為 2 維,

為 1 維,

,相反的方程為:

,

。如圖:

經典算法筆記:無監督算法(聚類、降維)

如你所知,這是一個漂亮的與原始資料相當相似。是以,這就是你從低維表示

回到未壓縮的表示。我們得到的資料的一個之間你的原始資料 

,我們也把這個過程稱為重建原始資料。

當我們認為試圖重建從壓縮表示 

 的初始值。是以,給定未标記的資料集,您現在知道如何應用PCA,你的帶高維特征

和映射到這的低維表示

。這個視訊,希望你現在也知道如何采取這些低維表示

,映射到備份到一個近似你原有的高維資料。

現在你知道如何實施應用PCA,我們将要做的事是談論一些技術在實際使用PCA很好,特别是,在接下來的視訊中,我想談一談關于如何選擇

14.7 主成分分析法的應用建議

參考視訊: 14 - 7 - Advice for Applying PCA (13 min).mkv

假使我們正在針對一張 100×100 像素的圖檔進行某個計算機視覺的機器學習,即總共有 10000 個特征。

  1. 第一步是運用主要成分分析将資料壓縮至 1000 個特征
  2. 然後對訓練集運作學習算法
  3. 在預測時,采用之前學習而來的

    将輸入的特征

    轉換成特征向量

    ,然後再進行預測

注:如果我們有交叉驗證集合測試集,也采用對訓練集學習而來的

錯誤的主要成分分析情況:一個常見錯誤使用主要成分分析的情況是,将其用于減少過拟合(減少了特征的數量)。這樣做非常不好,不如嘗試正則化處理。原因在于主要成分分析隻是近似地丢棄掉一些特征,它并不考慮任何與結果變量有關的資訊,是以可能會丢失非常重要的特征。然而當我們進行正則化處理時,會考慮到結果變量,不會丢掉重要的資料。

另一個常見的錯誤是,預設地将主要成分分析作為學習過程中的一部分,這雖然很多時候有效果,最好還是從所有原始特征開始,隻在有必要的時候(算法運作太慢或者占用太多記憶體)才考慮采用主要成分分析。

代碼部分:

機器學習練習 4 - K-means 和 PCA(主成分分析)

代碼修改并注釋:黃海廣,[email protected]

在本練習中,我們将實作 K-means 聚類,并使用它來壓縮圖像。 我們将從一個簡單的 2D 資料集開始,以了解 K-means 是如何工作的,然後我們将其應用于圖像壓縮。 我們還将對主成分分析進行實驗,并了解如何使用它來找到面部圖像的低維表示。

K-means 聚類

我們将實施和應用 K-means 到一個簡單的二維資料集,以獲得一些直覺的工作原理。 K-means 是一個疊代的,無監督的聚類算法,将類似的執行個體組合成簇。 該算法通過猜測每個簇的初始聚類中心開始,然後重複将執行個體配置設定給最近的簇,并重新計算該簇的聚類中心。 我們要實作的第一部分是找到資料中每個執行個體最接近的聚類中心的函數。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
from scipy.io import loadmat      
def find_closest_centroids(X, centroids):
    m = X.shape[0]
    k = centroids.shape[0]
    idx = np.zeros(m)


    for i in range(m):
        min_dist = 1000000
        for j in range(k):
            dist = np.sum((X[i,:] - centroids[j,:]) ** 2)
            if dist < min_dist:
                min_dist = dist
                idx[i] = j


    return idx      

讓我們來測試這個函數,以確定它的工作正常。 我們将使用練習中提供的測試用例。

data = loadmat('data/ex7data2.mat')
X = data['X']
initial_centroids = initial_centroids = np.array([[3, 3], [6, 2], [8, 5]])


idx = find_closest_centroids(X, initial_centroids)
idx[0:3]      
array([0., 2., 1.])      

輸出與文本中的預期值比對(記住我們的數組是從零開始索引的,而不是從一開始索引的,是以值比練習中的值低一個)。 接下來,我們需要一個函數來計算簇的聚類中心。 聚類中心隻是目前配置設定給簇的所有樣本的平均值。

data2 = pd.DataFrame(data.get('X'), columns=['X1', 'X2'])
data2.head()      
X1 X2
1.842080 4.607572
1 5.658583 4.799964
2 6.352579 3.290854
3 2.904017 4.612204
4 3.231979 4.939894
sb.set(context="notebook", style="white")
sb.lmplot('X1', 'X2', data=data2, fit_reg=False)
plt.show()      
經典算法筆記:無監督算法(聚類、降維)
def compute_centroids(X, idx, k):
    m, n = X.shape
    centroids = np.zeros((k, n))


    for i in range(k):
        indices = np.where(idx == i)
        centroids[i,:] = (np.sum(X[indices,:], axis=1) / len(indices[0])).ravel()


    return centroids      
compute_centroids(X, idx, 3)      
array([[2.42830111, 3.15792418],
       [5.81350331, 2.63365645],
       [7.11938687, 3.6166844 ]])      

此輸出也符合練習中的預期值。 下一部分涉及實際運作該算法的一些疊代次數和可視化結果。 這個步驟是由于并不複雜,我将從頭開始建構它。 為了運作算法,我們隻需要在将樣本配置設定給最近的簇并重新計算簇的聚類中心。

def run_k_means(X, initial_centroids, max_iters):
    m, n = X.shape
    k = initial_centroids.shape[0]
    idx = np.zeros(m)
    centroids = initial_centroids


    for i in range(max_iters):
        idx = find_closest_centroids(X, centroids)
        centroids = compute_centroids(X, idx, k)


    return idx, centroids      
idx, centroids = run_k_means(X, initial_centroids, 10)      
cluster1 = X[np.where(idx == 0)[0],:]
cluster2 = X[np.where(idx == 1)[0],:]
cluster3 = X[np.where(idx == 2)[0],:]


fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(cluster1[:,0], cluster1[:,1], s=30, color='r', label='Cluster 1')
ax.scatter(cluster2[:,0], cluster2[:,1], s=30, color='g', label='Cluster 2')
ax.scatter(cluster3[:,0], cluster3[:,1], s=30, color='b', label='Cluster 3')
ax.legend()
plt.show()      
經典算法筆記:無監督算法(聚類、降維)

我們跳過的一個步驟是初始化聚類中心的過程。 這可以影響算法的收斂。 我們的任務是建立一個選擇随機樣本并将其用作初始聚類中心的函數。

def init_centroids(X, k):
    m, n = X.shape
    centroids = np.zeros((k, n))
    idx = np.random.randint(0, m, k)


    for i in range(k):
        centroids[i,:] = X[idx[i],:]


    return centroids      
init_centroids(X, 3)      
array([[0.99253246, 5.01567424],
       [6.63060699, 3.01502301],
       [4.88804332, 5.50670795]])      
from sklearn.cluster import KMeans#導入kmeans庫


model = KMeans(n_clusters=3, n_init=100, n_jobs=-1)      
model.fit(data2)      
KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
    n_clusters=3, n_init=100, n_jobs=-1, precompute_distances='auto',
    random_state=None, tol=0.0001, verbose=0)      
centroids = model.cluster_centers_
print(centroids.shape)


C = model.predict(data2)
print(C.shape)      
(3, 2)
(300,)      
centroids[C].shape      
(300, 2)      

肘部法則

當人們在讨論,選擇聚類數目的方法時,有一個可能會談及的方法叫作“肘部法則”。關于“肘部法則”,我們所需要做的是改變 K 值,也就是聚類類别數目的總數。我們用一個聚類來運作 K 均值聚類方法。這就意味着,所有的資料都會分到一個聚類裡,然後計算成本函數或者計算畸變函數 J。K 代表聚類數字。

我們可能會得到一條類似于這樣的曲線。像一個人的肘部。這就是“肘部法則”所做的,讓我們來看這樣一個圖,看起來就好像有一個很清楚的肘在那兒。好像人的手臂,如果你伸出你的胳膊,那麼這就是你的肩關節、肘關節、手。這就是“肘部法則”。你會發現這種模式,它的畸變值會迅速下降,從 1 到 2,從 2 到 3 之後,你會在 2 的時候達到一個肘點。在此之後,畸變值就下降的非常慢,看起來就像使用 3 個聚類來進行聚類是正确的,這是因為那個點是曲線的肘點,畸變值下降得很快,K=2 之後就下降得很慢,那麼我們就選 K=2。當你應用“肘部法則”的時候,如果你得到了一個像下面這樣的圖,那麼這将是一種用來選擇聚類個數的合理方法。

import numpy as np
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt


c1x = np.random.uniform(0.5, 1.5, (1, 10))
c1y = np.random.uniform(0.5, 1.5, (1, 10))
c2x = np.random.uniform(3.5, 4.5, (1, 10))
c2y = np.random.uniform(3.5, 4.5, (1, 10))
x = np.hstack((c1x, c2x))
y = np.hstack((c1y, c2y))
X = np.vstack((x, y)).T


K = range(1, 10)
meanDispersions = []
for k in K:
    kmeans = KMeans(n_clusters=k)
    kmeans.fit(X)
    meanDispersions.append(sum(np.min(cdist(X, kmeans.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])


plt.plot(K, meanDispersions, 'bx-')
plt.xlabel('k')
plt.ylabel('Average Dispersion')
plt.title('Selecting k with the Elbow Method')
plt.show()      
經典算法筆記:無監督算法(聚類、降維)
#導入相應的包
import scipy
import scipy.cluster.hierarchy as sch
from scipy.cluster.vq import vq,kmeans,whiten
import numpy as np
import matplotlib.pylab as plt
import pandas as pd
from sklearn import preprocessing
from sklearn.decomposition import PCA      
newData=np.load("data/filename.npy")      
newData      
array([[-1.18945132, -0.31092235],
       [ 2.06415695, -0.74854414],
       [-1.43769023, -0.80669682],
       [-3.23039706,  0.84519783],
       [ 2.36892693, -0.44480961],
       [ 0.28997221,  2.79266758],
       [ 1.2099519 , -0.00638496],
       [-2.09689459, -0.22796377],
       [ 5.50091159, -0.14275827],
       [-3.47948639, -0.94978548]])      
#1. 層次聚類
#生成點與點之間的距離矩陣,這裡用的歐氏距離:
disMat = sch.distance.pdist(newData,'euclidean')
#進行層次聚類:
Z=sch.linkage(disMat,method='average')
#将層級聚類結果以樹狀圖表示出來并儲存為plot_dendrogram.png
P=sch.dendrogram(Z)      
經典算法筆記:無監督算法(聚類、降維)

dbscan 密度聚類

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_blobs
# from  .agglomerative_clustering import test_AgglomerativeClustering,test_AgglomerativeClustering_nclusters,test_AgglomerativeClustering_linkage
# from .dbscan import test_DBSCAN,test_DBSCAN_epsilon,test_DBSCAN_min_samples
# from chapters.Cluster_EM.gmm import test_GMM,test_GMM_cov_type,test_GMM_n_components
# from .kmeans import test_Kmeans,test_Kmeans_n_init,test_Kmeans_nclusters


def create_data(centers,num=100,std=0.7):
    '''
    生成用于聚類的資料集


    :param centers: 聚類的中心點組成的數組。如果中心點是二維的,則産生的每個樣本都是二維的。
    :param num: 樣本數
    :param std: 每個簇中樣本的标準差
    :return: 用于聚類的資料集。是一個元組,第一個元素為樣本集,第二個元素為樣本集的真實簇分類标記
    '''
    X, labels_true = make_blobs(n_samples=num, centers=centers, cluster_std=std)
    return  X,labels_true
def plot_data(*data):
    '''
    繪制用于聚類的資料集


    :param data: 可變參數。它是一個元組。元組元素依次為:第一個元素為樣本集,第二個元素為樣本集的真實簇分類标記
    :return: None
    '''
    X,labels_true=data
    labels=np.unique(labels_true)
    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)
    colors='rgbyckm' # 每個簇的樣本标記不同的顔色
    for i,label in enumerate(labels):
        position=labels_true==label
        ax.scatter(X[position,0],X[position,1],label="cluster %d"%label,
    color=colors[i%len(colors)])


    ax.legend(loc="best",framealpha=0.5)
    ax.set_xlabel("X[0]")
    ax.set_ylabel("Y[1]")
    ax.set_title("data")
    plt.show()      
from sklearn import  cluster
from sklearn.metrics import adjusted_rand_score
import matplotlib.pyplot as plt
plt.figure(figsize=(12,12))
def test_DBSCAN(*data):
    '''
    測試 DBSCAN 的用法


    :param data:  可變參數。它是一個元組。元組元素依次為:第一個元素為樣本集,第二個元素為樣本集的真實簇分類标記
    :return: None
    '''
    X,labels_true=data
    clst=cluster.DBSCAN()
    predicted_labels=clst.fit_predict(X)
    print("ARI:%s"% adjusted_rand_score(labels_true,predicted_labels))
    print("Core sample num:%d"%len(clst.core_sample_indices_))
def test_DBSCAN_epsilon(*data):
    '''
    測試 DBSCAN 的聚類結果随  eps 參數的影響


    :param data:  可變參數。它是一個元組。元組元素依次為:第一個元素為樣本集,第二個元素為樣本集的真實簇分類标記
    :return: None
    '''
    X,labels_true=data
    epsilons=np.logspace(-1,1.5)
    ARIs=[]
    Core_nums=[]
    for epsilon in epsilons:
        clst=cluster.DBSCAN(eps=epsilon)
        predicted_labels=clst.fit_predict(X)
        ARIs.append( adjusted_rand_score(labels_true,predicted_labels))
        Core_nums.append(len(clst.core_sample_indices_))


    ## 繪圖
    fig=plt.figure()
    ax=fig.add_subplot(1,2,1)
    ax.plot(epsilons,ARIs,marker='+')
    ax.set_xscale('log')
    ax.set_xlabel(r"$\epsilon$")
    ax.set_ylim(0,1)
    ax.set_ylabel('ARI')


    ax=fig.add_subplot(1,2,2)
    ax.plot(epsilons,Core_nums,marker='o')
    ax.set_xscale('log')
    ax.set_xlabel(r"$\epsilon$")
    ax.set_ylabel('Core_Nums')


    fig.suptitle("DBSCAN")
    plt.show()
def test_DBSCAN_min_samples(*data):
    '''
    測試 DBSCAN 的聚類結果随  min_samples 參數的影響


    :param data:  可變參數。它是一個元組。元組元素依次為:第一個元素為樣本集,第二個元素為樣本集的真實簇分類标記
    :return:  None
    '''
    X,labels_true=data
    min_samples=range(1,100)
    ARIs=[]
    Core_nums=[]
    for num in min_samples:
        clst=cluster.DBSCAN(min_samples=num)
        predicted_labels=clst.fit_predict(X)
        ARIs.append( adjusted_rand_score(labels_true,predicted_labels))
        Core_nums.append(len(clst.core_sample_indices_))


    ## 繪圖
    fig=plt.figure()
    ax=fig.add_subplot(1,2,1)
    ax.plot(min_samples,ARIs,marker='+')
    ax.set_xlabel( "min_samples")
    ax.set_ylim(0,1)
    ax.set_ylabel('ARI')


    ax=fig.add_subplot(1,2,2)
    ax.plot(min_samples,Core_nums,marker='o')
    ax.set_xlabel( "min_samples")
    ax.set_ylabel('Core_Nums')


    fig.suptitle("DBSCAN")
    plt.show()      
centers=[[1,1],[2,2],[1,2],[10,20]] # 用于産生聚類的中心點
X,labels_true=create_data(centers,1000,0.5) # 産生用于聚類的資料集      
X      
array([[0.81394428, 0.97283296],
       [2.39126942, 1.39691895],
       [0.52294212, 2.28195147],
       ...,
       [0.07392303, 0.23407894],
       [0.92014917, 1.63693107],
       [0.95976852, 1.00443603]])      
labels_true      
array([0, 1, 2, 1, 2, 2, 3, 3, 3, 3, 3, 2, 2, 1, 1, 0, 0, 0, 2, 1, 3, 1,
       0, 1, 1, 1, 1, 0, 1, 2, 3, 2, 3, 1, 0, 1, 3, 2, 3, 2, 2, 2, 2, 3,
       2, 2, 2, 0, 1, 3, 0, 2, 1, 0, 2, 2, 3, 2, 2, 1, 3, 3, 1, 2, 2, 0,
       1, 3, 1, 0, 2, 0, 0, 0, 0, 1, 0, 1, 0, 3, 0, 3, 0, 1, 2, 2, 0, 1,
       1, 3, 0, 1, 0, 3, 0, 0, 1, 3, 3, 3, 0, 1, 0, 1, 0, 1, 1, 2, 2, 2,
       1, 1, 3, 2, 2, 2, 2, 1, 2, 1, 1, 0, 2, 0, 1, 1, 1, 1, 1, 3, 2, 0,
       0, 1, 3, 1, 0, 3, 3, 2, 3, 1, 1, 2, 1, 0, 2, 3, 0, 1, 3, 2, 2, 2,
       0, 2, 2, 0, 3, 0, 1, 0, 3, 3, 0, 0, 3, 2, 3, 1, 3, 0, 0, 1, 0, 1,
       3, 3, 3, 1, 3, 2, 3, 2, 0, 0, 2, 1, 1, 2, 3, 1, 2, 3, 0, 3, 1, 0,
       0, 1, 3, 2, 1, 1, 0, 3, 1, 2, 2, 3, 0, 0, 2, 2, 1, 3, 3, 0, 2, 0,
       0, 2, 1, 0, 3, 0, 1, 3, 2, 2, 0, 0, 3, 3, 2, 2, 1, 3, 0, 1, 3, 1,
       2, 2, 0, 3, 0, 2, 2, 2, 2, 2, 1, 2, 0, 2, 0, 0, 2, 3, 1, 3, 3, 3,
       3, 3, 3, 0, 1, 3, 2, 0, 1, 2, 2, 3, 0, 1, 1, 0, 3, 0, 3, 1, 2, 2,
       3, 0, 0, 3, 1, 0, 3, 2, 2, 0, 3, 2, 1, 0, 3, 3, 2, 1, 0, 1, 3, 3,
       0, 1, 2, 0, 3, 3, 0, 2, 0, 0, 0, 2, 3, 0, 2, 3, 1, 2, 1, 1, 3, 3,
       1, 2, 2, 0, 0, 2, 0, 3, 1, 3, 1, 2, 2, 1, 2, 0, 0, 1, 2, 1, 1, 1,
       0, 0, 1, 3, 2, 3, 3, 0, 3, 1, 1, 1, 0, 3, 1, 1, 3, 1, 3, 0, 3, 3,
       1, 0, 2, 0, 0, 2, 2, 1, 2, 3, 2, 2, 0, 2, 2, 1, 3, 1, 1, 2, 3, 1,
       3, 2, 3, 2, 1, 3, 3, 0, 1, 1, 3, 2, 1, 1, 2, 3, 1, 3, 0, 3, 0, 2,
       3, 0, 0, 2, 3, 2, 3, 0, 0, 3, 3, 3, 0, 0, 2, 3, 2, 3, 3, 0, 2, 3,
       2, 3, 0, 1, 1, 1, 3, 0, 3, 0, 1, 0, 3, 0, 3, 1, 2, 2, 2, 1, 2, 0,
       3, 0, 0, 1, 0, 0, 3, 0, 2, 3, 3, 1, 0, 2, 1, 0, 2, 3, 0, 1, 2, 1,
       3, 0, 1, 2, 2, 3, 2, 1, 0, 1, 2, 3, 2, 3, 1, 2, 3, 0, 1, 2, 2, 2,
       3, 0, 3, 1, 1, 1, 2, 1, 1, 0, 0, 1, 1, 0, 3, 3, 0, 1, 1, 3, 1, 3,
       2, 2, 0, 3, 1, 0, 2, 1, 0, 2, 3, 1, 0, 1, 2, 3, 2, 2, 0, 0, 0, 1,
       0, 0, 0, 3, 3, 2, 0, 0, 2, 2, 1, 2, 1, 0, 0, 1, 0, 2, 3, 1, 1, 3,
       3, 1, 3, 3, 3, 1, 2, 0, 2, 1, 3, 3, 1, 1, 1, 0, 1, 3, 3, 3, 1, 1,
       2, 1, 2, 1, 0, 3, 1, 0, 1, 1, 2, 3, 1, 0, 1, 0, 1, 0, 3, 0, 0, 3,
       3, 0, 1, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 0, 3, 2, 0, 0, 2, 2, 0,
       1, 0, 2, 1, 0, 2, 2, 0, 0, 1, 0, 1, 0, 3, 3, 3, 1, 2, 3, 1, 0, 1,
       3, 3, 0, 1, 3, 2, 3, 3, 0, 0, 0, 3, 2, 1, 0, 0, 2, 3, 0, 0, 2, 0,
       3, 1, 3, 3, 3, 2, 1, 2, 3, 0, 1, 3, 3, 2, 3, 2, 2, 2, 1, 3, 0, 2,
       3, 2, 1, 2, 3, 3, 1, 1, 0, 0, 1, 2, 1, 1, 2, 3, 2, 0, 1, 0, 0, 3,
       2, 3, 1, 1, 2, 3, 1, 1, 0, 3, 2, 0, 1, 3, 2, 2, 0, 0, 1, 0, 3, 2,
       1, 0, 1, 3, 0, 3, 3, 3, 1, 2, 2, 0, 2, 2, 2, 0, 0, 0, 3, 2, 0, 0,
       1, 0, 1, 1, 2, 1, 0, 3, 0, 0, 3, 3, 3, 3, 2, 1, 0, 1, 2, 3, 1, 0,
       0, 0, 1, 1, 1, 3, 1, 0, 3, 2, 2, 2, 2, 2, 1, 2, 3, 1, 3, 0, 1, 2,
       3, 1, 0, 0, 0, 1, 2, 3, 2, 2, 3, 3, 2, 1, 0, 2, 2, 2, 2, 3, 1, 0,
       1, 2, 0, 0, 2, 1, 2, 0, 0, 2, 1, 0, 0, 2, 3, 0, 1, 2, 0, 3, 1, 2,
       1, 0, 3, 2, 0, 1, 0, 0, 3, 1, 2, 0, 0, 2, 2, 1, 1, 2, 3, 2, 3, 0,
       0, 2, 3, 2, 1, 0, 0, 3, 0, 0, 2, 3, 3, 3, 1, 3, 0, 2, 2, 3, 2, 1,
       0, 3, 3, 0, 1, 3, 3, 2, 2, 2, 3, 3, 2, 3, 2, 1, 3, 3, 3, 2, 0, 1,
       2, 1, 3, 1, 1, 1, 0, 1, 1, 0, 2, 1, 2, 2, 1, 3, 0, 2, 0, 1, 2, 1,
       1, 0, 3, 0, 2, 1, 1, 0, 1, 1, 0, 1, 0, 1, 2, 3, 1, 3, 3, 2, 1, 2,
       2, 1, 2, 3, 2, 2, 0, 3, 0, 1, 0, 0, 3, 1, 3, 1, 0, 2, 1, 2, 2, 2,
       3, 0, 3, 2, 1, 3, 1, 0, 2, 0])      
test_DBSCAN(X,labels_true) #  調用 test_DBSCAN 函數      
ARI:0.3326689255565291
Core sample num:990      
test_DBSCAN_epsilon(X,labels_true) #  調用 test_DBSCAN_epsilon 函數      
經典算法筆記:無監督算法(聚類、降維)
test_DBSCAN_min_samples(X,labels_true) #  調用 test_DBSCAN_min_samples 函數      
經典算法筆記:無監督算法(聚類、降維)
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
%matplotlib inline
X1, y1=datasets.make_circles(n_samples=5000, factor=.6,
                                      noise=.05)
X2, y2 = datasets.make_blobs(n_samples=1000, n_features=2, centers=[[1.2,1.2]], cluster_std=[[.1]],
               random_state=9)


X = np.concatenate((X1, X2))
plt.scatter(X[:, 0], X[:, 1], marker='o')
plt.show()      
經典算法筆記:無監督算法(聚類、降維)
from sklearn.cluster import KMeans
y_pred = KMeans(n_clusters=3, random_state=9).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()      
經典算法筆記:無監督算法(聚類、降維)
from sklearn.cluster import DBSCAN
y_pred = DBSCAN().fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()      
經典算法筆記:無監督算法(聚類、降維)
y_pred = DBSCAN(eps = 0.1).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()      
經典算法筆記:無監督算法(聚類、降維)
y_pred = DBSCAN(eps = 0.1, min_samples = 4).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()      
經典算法筆記:無監督算法(聚類、降維)

Principal component analysis(主成分分析)

PCA 是在資料集中找到“主成分”或最大方差方向的線性變換。 它可以用于降維。 在本練習中,我們首先負責實作 PCA 并将其應用于一個簡單的二維資料集,以了解它是如何工作的。 我們從加載和可視化資料集開始。

data = loadmat('data/ex7data1.mat')      
X = data['X']


fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(X[:, 0], X[:, 1])
plt.show()      
經典算法筆記:無監督算法(聚類、降維)

PCA 的算法相當簡單。 在確定資料被歸一化之後,輸出僅僅是原始資料的協方差矩陣的奇異值分解。

def pca(X):
    # normalize the features
    X = (X - X.mean()) / X.std()


    # compute the covariance matrix
    X = np.matrix(X)
    cov = (X.T * X) / X.shape[0]


    # perform SVD
    U, S, V = np.linalg.svd(cov)


    return U, S, V      
U, S, V = pca(X)
U, S, V      
(matrix([[-0.79241747, -0.60997914],
         [-0.60997914,  0.79241747]]),
 array([1.43584536, 0.56415464]),
 matrix([[-0.79241747, -0.60997914],
         [-0.60997914,  0.79241747]]))      
print(X.shape)
print(U.shape)
print(S.shape)
print(V.shape)      
(50, 2)
(2, 2)
(2,)
(2, 2)      

現在我們有主成分(矩陣 U),我們可以用這些來将原始資料投影到一個較低維的空間中。 對于這個任務,我們将實作一個計算投影并且僅選擇頂部 K 個分量的函數,有效地減少了維數。

def project_data(X, U, k):
    U_reduced = U[:,:k]
    return np.dot(X, U_reduced)      
Z = project_data(X, U, 1)      

我們也可以通過反向轉換步驟來恢複原始資料。

def recover_data(Z, U, k):
    U_reduced = U[:,:k]
    return np.dot(Z, U_reduced.T)      
X_recovered = recover_data(Z, U, 1)      
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(list(X_recovered[:, 0]), list(X_recovered[:, 1]))
plt.show()      
經典算法筆記:無監督算法(聚類、降維)

請注意,第一主成分的投影軸基本上是資料集中的對角線。 當我們将資料減少到一個次元時,我們失去了該對角線周圍的變化,是以在我們的再現中,一切都沿着該對角線。

參考資料

[1] 機器學習課程: https://www.coursera.org/course/ml

[2] 黃海廣: https://github.com/fengdu78

[3] github: https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes

[4] 作業代碼: https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes/blob/master/codeex7-kmeans%20and%20PCA/ML-Exercise7.ipynb

[5] markdown 檔案: https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes/blob/master/markdown/week8.md

[6] pdf 檔案: https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes/blob/master/機器學習個人筆記完整版v5.4-A4列印版.pdf

經典算法筆記:無監督算法(聚類、降維)

關于本站