1. 程式人生 > >聚類之高斯混合模型(Gaussian Mixture Model)【轉】

聚類之高斯混合模型(Gaussian Mixture Model)【轉】

k-means應該是原來級別的聚類方法了,這整理下一個使用後驗概率準確評測其精度的方法—高斯混合模型。

我們談到了用 k-means 進行聚類的方法,這次我們來說一下另一個很流行的演算法:Gaussian Mixture Model (GMM)。事實上,GMM 和 k-means 很像,不過 GMM 是學習出一些概率密度函式來(所以 GMM 除了用在 clustering 上之外,還經常被用於 density estimation ),簡單地說,k-means 的結果是每個資料點被 assign 到其中某一個 cluster 了,而 GMM 則給出這些資料點被 assign 到每個 cluster 的概率,又稱作 soft assignment 。

得出一個概率有很多好處,因為它的資訊量比簡單的一個結果要多,比如,我可以把這個概率轉換為一個 score ,表示演算法對自己得出的這個結果的把握。也許我可以對同一個任務,用多個方法得到結果,最後選取“把握”最大的那個結果;另一個很常見的方法是在諸如疾病診斷之類的場所,機器對於那些很容易分辨的情況(患病或者不患病的概率很高)可以自動區分,而對於那種很難分辨的情況,比如,49% 的概率患病,51% 的概率正常,如果僅僅簡單地使用 50% 的閾值將患者診斷為“正常”的話,風險是非常大的,因此,在機器對自己的結果把握很小的情況下,會“拒絕發表評論”,而把這個任務留給有經驗的醫生去解決。

廢話說了一堆,不過,在回到 GMM 之前,我們再稍微扯幾句。我們知道,不管是機器還是人,學習的過程都可以看作是一種“歸納”的過程,在歸納的時候你需要有一些假設的前提條件,例如,當你被告知水裡遊的那個傢伙是魚之後,你使用“在同樣的地方生活的是同一種東西”這類似的假設,歸納出“在水裡遊的都是魚”這樣一個結論。當然這個過程是完全“本能”的,如果不仔細去想,你也不會了解自己是怎樣“認識魚”的。另一個值得注意的地方是這樣的假設並不總是完全正確的,甚至可以說總是會有這樣那樣的缺陷的,因此你有可能會把蝦、龜、甚至是潛水員當做魚。也許你覺得可以通過修改前提假設來解決這個問題,例如,基於“生活在同樣的地方並且穿著同樣衣服的是同一種東西”這個假設,你得出結論:在水裡有並且身上長有鱗片的是魚。可是這樣還是有問題,因為有些沒有長鱗片的魚現在又被你排除在外了。

在這個問題上,機器學習面臨著和人一樣的問題,在機器學習中,一個學習演算法也會有一個前提假設,這裡被稱作“歸納偏執 (bias)”(bias 這個英文詞在機器學習和統計裡還有其他許多的意思)。例如線性迴歸,目的是要找一個函式儘可能好地擬合給定的資料點,它的歸納偏執就是“滿足要求的函式必須是線性函式”。一個沒有歸納偏執的學習演算法從某種意義上來說毫無用處,就像一個完全沒有歸納能力的人一樣,在第一次看到魚的時候有人告訴他那是魚,下次看到另一條魚了,他並不知道那也是魚,因為兩條魚總有一些地方不一樣的,或者就算是同一條魚,在河裡不同的地方看到,或者只是看到的時間不一樣,也會被他認為是不同的,因為他無法歸納,無法提取主要矛盾、忽略次要因素,只好要求所有的條件都完全一樣──然而哲學家已經告訴過我們了:世界上不會有任何樣東西是完全一樣的,所以這個人即使是有無比強悍的記憶力,也絕學不到任何一點知識。

