機器學習(十四)-GMM混合高斯模型(Gaussian mixture model)算法及Python實例

原創不易,轉載前請註明博主的鏈接地址:Blessy_Zhu https://blog.csdn.net/weixin_42555080
本次代碼的環境:
運行平臺: Windows
Python版本: Python3.x
IDE: PyCharm
 

 
在這裏插入圖片描述

一 什麼是Gaussian mixture model

高斯混合模型,英文全稱:​​Gaussian mixture model,簡稱GMM。高斯混合模型就是用高斯概率密度函數(二維時也稱爲:正態分佈曲線)精確的量化事物,將一個事物分解爲若干基於高斯概率密度函數行程的模型。這句話看起來有些深奧,這樣去理解,事物的數學表現形式就是曲線,其意思就是任何一個曲線,無論多麼複雜,我們都可以用若干個高斯曲線來無限逼近它,這就是高斯混合模型的基本思想。那麼下圖表示的就是這樣的一個思想。
111
對於上圖,換一種方式理解,曲線是模擬一組數據的結果,而這些數據分佈情況如下圖所示。那麼此時GMM模擬出的曲線就有了現實的意義,這時就可以用構造好的GMM模型來表達這些數據,相比於存儲數據,使用GMM中的參數來表達數據要方便簡單的多,並且是數學上有完整的表達式。
在這裏插入圖片描述
反過來思考,假如先拿到的是數據分佈圖,知道了數據的分佈情況. 如何用曲線和數學表達式來逼近模擬它呢?答:用高斯混合模型來做!!!而混合高斯的曲線是由若干個單高斯函數疊加而成的。以上就是高斯混合模型的基本概念。
實際上,混合高斯就是通過求解多個單高斯模型,並通過一定的權重將多個單高斯模型融合成一個模型,即最終的混合高斯模型。一般化的描述爲:假設混合高斯模型由K個高斯模型組成(即數據包含K個類),則GMM的概率密度函數如下
在這裏插入圖片描述
其中,
在這裏插入圖片描述
是第k個高斯模型的概率密度函數,可以看成選定第k個模型後,該模型產生x的概率;p(k)=πk 是第k個高斯模型的權重,稱作選擇第k個模型的先驗概率,且滿足
在這裏插入圖片描述
實際上,GMM的目的就是找到一個合適的高斯分佈(也就是確定高斯分佈的參數μ,Σ),使得這個高斯分佈能產生這組樣本的可能性儘可能大(即:擬合樣本數據)。高斯混合模型也​被視爲一種聚類方法,是機器學習中對“無標籤數據”進行訓練得到的分類結果。其分類結果由概率表示,概率大者,則認爲屬於這一類。

二 Gaussian mixture model內容介紹

怎麼去實現Gaussian mixture model呢?最開始的想法肯定是Log-Likelihood Function,因爲這個方法在求最優化問題時是常用的手段。那最大化對數似然函數(log-likelihood function)的意義是什麼呢?

2.1 最大化對數似然函數(log-likelihood function)的意義

首先直觀化地解釋一下最大化對數似然函數要解決的是什麼問題。
假設我們採樣得到一組樣本yt ,而且知道變量Y服從高斯分佈(本文只提及高斯分佈,其他變量分佈模型類似),數學形式表示爲Y∼N(μ,Σ)。採樣的樣本如下圖所示,我們的目的就是找到一個合適的高斯分佈(也就是確定高斯分佈的參數μ,Σ),使得這個高斯分佈能產生這組樣本的可能性儘可能大。
那怎麼找到這個合適的高斯分佈呢(在如下圖中的表示就是1~4哪個分佈較爲合適)?這時候似然函數就閃亮登場了。
 

 
在這裏插入圖片描述

似然函數數學化:設有樣本集Y=y1,y2…yN ,p(yny_n∣μ,Σ)是高斯分佈的概率分佈函數,表示變量Y=yny_n的概率。假設樣本的抽樣是獨立的,那麼我們同時抽到這N個樣本的概率是抽到每個樣本概率的乘積,也就是樣本集Y的聯合概率。此聯合概率即爲似然函數:
在這裏插入圖片描述
對式子(2)進行求導並令導數爲0(即最大化似然函數,一般還會先轉化爲對數似然函數再最大化),所求出的參數就是最佳的高斯分佈對應的參數。
  所以最大化似然函數的意義就是:通過使得樣本集的聯合概率最大來對參數進行估計,從而選擇最佳的分佈模型。
  對於上圖產生的樣本用最大化似然函數的方法,最終可以得到序號1對應的高斯分佈模型是最佳的模型。

