簡介
本文章實現了Haojun Sun提出的一種計算高斯混合模型(GMM)重疊率的方法(論文:Measuring the component overlapping in the Gaussian mixture model)。這篇文論提出的方法可以計算任意兩個混合高斯分佈之間的重疊度。該方法可以用來評價GMM模型的好壞,我在我的論文中使用了這個算法,用來評價高斯混合模型聚類的可分性。
關於高斯混合模型(GMM)的相關概念可以參考另一篇博文:高斯混合模型及其EM算法的理解
使用GMM聚類或分析兩個高斯混合分佈的數據時,我們有時會希望兩個高斯分佈離得越遠越好,這樣表示數據纔有可分性。但很多情況下兩個高斯分佈會有重疊。一維和二維的重疊情況如下所示(圖片取自作者論文)。
我們可以計算一些指標來間接反映兩個高斯分佈的重疊情況。比如可以計算Mahalanobis距離,Bhattacharyya距離或Kullback-Leibler (KL)距離,可以衡量兩個高斯分佈的相似性。但是Mahalanobis距離預設兩個分佈具有相同的協方差,Bhattacharyya距離和KL距離都考慮了協方差,但卻沒有考慮高斯混合分佈的混合係數(mixing coefficient)。而且KL距離對高維的正態分佈沒有解析解,計算複雜。
這篇論文提出的計算OLR的方法考慮了高斯混合分佈中的所有參數,包括均值,協方差和混合係數。
OLR計算
假設有個維的樣本. 其中是一個維向量。一個混合高斯模型的pdf可以表示爲:
其中是混合係數,滿足且.
是一個維高斯分佈,可以表示爲下面的形式:
以二維高斯分佈爲例。當兩個高斯分佈有重疊時,會形成鞍狀。如上圖的d和e,二維高斯分佈混合時會出現兩個峯和一個鞍部;當兩個分佈幾乎完全混合時,鞍部可能消失,但峯還在,此時明顯的峯只有一個,如上圖中的f。
論文中的兩個高斯分佈的OLR定義如下:
其中是pdf中的鞍點(saddle point),是pdf中的較低的峯(lower peak point)。OLR的示意圖如下圖所示。OLR計算的是鞍點的pdf與較低峯的pdf的比值。這麼做是因爲鞍點的pdf與混合係數有關。注意到OLR並不是落在重疊區域內數據的比例,因此跟數據量無關,只跟數據的分佈有關。定義中的容易求,只需將兩個均值帶入(1)式,取較小的值即可。但是不容直接求得。下面介紹如何計算。
注意到兩個峯點和鞍點在整個曲面上都應該是極值點。因此和應該滿足下式:
其中,
(4)式式一條曲線。如果已知,(5)式可求出。論文接下來證明,峯點和鞍點會在同一條曲線上,曲線方程如下:
而且,鞍點會在以兩個峯點(均值處的pdf)之間的曲線段上。因此只要從第一個均值開始,沿着曲線(4)一直找到另一個均值,這個過程中的極小值點就是鞍點。得到鞍點的座標,帶入(1)式,就可以求得鞍點的pdf值。(6)式中的曲線稱爲Ridge Curve (RC).
OLR的算法如下:
- 輸入混合高斯分佈的參數
- 計算RC:
- 沿着RC,從到按步長找到RC中取得最大值和最小值的點
3.1 令,的下一個點的第一維(x座標).
3.2 將帶入RC方程(6),求得的第二維(y座標)
3.3 根據(1)式計算
3.4 if and , is maximum point (peak)
3.5 if and , is minimum point - 根據(3)式計算OLR
上述算法可以取. 作者認爲當OLR小於0.6時,兩個類別可分性良好(visually well separated),當OLR大於0.8時,兩個類別嚴重重疊(strongly overlapping)。
如果是有多個類別的情況,可以計算所有任意兩個類別的重疊度,最後對所有重疊度求均值作爲整體的重疊度。
算法實現
求可以用python第三方統計包scipy.stats
中的multivariate_normal
計算。輸入兩個高斯分佈的參數可以求出pdf值。
完整代碼可以參考GMM Overlap Rate。論文中給出的算法有一些問題。
import math
import numpy as np
import matplotlib.pyplot as plt
from numpy.linalg import inv
from scipy.stats import multivariate_normal
class BiGauss(object):
"""docstring for BiGauss"""
def __init__(self, mu1, mu2, Sigma1, Sigma2, pi1, pi2, steps = 100):
super(BiGauss, self).__init__()
self.mu1 = mu1
self.mu2 = mu2
self.Sigma1 = Sigma1
self.Sigma2 = Sigma2
self.pi1 = pi1
self.pi2 = pi2
self.biGauss1 = multivariate_normal(mean = self.mu1, cov = self.Sigma1, allow_singular = True)
self.biGauss2 = multivariate_normal(mean = self.mu2, cov = self.Sigma2, allow_singular = True)
self.steps = steps
self.inv_Sig1 = -inv(self.Sigma1)
self.inv_Sig2 = -inv(self.Sigma2)
# variables to calculate RC
self.A_1 = self.inv_Sig1[0][0]
self.B_1 = self.inv_Sig1[0][1]
self.C_1 = self.inv_Sig1[1][0]
self.D_1 = self.inv_Sig1[1][1]
self.A_2 = self.inv_Sig2[0][0]
self.B_2 = self.inv_Sig2[0][1]
self.C_2 = self.inv_Sig2[1][0]
self.D_2 = self.inv_Sig2[1][1]
計算pdf
def pdf(self, x):
return self.pi1 * self.biGauss1.pdf(x) + self.pi2 * self.biGauss2.pdf(x)
根據求出,使得在RC上
def RC(self, x):
E = self.A_1 * (x - self.mu1[0])
F = self.C_1 * (x - self.mu1[0])
G = self.A_2 * (x - self.mu2[0])
H = self.C_2 * (x - self.mu2[0])
I = E * self.D_2 - F * self.B_2
J = H * self.B_1 - G * self.D_1
K = self.B_1 * self.D_2 - self.B_2 * self.D_1
M = F * G - E * H
P = K
Q = I + J - K * (self.mu2[1] + self.mu1[1])
S = -(M + I * self.mu2[1] + J * self.mu1[1])
if Q**2 - 4*P*S < 0:
return None
y = max((-Q + math.sqrt(Q**2 - 4*P*S)) / (2*P), (-Q - math.sqrt(Q**2 - 4*P*S)) / (2*P))
return y
求OLR
def OLR(self):
e = math.sqrt((self.mu1[0] - self.mu2[0])**2 + (self.mu1[1] - self.mu2[1])**2) / float(self.steps)
x_step = e*(self.mu1[0]-self.mu2[0]) # each step for x
y_step = e*(self.mu1[1]-self.mu2[1]) # each step for y
p_x = self.mu1[0] - x_step
while self.RC(p_x) == None:
p_x = p_x - x_step
p_y = self.RC(p_x)
p = [p_x, p_y]
p_pre = self.mu1
p_min = min(self.pdf(p), self.pdf(p_pre))
p_max = max(self.pdf(p), self.pdf(p_pre))
index = 0
while index < self.steps:
if self.RC(p[0] - x_step) != None:
p_next = [p[0] - x_step, self.RC(p[0] - x_step)] # next point on ridge curve
if self.pdf(p) > self.pdf(p_pre) and self.pdf(p) > self.pdf(p_next):
p_max = self.pdf(p)
if self.pdf(p) < self.pdf(p_pre) and self.pdf(p) < self.pdf(p_next):
p_min = self.pdf(p)
p_pre = p
p = p_next
index += 1
pdf_mu1 = self.pdf(self.mu1)
pdf_mu2 = self.pdf(self.mu2)
return p_min / min(pdf_mu1, pdf_mu2) if p_min < min(pdf_mu1, pdf_mu2) else 1.0
上述代碼有時會計算出OLR大於1的情況,還沒有分析原因,因爲草稿丟了,不知道代碼中的A~S變量代表什麼意思……因此代碼中做了限制,如果求出的OLR大於1,那麼只會返回1.
論文中探討了混合係數、均值間距離和協方差對OLR的影響。論文中給出了一個例子,如下。當時該例子可以取到最小的OLR . 論文沒有給出的具體數值,但是給出了OLR隨取值變化的曲線圖。上述代碼算出來的結果是,也確實在處取得。與曲線圖中的位置吻合。論文中提到當時,ORL等於0.7288,上述代碼給出的結果是0.7270.
示例代碼中也畫出了OLR隨變化的曲線圖和OLR隨兩個均值之間距離變化的曲線圖。曲線走勢與論文中的圖示一致,但具體數值有些差別。
這個示例代碼只能計算二維混合高斯模型,更高維的無法計算,但是理論上,這個算法是適用於任何維度的GMM的。