這個問題在機器學習中稱作“過擬合 (Overfitting)”,例如前面的迴歸的問題,如果去掉“線性函式”這個歸納偏執,因為對於 N 個點,我們總是可以構造一個 N-1 次多項式函式,讓它完美地穿過所有的這 N 個點,或者如果我用任何大於 N-1 次的多項式函式的話,我甚至可以構造出無窮多個滿足條件的函數出來。如果假定特定領域裡的問題所給定的資料個數總是有個上限的話,我可以取一個足夠大的 N ,從而得到一個(或者無窮多個)“超級函式”,能夠 fit 這個領域內所有的問題。然而這個(或者這無窮多個)“超級函式”有用嗎?只要我們注意到學習的目的(通常)不是解釋現有的事物,而是從中歸納出知識,並能應用到新的事物上,結果就顯而易見了。

沒有歸納偏執或者歸納偏執太寬泛會導致 Overfitting ,然而另一個極端──限制過大的歸納偏執也是有問題的:如果資料本身並不是線性的,強行用線性函式去做迴歸通常並不能得到好結果。難點正在於在這之間尋找一個平衡點。不過人在這裡相對於(現在的)機器來說有一個很大的優勢:人通常不會孤立地用某一個獨立的系統和模型去處理問題,一個人每天都會從各個來源獲取大量的資訊,並且通過各種手段進行整合處理,歸納所得的所有知識最終得以統一地儲存起來,並能有機地組合起來去解決特定的問題。這裡的“有機”這個詞很有意思,搞理論的人總能提出各種各樣的模型,並且這些模型都有嚴格的理論基礎保證能達到期望的目的,然而絕大多數模型都會有那麼一些“引數”(例如 K-means 中的 k ),通常沒有理論來說明引數取哪個值更好,而模型實際的效果卻通常和引數是否取到最優值有很大的關係,我覺得,在這裡“有機”不妨看作是所有模型的引數已經自動地取到了最優值。另外,雖然進展不大,但是人們也一直都期望在計算機領域也建立起一個統一的知識系統(例如語意網就是這樣一個嘗試)。

廢話終於說完了,回到 GMM 。按照我們前面的討論,作為一個流行的演算法,GMM 肯定有它自己的一個相當體面的歸納偏執了。其實它的假設非常簡單,顧名思義,Gaussian Mixture Model ,就是假設資料服從 Mixture Gaussian Distribution ,換句話說,資料可以看作是從數個 Gaussian Distribution 中生成出來的。實際上,我們在 K-means 和 K-medoids 兩篇文章中用到的那個例子就是由三個 Gaussian 分佈從隨機選取出來的。實際上,從中心極限定理可以看出,Gaussian 分佈(也叫做正態 (Normal) 分佈)這個假設其實是比較合理的,除此之外,Gaussian 分佈在計算上也有一些很好的性質,所以,雖然我們可以用不同的分佈來隨意地構造 XX Mixture Model ,但是還是 GMM 最為流行。另外,Mixture Model 本身其實也是可以變得任意複雜的,通過增加 Model 的個數,我們可以任意地逼近任何連續的概率密分佈。

每個 GMM 由 K 個 Gaussian 分佈組成,每個 Gaussian 稱為一個“Component”,這些 Component 線性加成在一起就組成了 GMM 的概率密度函式: 

這裡寫圖片描述


根據上面的式子,如果我們要從 GMM 的分佈中隨機地取一個點的話,實際上可以分為兩步:首先隨機地在這 K 個 Component 之中選一個,每個 Component 被選中的概率實際上就是它的係數πkπk,選中了 Component 之後,再單獨地考慮從這個 Component 的分佈中選取一個點就可以了──這裡已經回到了普通的 Gaussian 分佈,轉化為了已知的問題。

 

那麼如何用 GMM 來做 clustering 呢?其實很簡單,現在我們有了資料,假定它們是由 GMM 生成出來的,那麼我們只要根據資料推出 GMM 的概率分佈來就可以了,然後 GMM 的 K 個 Component 實際上就對應了 K 個 cluster 了。根據資料來推算概率密度通常被稱作 density estimation ,特別地,當我們在已知(或假定)了概率密度函式的形式,而要估計其中的引數的過程被稱作“引數估計”。