2.2 嘗試用最大化對數似然函數(log-likelihood function)解決GMM

既然已經介紹了最大化對數似然函數(log-likelihood function),那麼接着嘗試這用log-likelihood function來解決GMM。看看是否行得通。
解GMM模型,實際上就是確定GMM模型的參數(μ,Σ,π) ,使得由這組參數確定的GMM模型最有可能產生採樣的樣本。要利用極大似然估計求解模型最重要的一步就是求出似然函數,即樣本集出現的聯合概率。而對於混合高斯模型,如何求解某個樣本yty_t的概率?顯然我們得先知道這個樣本來源於哪一類高斯模型,然後求這個高斯模型生成這個樣本的概率p(yty_t) ,這時就要引入隱變量了先引入一個隱變量γ\gamma。它是一個K維二值隨機變量,在它的K維取值中只有某個特定的元素γt\gamma_t 的取值爲1,其它元素的取值爲0。實際上,隱變量描述的就是:每一次採樣,選擇第k個高斯模型的概率,故有:
在這裏插入圖片描述
直接給出對數似然函數表示:在這裏插入圖片描述
然後求導,令導數爲0,就可以得到模型參數(μ,Σ,π) 。
  然而仔細觀察可以發現,對數似然函數裏面,對數裏面還有求和。實際上沒有辦法通過求導的方法來求這個對數似然函數的最大值。此時,就需要藉助EM算法來解決此問題了

2.3 極大似然估計與EM算法適用問題分析

在這裏插入圖片描述
在這裏插入圖片描述
如果我們已經清楚了某個變量服從的高斯分佈,而且通過採樣得到了這個變量的樣本數據,想求高斯分佈的參數,這時候極大似然估計可以勝任這個任務;而如果我們要求解的是一個混合模型,只知道混合模型中各個類的分佈模型(譬如都是高斯分佈)和對應的採樣數據,而不知道這些採樣數據分別來源於哪一類(隱變量),那這時候就可以借鑑EM算法。EM算法可以用於解決數據缺失的參數估計問題(隱變量的存在實際上就是數據缺失問題,缺失了各個樣本來源於哪一類的記錄)。
下面將介紹EM算法的兩個步驟:E-step(expectation-step,期望步)和M-step(Maximization-step,最大化步);

2.4 EM(xceptation-Maximization Algorithm)解決GMM

EM算法可以用於解決數據缺失的參數估計問題(隱變量的存在實際上就是數據缺失問題,缺失了各個樣本來源於哪一類的記錄)。

2.4.1 定義隱變量

引入隱變量yjky_jk,它的取值只能是1或者0。

  • 取值爲1:第j個觀測變量來自第k個高斯分量
  • 取值爲0:第j個觀測變量不是來自第k個高斯分量

那麼對於每一個觀測數據yjy_j都會對應於一個向量變量,ΓjΓ_j={γj1γ_j1,…,γjKγ_jK},那麼有:
在這裏插入圖片描述
其中,K爲GMM高斯分量的個數,αkα_k爲第k個高斯分量的權值。因爲觀測數據來自GMM的各個高斯分量相互獨立,而αkα_k剛好可以看做是觀測數據來自第k個高斯分量的概率,因此可以直接通過連乘得到整個隱變量ΓjΓ_j的先驗分佈概率。

2.4.2 得到完全數據的似然函數

對於觀測數據yjy_j,當已知其是哪個高斯分量生成的之後,其服從的概率分佈爲:
在這裏插入圖片描述
由於觀測數據從哪個高斯分量生成這個事件之間的相互獨立的,因此可以寫爲:
在這裏插入圖片描述
這樣我們就得到了已知ΓjΓ_j的情況下單個觀測數據的後驗概率分佈。結合之前得到的ΓjΓ_j的先驗分佈,則我們可以寫出單個完全觀測數據的似然函數爲:
在這裏插入圖片描述
取對數,得到對數似然函數爲:
在這裏插入圖片描述

2.4.3 得到各個高斯分量的參數計算公式

