高斯混合模型(後面本文中将使用他的縮寫 GMM)聽起來很複雜,其實他的工作原理和 KMeans 非常相似,你甚至可以認為它是 KMeans 的機率版本。 這種機率特征使 GMM 可以應用于 KMeans 無法解決的許多複雜問題。
因為KMeans的限制很多,比如: 它假設簇是球形的并且大小相同,這在大多數現實世界的場景中是無效的。并且它是硬聚類方法,這意味着每個資料點都配置設定給一個叢集,這也是不現實的。
在本文中,我們将根據上面的内容來介紹 KMeans 的一個替代方案之一,高斯混合模型。
從概念上解釋:高斯混合模型就是用高斯機率密度函數(正态分布曲線)精确地量化事物,它是一個将事物分解為若幹的基于高斯機率密度函數(正态分布曲線)形成的模型。
高斯混合模型 (GMM) 算法的工作原理
正如前面提到的,可以将 GMM 稱為 機率的KMeans,這是因為 KMeans 和 GMM 的起點和訓練過程是相同的。 但是,KMeans 使用基于距離的方法,而 GMM 使用機率方法。 GMM 中有一個主要假設:資料集由多個高斯分布組成,換句話說,GMM 模型可以看作是由 K 個單高斯模型組合而成的模型,這 K 個子模型是混合模型的隐變量(Hidden variable)
上述分布通常稱為多模型分布。 每個峰代表我們資料集中不同的高斯分布或聚類。 我們肉眼可以看到這些分布,但是使用公式如何估計這些分布呢?
在解釋這個問題之前,我們先建立一些高斯分布。這裡我們生成的是多元正态分布; 它是單變量正态分布的更高維擴充。
讓我們定義資料點的均值和協方差。 使用均值和協方差,我們可以生成如下分布。
# Set the mean and covariance
mean1 = [0, 0]
mean2 = [2, 0]
cov1 = [[1, .7], [.7, 1]]
cov2 = [[.5, .4], [.4, .5]]
# Generate data from the mean and covariance
data1 = np.random.multivariate_normal(mean1, cov1, size=1000)
data2 = np.random.multivariate_normal(mean2, cov2, size=1000)
可視化一下生成的資料
plt.figure(figsize=(10,6))
plt.scatter(data1[:,0],data1[:,1])
plt.scatter(data2[:,0],data2[:,1])
sns.kdeplot(data1[:, 0], data1[:, 1], levels=20, linewidth=10, color='k', alpha=0.2)
sns.kdeplot(data2[:, 0], data2[:, 1], levels=20, linewidth=10, color='k', alpha=0.2)
plt.grid(False)
plt.show()
我們上面所做的工作是:使用均值和協方差矩陣生成了随機高斯分布。 而 GMM 要做正好與這個相反,也就是找到一個分布的均值和協方差,那麼怎麼做呢?
工作過程大緻如下:
為給定的資料集确定聚類的數量(這裡我們可以使用領域知識或其他方法,例如 BIC/AIC)。 根據我們上面的參數,有 1000 個資料點,和兩個簇2。
初始化每個簇的均值、協方差和權重參數。
使用期望最大化算法執行以下操作:
- 期望步驟(E-step):計算每個資料點屬于每個分布的機率,然後使用參數的目前估計評估似然函數
- 最大化步驟(M-step):更新之前的均值、協方差和權重參數,這樣最大化E步驟中找到的預期似然
- 重複這些步驟,直到模型收斂。
以上是GMM 算法的非數學的通俗化的解釋。
GMM 數學原理
有了上面的通俗解釋,我們開始進入正題,上面的解釋可以看到GMM 的核心在于上一節中描述的期望最大化 (EM) 算法。
在解釋之前,我們先示範一下 EM 算法在 GMM 中的應用。
1、初始化均值、協方差和權重參數
- mean (μ): 随機初始化
- 協方差 (Σ):随機初始化
- 權重(混合系數)(π):每個類的分數是指特定資料點屬于每個類的可能性。 一開始,這對所有簇都是平等的。 假設我們用三個分量拟合 GMM,那麼每個元件的權重參數可能設定為 1/3,這樣機率分布為 (1/3, 1/3, 1/3)。
2、期望步驟(E-step)
對于每個資料點,使用以下等式計算資料點屬于簇 () 的機率。 這裡的k是我分布(簇)數。
上面的_是高斯分布c的混合系數(有時稱為權重),它在上一階段被初始化,(|,)描述了高斯分布的機率密度函數(PDF),均值為和 關于資料點 x 的協方差 Σ; 是以可以有如下表示。
E-step 使用模型參數的目前估計值計算機率。 這些機率通常稱為高斯分布的“responsibilities”。 它們由變量 r_ic 表示,其中 i 是資料點的索引,c 是高斯分布的索引。 responsibilities衡量第 c 個高斯分布對生成第 i 個資料點“負責”的程度。 這裡使用條件機率,更具體地說是貝葉斯定理。
一個簡單的例子。 假設我們有 100 個資料點,需要将它們聚類成兩組。 我們可以這樣寫 r_ic(i=20,c=1) 。 其中 i 代表資料點的索引,c 代表我們正在考慮的簇的索引。
不要忘記在開始時,_ 會初始化為等值。 在我們的例子中,_1 = _2 = 1/2。
E-step 的結果是混合模型中每個資料點和每個高斯分布的一組responsibilities。 這些responsibilities會在 M-step更新模型參數的估計。
3、最大化步驟(M-step)
算法使用高斯分布的responsibilities(在 E-step中計算的)來更新模型參數的估計值。
M-step更新參數的估計如下:
上面的公式 4 更新 πc(混合系數),公式 5 更新 μc,公式6 更新 Σc。更新後的的估計會在下一個 E-step 中用于計算資料點的新responsibilities。
GMM 将将重複這個過程直到算法收斂,通常在模型參數從一次疊代到下一次疊代沒有顯着變化時就被認為收斂了。
讓我們将上面的過程整理成一個簡單的流程圖,這樣可以更好的了解:
數學原理完了,下面該開始使用 Python 從頭開始實作 GMM了
使用 Python 從頭開始實作 GMM
了解了數學原理,GMM的代碼也不複雜,基本上上面的每一個公式使用1-2行就可以完成
首先,建立一個實驗資料集,我們将為一維資料集實作 GMM,因為這個比較簡單
import numpy as np
n_samples = 100
mu1, sigma1 = -5, 1.2
mu2, sigma2 = 5, 1.8
mu3, sigma3 = 0, 1.6
x1 = np.random.normal(loc = mu1, scale = np.sqrt(sigma1), size = n_samples)
x2 = np.random.normal(loc = mu2, scale = np.sqrt(sigma2), size = n_samples)
x3 = np.random.normal(loc = mu3, scale = np.sqrt(sigma3), size = n_samples)
X = np.concatenate((x1,x2,x3))
下面是可視化的函數封裝
from scipy.stats import norm
def plot_pdf(mu,sigma,label,alpha=0.5,linestyle='k--',density=True):
"""
Plot 1-D data and its PDF curve.
"""
# Compute the mean and standard deviation of the data
# Plot the data
X = norm.rvs(mu, sigma, size=1000)
plt.hist(X, bins=50, density=density, alpha=alpha,label=label)
# Plot the PDF
x = np.linspace(X.min(), X.max(), 1000)
y = norm.pdf(x, mu, sigma)
plt.plot(x, y, linestyle)
并繪制生成的資料如下。 這裡沒有繪制資料本身,而是繪制了每個樣本的機率密度。
plot_pdf(mu1,sigma1,label=r"$\mu={} \ ; \ \sigma={}#34;.format(mu1,sigma1))
plot_pdf(mu2,sigma2,label=r"$\mu={} \ ; \ \sigma={}#34;.format(mu2,sigma2))
plot_pdf(mu3,sigma3,label=r"$\mu={} \ ; \ \sigma={}#34;.format(mu3,sigma3))
plt.legend()
plt.show()
下面開始根據上面的公式實作代碼:
1、初始化
def random_init(n_compenents):
"""Initialize means, weights and variance randomly
and plot the initialization
"""
pi = np.ones((n_compenents)) / n_compenents
means = np.random.choice(X, n_compenents)
variances = np.random.random_sample(size=n_compenents)
plot_pdf(means[0],variances[0],'Random Init 01')
plot_pdf(means[1],variances[1],'Random Init 02')
plot_pdf(means[2],variances[2],'Random Init 03')
plt.legend()
plt.show()
return means,variances,pi
2、E step
def step_expectation(X,n_components,means,variances):
"""E Step
Parameters
----------
X : array-like, shape (n_samples,)
The data.
n_components : int
The number of clusters
means : array-like, shape (n_components,)
The means of each mixture component.
variances : array-like, shape (n_components,)
The variances of each mixture component.
Returns
-------
weights : array-like, shape (n_components,n_samples)
"""
weights = np.zeros((n_components,len(X)))
for j in range(n_components):
weights[j,:] = norm(loc=means[j],scale=np.sqrt(variances[j])).pdf(X)
return weights
3、M step
def step_maximization(X,weights,means,variances,n_compenents,pi):
"""M Step
Parameters
----------
X : array-like, shape (n_samples,)
The data.
weights : array-like, shape (n_components,n_samples)
initilized weights array
means : array-like, shape (n_components,)
The means of each mixture component.
variances : array-like, shape (n_components,)
The variances of each mixture component.
n_components : int
The number of clusters
pi: array-like (n_components,)
mixture component weights
Returns
-------
means : array-like, shape (n_components,)
The means of each mixture component.
variances : array-like, shape (n_components,)
The variances of each mixture component.
"""
r = []
for j in range(n_compenents):
r.append((weights[j] * pi[j]) / (np.sum([weights[i] * pi[i] for i in range(n_compenents)], axis=0)))
#5th equation above
means[j] = np.sum(r[j] * X) / (np.sum(r[j]))
#6th equation above
variances[j] = np.sum(r[j] * np.square(X - means[j])) / (np.sum(r[j]))
#4th equation above
pi[j] = np.mean(r[j])
return variances,means,pi
然後就是訓練的循環
def train_gmm(data,n_compenents=3,n_steps=50, plot_intermediate_steps_flag=True):
""" Training step of the GMM model
Parameters
----------
data : array-like, shape (n_samples,)
The data.
n_components : int
The number of clusters
n_steps: int
number of iterations to run
"""
#intilize model parameters at the start
means,variances,pi = random_init(n_compenents)
for step in range(n_steps):
#perform E step
weights = step_expectation(data,n_compenents,means,variances)
#perform M step
variances,means,pi = step_maximization(X, weights, means, variances, n_compenents, pi)
plot_pdf(means,variances)
這樣就完成了,讓我們看看 GMM 表現如何。
在上圖中紅色虛線表示原始分布,而其他顔色表示學習分布。 第 30 次疊代後,可以看到的模型在這個生成的資料集上表現良好。
這裡隻是為了解釋GMM的概念進行的Python實作,在實際用例中請不要直接使用,請使用scikit-learn提供的GMM,因為它比我們這個手寫的要快多了,具體的對象名是 sklearn.mixture.GaussianMixture,它的常用參數如下:
- tol:定義模型的停止标準。 當下限平均增益低于 tol 參數時,EM 疊代将停止。
- init_params:用于初始化權重的方法
總結
本文對高斯混合模型進行全面的介紹,希望閱讀完本文後你對 GMM 能夠有一個詳細的了解,GMM 的一個常見問題是它不能很好地擴充到大型資料集。
作者:Ransaka Ravihara