現在假設我們有 N 個數據點,並假設它們服從某個分佈(記作 p(x) ),現在要確定裡面的一些引數的值,例如,在 GMM 中,我們就需要確定πkπk、μkμk 和 ΣkΣk 這些引數。 我們的想法是,找到這樣一組引數,它所確定的概率分佈生成這些給定的資料點的概率最大,而這個概率實際上就等於Ni=1p(xi)∏i=1Np(xi),我們把這個乘積稱作似然函式 (Likelihood Function)。通常單個點的概率都很小,許多很小的數字相乘起來在計算機裡很容易造成浮點數下溢,因此我們通常會對其取對數,把乘積變成加和Ni=1logp(xi)∑i=1Nlog⁡p(xi),得到 log-likelihood function 。接下來我們只要將這個函式最大化(通常的做法是求導並令導數等於零,然後解方程),亦即找到這樣一組引數值,它讓似然函式取得最大值,我們就認為這是最合適的引數,這樣就完成了引數估計的過程。

下面讓我們來看一看 GMM 的 log-likelihood function :

 

  i=1Nlog{k=1KπkN(xi|μk,Σk)}∑i=1Nlog⁡{∑k=1KπkN(xi|μk,Σk)}


由於在對數函式裡面又有加和,我們沒法直接用求導解方程的辦法直接求得最大值。為了解決這個問題,我們採取之前從 GMM 中隨機選點的辦法:分成兩步,實際上也就類似於 K-means 的兩步。 

 

  • 估計資料由每個 Component 生成的概率(並不是每個 Component 被選中的概率):對於每個資料 x_i 來說,它由第 k 個 Component 生成的概率為 
      γ(i,k)=πkN(xi|μk,Σk)Kj=1πjN(xi|μj,Σj)γ(i,k)=πkN(xi|μk,Σk)∑j=1KπjN(xi|μj,Σj)
    由於式子裡的μkΣkμk和Σk也是需要我們估計的值,我們採用迭代法,在計算 γ(i,k)γ(i,k)的時候我們假定μkΣkμk和Σk均已知,我們將取上一次迭代所得的值(或者初始值)。
  • 估計每個 Component 的引數:現在我們假設上一步中得到的γ(i,k)γ(i,k)就是正確的“資料xixi 由 Component k 生成的概率”,亦可以當做該 Component 在生成這個資料上所做的貢獻,或者說,我們可以看作xixi 這個值其中有 γ(i,k)xiγ(i,k)xi這部分是由 Component k 所生成的。集中考慮所有的資料點,現在實際上可以看作 Component 生成了 γ(1,k)x1,,γ(N,k)xNγ(1,k)x1,…,γ(N,k)xN這些點。由於每個 Component 都是一個標準的 Gaussian 分佈,可以很容易分佈求出最大似然所對應的引數值: 
      μkΣk=1Nki=1Nγ(i,k)xi=1Nki=1Nγ(i,k)(xiμk)(xiμk)Tμk=1Nk∑i=1Nγ(i,k)xiΣk=1Nk∑i=1Nγ(i,k)(xi−μk)(xi−μk)T
    其中Nk=Ni=1γ(i,k)Nk=∑i=1Nγ(i,k) ,並且 πkπk也順理成章地可以估計為Nk/NNk/N 。

 

重複迭代前面兩步,直到似然函式的值收斂為止。 
當然,上面給出的只是比較“直觀”的解釋,想看嚴格的推到過程的話,可以參考 Pattern Recognition and Machine Learning 這本書的第九章。有了實際的步驟,再實現起來就很簡單了。Matlab 程式碼如下:

(Update 2012.07.03:如果你直接把下面的程式碼拿去運行了,碰到 covariance 矩陣 singular 的情況,可以參見這篇文章。)