首先,我們將上式中的lnN(yjy_j|μkμ_k,ΣkΣ_k)根據單高斯的向量形式的概率密度函數的表達形式展開:
在這裏插入圖片描述
假設我們已經知道隱變量γjkγ_jk的取值,對上面得到的似然函數分別對αkα_kΣkΣ_k求偏導並且偏導結果爲零,可以得到:
在這裏插入圖片描述
現在參數空間中剩下一個αkα_k還沒有求。這是一個約束滿足問題,因爲必須滿足約束Σk=1KΣ_k=1^Kαkα_k=1。我們使用拉格朗日乘子法結合似然函數和約束條件對αkα_k求偏導,可以得到:
在這裏插入圖片描述
將上式的左右兩邊分別對k=1…K求和,可以得到:

在這裏插入圖片描述
將λ代入,最終得到:
在這裏插入圖片描述
至此,我們在隱變量已知的情況下得到了GMM的三種類型參數的求解公式。

2.4.4得到隱變量的估計公式

根據EM算法,現在我們需要通過當前參數的取值得到隱變量的估計公式也就是說隱變量的期望的表達形式。即如何求解E{γjkγ_jk|y,Θ}。
在這裏插入圖片描述

三 Gaussian mixture model的Python實現

在聚類中,k-means聚類模型簡單易懂,但其簡單性導致其應用中存在實際挑戰。具體而言,k-means的非概率特性及簡單地計算點與類蔟中心的歐式距離來判定歸屬,會導致其在許多真實的場景中性能較差。接下來探討高斯混合模型(GMMs),其可以看成k-means的延伸,更可以看成一個強有力的估計工具,而不僅僅是聚類。

3.1 GMM和K-means直觀對比

首先看一下兩者的過程:
GMM:

  • 先計算所有數據對每個分模型的響應度
  • 根據響應度計算每個分模型的參數
  • 迭代

K-means:

  • 先計算所有數據對於K個點的距離,取距離最近的點作爲自己所屬於的類
  • 根據上一步的類別劃分更新點的位置(點的位置就可以看做是模型參數)
  • 迭代

可以看出GMM和K-means還是有很大的相同點的。GMM中數據對高斯分量的響應度就相當於K-means中的距離計算,GMM中的根據響應度計算高斯分量參數就相當於K-means中計算分類點的位置。然後它們都通過不斷迭代達到最優。不同的是:GMM模型給出的是每一個觀測點由哪個高斯分量生成的概率,而K-means直接給出一個觀測點屬於哪一類。

3.2 GMM的Python實現

3.2.1 GMM的動機:K-means的弱點

我們看下k-means的缺陷,思考下如何提高聚類模型。k-means的作用就是,給定簡單,易於分類的數據,k-means能找到合適的聚類結果。
舉例而言,假設有些簡單的數據點,k-means算法能以某種方式很快地將它們聚類,跟我們肉眼分辨的結果很接近:

import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
import numpy as np
# Generate some data
from sklearn.datasets.samples_generator import make_blobs
X, y_true = make_blobs(n_samples=400, centers=4,
                       cluster_std=0.60, random_state=0)
X = X[:, ::-1] # flip axes for better plotting
# Plot the data with K Means Labels
from sklearn.cluster import KMeans
kmeans = KMeans(4, random_state=0)
labels = kmeans.fit(X).predict(X)
plt.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap='viridis')
plt.show()

結果:
在這裏插入圖片描述

從直觀的角度來看,期望聚類分配時,某些點比其他的更確定:舉例而言,中間兩個聚類之間似乎存在非常輕微的重疊,這樣我們可能對這些數據點的分配沒有完全的信心。不幸的是,k-means模型沒有聚類分配的概率或不確定性的內在度量(儘管可能使用bootstrap 的方式來估計這種不確定性)。爲此,我們必須考慮泛化這種模型。
k-means模型的一種理解思路是,它在每個類蔟的中心放置了一個圈(或者,更高維度超球面),其半徑由聚類中最遠的點確定。該半徑充當訓練集中聚類分配的一個硬截斷:任何圈外的數據點不被視爲該類的成員。我們可以使用以下函數可視化這個聚類模型:

from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
# Generate some data
from sklearn.datasets.samples_generator import make_blobs
X, y_true = make_blobs(n_samples=400, centers=4,
                       cluster_std=0.60, random_state=0)
X = X[:, ::-1] # flip axes for better plotting

def plot_kmeans(kmeans, X, n_clusters=4, rseed=0, ax=None):
    labels = kmeans.fit_predict(X)

    # plot the input data
    ax = ax or plt.gca()
    ax.axis('equal')
    ax.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap='viridis', zorder=2)

    # plot the representation of the KMeans model
    centers = kmeans.cluster_centers_
    radii = [cdist(X[labels == i], [center]).max()
             for i, center in enumerate(centers)]
    for c, r in zip(centers, radii):
        ax.add_patch(plt.Circle(c, r, fc='#CCCCCC', lw=3, alpha=0.5, zorder=1))

kmeans = KMeans(n_clusters=4,random_state=0)
plot_kmeans(kmeans,X)
plt.show()

結果:
在這裏插入圖片描述

觀察k-means的一個重要發現,這些聚類模式必須是圓形的。k-means沒有內置的方法來計算橢圓形或橢圓形的簇。因此,舉例而言,假設我們將相同的數據點作變換,這種聚類分配方式最終變得混亂:

from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
import numpy as np
# Generate some data
from sklearn.datasets.samples_generator import make_blobs
X, y_true = make_blobs(n_samples=400, centers=4,
                       cluster_std=0.60, random_state=0)
X = X[:, ::-1] # flip axes for better plotting
rng = np.random.RandomState(13)
X_stretched = np.dot(X, rng.randn(2, 2))


def plot_kmeans(kmeans, X, n_clusters=4, rseed=0, ax=None):
    labels = kmeans.fit_predict(X)

    # plot the input data
    ax = ax or plt.gca()
    ax.axis('equal')
    ax.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap='viridis', zorder=2)

    # plot the representation of the KMeans model
    centers = kmeans.cluster_centers_
    radii = [cdist(X[labels == i], [center]).max()
             for i, center in enumerate(centers)]
    for c, r in zip(centers, radii):
        ax.add_patch(plt.Circle(c, r, fc='#CCCCCC', lw=3, alpha=0.5, zorder=1))

kmeans = KMeans(n_clusters=4,random_state=0)
plot_kmeans(kmeans,X_stretched)
plt.show()

結果:
在這裏插入圖片描述

肉眼觀察,我們分別出這些變換過的集羣是非圓的,因此圓形模式的聚類很難擬合。然而,k-means不夠靈活解釋這一點,嘗試強行將數據點分配到四個圓形類別。這將導致聚類分配的混亂,其生成的圓形重疊:具體見圖的右下角。有人會想到使用PCA(In Depth: Principal Component Analysis)預處理數據來解決這種特殊情況,但實際上並不能保證這種處理方式能使數據分佈成圓簇狀。
k-means的兩大缺陷 – 聚類形狀的不夠靈活和缺少聚類分配的概率值
意味着許多數據集(尤其是低維數據集),可能不會有預期的效果。
你也許想到通過泛化k-means模型來克服這些缺點:譬如,你可以在數據點聚類分配時,利用每個點到各個類簇的中心的距離來衡量不確定性,而不是僅僅關注最接近的。你也可以想象允許聚類的邊界是橢圓而不是圓,以便更好地擬合非圓形的類簇。事實證明,高斯混合模型有着這兩種基本的優點。

3.2.2 高斯混合模型GMM的Python應用

高斯混合模型(GMM)試圖找到一個多維高斯概率分佈的混合,以模擬任何輸入數據集。在最簡單的情況下,GMM可用於以與k-means相同的方式聚類。

from sklearn import mixture
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False

# Generate some data
from sklearn.datasets.samples_generator import make_blobs
X, y_true = make_blobs(n_samples=400, centers=4,
                       cluster_std=0.60, random_state=0)
X = X[:, ::-1] # flip axes for better plotting

gmm = mixture.GaussianMixture(n_components=4).fit(X)
labels = gmm.predict(X)
plt.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap='viridis')
plt.show()

結果:
在這裏插入圖片描述

但因爲GMM包含概率模型,因此可以找到聚類分配的概率方式 - 在Scikit-Learn中,通過調用predict_proba方法實現。它將返回一個大小爲[n_samples, n_clusters]的矩陣,用於衡量每個點屬於給定類別的概率:

from sklearn import mixture
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False

# Generate some data
from sklearn.datasets.samples_generator import make_blobs
X, y_true = make_blobs(n_samples=400, centers=4,
                       cluster_std=0.60, random_state=0)
X = X[:, ::-1] # flip axes for better plotting
probs = gmm.predict_proba(X)
print(probs[:5].round(3))

結果:
[[0.469 0. 0. 0.531]
[0. 1. 0. 0. ]
[0. 1. 0. 0. ]
[0. 0. 0. 1. ]
[0. 1. 0. 0. ]]