function varargout = gmm(X, K_or_centroids) % ============================================================ % Expectation-Maximization iteration implementation of % Gaussian Mixture Model. % % PX = GMM(X, K_OR_CENTROIDS) % [PX MODEL] = GMM(X, K_OR_CENTROIDS) % % - X: N-by-D data matrix. % - K_OR_CENTROIDS: either K indicating the number of % components or a K-by-D matrix indicating the % choosing of the initial K centroids. % % - PX: N-by-K matrix indicating the probability of each % component generating each point. % - MODEL: a structure containing the parameters for a GMM: % MODEL.Miu: a K-by-D matrix. % MODEL.Sigma: a D-by-D-by-K matrix. % MODEL.Pi: a 1-by-K vector. % ============================================================ threshold = 1e-15; [N, D] = size(X); if isscalar(K_or_centroids) K = K_or_centroids; % randomly pick centroids rndp = randperm(N); centroids = X(rndp(1:K), :); else K = size(K_or_centroids, 1); centroids = K_or_centroids; end % initial values [pMiu pPi pSigma] = init_params(); Lprev = -inf; while true Px = calc_prob(); % new value for pGamma pGamma = Px .* repmat(pPi, N, 1); pGamma = pGamma ./ repmat(sum(pGamma, 2), 1, K); % new value for parameters of each Component Nk = sum(pGamma, 1); pMiu = diag(1./Nk) * pGamma' * X; pPi = Nk/N; for kk = 1:K Xshift = X-repmat(pMiu(kk, :), N, 1); pSigma(:, :, kk) = (Xshift' * ... (diag(pGamma(:, kk)) * Xshift)) / Nk(kk); end % check for convergence L = sum(log(Px*pPi')); if L-Lprev < threshold break; end Lprev = L; end if nargout == 1 varargout = {Px}; else model = []; model.Miu = pMiu; model.Sigma = pSigma; model.Pi = pPi; varargout = {Px, model}; end function [pMiu pPi pSigma] = init_params() pMiu = centroids; pPi = zeros(1, K); pSigma = zeros(D, D, K); % hard assign x to each centroids distmat = repmat(sum(X.*X, 2), 1, K) + ... repmat(sum(pMiu.*pMiu, 2)', N, 1) - ... 2*X*pMiu'; [dummy labels] = min(distmat, [], 2); for k=1:K Xk = X(labels == k, :); pPi(k) = size(Xk, 1)/N; pSigma(:, :, k) = cov(Xk); end end function Px = calc_prob() Px = zeros(N, K); for k = 1:K Xshift = X-repmat(pMiu(k, :), N, 1); inv_pSigma = inv(pSigma(:, :, k)); tmp = sum((Xshift*inv_pSigma) .* Xshift, 2); coef = (2*pi)^(-D/2) * sqrt(det(inv_pSigma)); Px(:, k) = coef * exp(-0.5*tmp); end end end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102

函式返回的 Px 是一個 N×KN×K 的矩陣,對於每一個 xixi ,我們只要取該矩陣第 i 行中最大的那個概率值所對應的那個 Component 為xixi所屬的 cluster 就可以實現一個完整的聚類方法了。對於最開始的那個例子,GMM 給出的結果如下: 

這裡寫圖片描述

 

相對於之前 K-means 給出的結果,這裡的結果更好一些,左下角的比較稀疏的那個 cluster 有一些點跑得比較遠了。當然,因為這個問題原本就是完全有 Mixture Gaussian Distribution 生成的資料,GMM (如果能求得全域性最優解的話)顯然是可以對這個問題做到的最好的建模。

另外,從上面的分析中我們可以看到 GMM 和 K-means 的迭代求解法其實非常相似(都可以追溯到 EM 演算法,下一次會詳細介紹),因此也有和 K-means 同樣的問題──並不能保證總是能取到全域性最優,如果運氣比較差,取到不好的初始值,就有可能得到很差的結果。對於 K-means 的情況,我們通常是重複一定次數然後取最好的結果,不過 GMM 每一次迭代的計算量比 K-means 要大許多,一個更流行的做法是先用 K-means (已經重複並取最優值了)得到一個粗略的結果,然後將其作為初值(只要將 K-means 所得的 centroids 傳入 gmm 函式即可),再用 GMM 進行細緻迭代。

如我們最開始所討論的,GMM 所得的結果(Px)不僅僅是資料點的 label ,而包含了資料點標記為每個 label 的概率,很多時候這實際上是非常有用的資訊。最後,需要指出的是,GMM 本身只是一個模型,我們這裡給出的迭代的辦法並不是唯一的求解方法。感興趣的同學可以自行查詢相關資料。

 

來源:https://blog.csdn.net/u014665013/article/details/78970184