本質上說,高斯混合模型與k-means非常相似:它使用期望-最大化的方式,定性地執行以下操作:

  • 選擇位置和形狀的初始猜想
  • 重複直到收斂
  • E步驟:對於每個點,計算其屬於每個類別的概率權重
  • M步驟:對於每個類別,使用E步算出的權重,根據所有數據點,更新其位置,規範化和形狀
  • 結果是,每個類別不是被硬邊界的球體界定,而是平滑的高斯模型。正如在k-means的期望-最大方法一樣,這種算法有時可能會錯過全局最優解,因此在實踐中使用多個隨機初始化。

讓我們創建一個函數,通過基於GMM輸出,繪製橢圓來幫助我們可視化GMM聚類的位置和形狀:

from sklearn import mixture
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
from matplotlib.patches import Ellipse
import numpy as np

# Generate some data
from sklearn.datasets.samples_generator import make_blobs
X, y_true = make_blobs(n_samples=400, centers=4,
                       cluster_std=0.60, random_state=0)
X = X[:, ::-1] # flip axes for better plotting

def draw_ellipse(position, covariance, ax=None, **kwargs):
    """Draw an ellipse (橢圓)with a given position and covariance"""
    ax = ax or plt.gca()

    # Convert covariance to principal axes
    if covariance.shape == (2, 2):
        U, s, Vt = np.linalg.svd(covariance)
        angle = np.degrees(np.arctan2(U[1, 0], U[0, 0]))
        width, height = 2 * np.sqrt(s)
    else:
        angle = 0
        width, height = 2 * np.sqrt(covariance)

    # Draw the Ellipse
    for nsig in range(1, 4):
        ax.add_patch(Ellipse(position, nsig * width, nsig * height,
                             angle, **kwargs))

def plot_gmm(gmm, X, label=True, ax=None):
    ax = ax or plt.gca()
    labels = gmm.fit(X).predict(X)
    if label:
        ax.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap='viridis', zorder=2)
    else:
        ax.scatter(X[:, 0], X[:, 1], s=40, zorder=2)
    ax.axis('equal')

    w_factor = 0.2 / gmm.weights_.max()
    i=0
    for  pos, covar, w in zip(gmm.means_, gmm.precisions_, gmm.weights_):
        if i<=3:
            draw_ellipse(pos, covar, alpha=w * w_factor)
            i=i+1

gmm = mixture.GaussianMixture(n_components=4, random_state=42)
plot_gmm(gmm, X)
plt.show()

結果:
在這裏插入圖片描述

3.2.3 選擇協方差類型

如果看了之前擬合的細節,將covariance_type選項在每個中都設置不同。該超參數控制每個類簇的形狀的自由度;對於任意給定的問題,必須仔細設置。

  • 默認值爲covariance_type =“diag”,這意味着可以獨立設置沿每個維度的類蔟大小,並將得到的橢圓約束爲與軸對齊。
  • 一個稍微簡單和快速的模型是covariance_type =“spherical”,它約束了類簇的形狀,使得所有維度都相等。儘管它並不完全等效,其產生的聚類將具有與k均值相似的特徵。
  • 更復雜且計算量更大的模型(特別是隨着維數的增長)是使用covariance_type =“full”,這允許將每個簇建模爲具有任意方向的橢圓。

對於一個類蔟,下圖我們可以看到這三個選項的可視化表示:
在這裏插入圖片描述

四 總結

下圖梳理了高斯分佈參數估計的邏輯流程。
 

 
在這裏插入圖片描述

下圖梳理了混合高斯分佈估計的邏輯流程。
 

 在這裏插入圖片描述

相對比於高斯分佈的參數估計,混合高斯分佈的參數估計更加複雜。主要原因在於隱變量的存在。而爲什麼混合高斯分佈的參數估計需要多次迭代循環進行?是因爲EM算法中對於γ\gamma的估計利用的是初始化或者第i步迭代的參數(μ^i,Σ ^i ,π ^i ),,這對於樣本的分類劃分是有誤差的。所以它只能通過多次迭代優化尋找更佳的參數來抵消這一誤差。

參考文章

1 詳解EM算法與混合高斯模型(Gaussian mixture model, GMM)
2 高斯混合模型(GMM)及其EM算法的理解
3 高斯混合模型的終極理解
4 深度理解高斯混合模型(GMM)
5 [譯] 高斯混合模型 — python教程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章