赞
踩
前几天忙着开学的事,加上图像处理的学习,这篇文章稍微搁置了些时间。闲话不多说,直接进入正题。本篇对EM算法和高斯混合模型的推导过程不会再详细说明,相关部分请见前两篇文章。
回顾在上一篇文章结尾,GMM聚类步骤为:
step1:
定义高斯分布个数K,对每个高斯分布设置初始参数值 θ k ( 0 ) = α k , μ k , Σ k \theta^{(0)}_k = \alpha_k,\mu_k,\Sigma_k θk(0)=αk,μk,Σk 。一般第一步不会自己设置初始值,而是通过K-mean算法计算初始值。
step2 E-step:
根据当前的参数 θ ( t ) \theta^{(t)} θ(t) ,计算每一个隐变量的后验概率分布 γ t ( z ( i ) ) \gamma_t(z^{(i)}) γt(z(i))。精确到每一个后验概率的计算,有
γ t ( z k ( i ) ) = α k ( t ) N ( x ∣ μ k ( t ) , Σ k ( t ) ) ∑ j = 1 K α j ( t ) N ( x ∣ μ j ( t ) , Σ j ( t ) ) \gamma_t(z_k^{(i)}) = \frac{\alpha_k^{(t)}N(x|\mu_k^{(t)},\Sigma_k^{(t)})}{\sum_{j=1}^K \alpha_j^{(t)}N(x|\mu_j^{(t)},\Sigma_j^{(t)})} γt(zk(i))=∑j=1Kαj(t)N(x∣μj(t),Σj(t))αk(t)N(x∣μk(t),Σk(t))
这里解释一下:
- γ t ( z k ( i ) ) \gamma_t(z_k^{(i)}) γt(zk(i)) :在第t轮时,第i个样本的隐变量z的第k个概率。或者说,在第t轮时,第i个样本属于第k个高斯分布的后验概率。
- α k ( t ) \alpha_k^{(t)} αk(t):在第t轮时,已固定的第k个高斯分布的权值。
- N ( x ∣ μ k ( t ) , Σ k ( t ) ) N(x|\mu_k^{(t)},\Sigma_k^{(t)}) N(x∣μk(t),Σk(t)) :在第t轮时,第k个已固定的多维高斯分布概率密度函数。
- 带上标(t)的都是已固定参数。
step3 M-step:
根据E-step计算出的隐变量后验概率分布,进一步计算新的 θ ( t + 1 ) \theta^{(t+1)} θ(t+1)
α k ( t + 1 ) = 1 N ∑ i = 1 n γ t ( z k ( i ) ) \alpha_k^{(t+1)} = \frac{1}{N}\sum_{i=1}^n\gamma_t(z^{(i)}_k) αk(t+1)=N1∑i=1nγt(zk(i))
μ k ( t + 1 ) = ∑ i = 1 n [ γ t ( z k ( i ) ) x i ] ∑ i = 1 n γ t ( z k ( i ) ) \mu_k^{(t+1)} = \frac{\sum_{i=1}^n[\gamma_t(z^{(i)}_k)x_i]}{\sum_{i=1}^n\gamma_t(z^{(i)}_k)} μk(t+1)=∑i=1nγt(zk(i))∑i=1n[γt(zk(i))xi]
Σ k ( t + 1 ) = ∑ i = 1 n γ t ( z k ( i ) ) ( x n − μ k ) ( x n − μ k ) T ∑ i = 1 n γ t ( z k ( i ) ) \Sigma_k^{(t+1)} = \frac{\sum_{i=1}^n\gamma_t(z^{(i)}_k)(x_n-\mu_k)(x_n-\mu_k)^T}{\sum_{i=1}^n\gamma_t(z^{(i)}_k)} Σk(t+1)=∑i=1nγt(zk(i))∑i=1nγt(zk(i))(xn−μk)(xn−μk)T
step4:
循环E-step和M-step直至收敛。
由于多维高斯分布概率密度函数对于个人而言不太好实现(其实是因为没学,可能后面会开一篇文章讲多维高斯分布概率密度函数的计算),所以这里就以服从一维高斯分布的数据作为手动代码实现的训练集。即训练集只有一个特征。
这里使用sklearn的make_blobs函数生成数据。
scikit中的make_blobs方法常被用来生成聚类算法的测试数据,make_blobs会根据用户指定的特征数量、中心点数量、范围等来生成几类数据。
其中:
- n_samples是待生成的样本的总数
- n_features是每个样本的特征数
- centers表示聚类中心点个数,可以理解为种类数。当centers为列表时,为每个类别指定中心位置。
- cluster_std设置每个类别的方差
- random_state是随机种子
下面将通过两个例子来直观说明make_blobs函数。
绘制数据时会用到plt.hist函数,详见python–plt.hist函数的输入参数和返回值的解释。
import matplotlib.pyplot as plt
from sklearn.datasets._samples_generator import make_blobs
# 生成样本集
centers = 2
X,y = make_blobs(n_samples=1000,n_features=1,centers=centers,cluster_std=[0.5,1],random_state=100)
plt.hist(X,bins = 100)
plt.show()
import matplotlib.pyplot as plt
from sklearn.datasets._samples_generator import make_blobs
# 生成样本集
centers = [[3],[5]]
X,y = make_blobs(n_samples=1000,n_features=1,centers=centers,cluster_std=[0.5,1],random_state=100)
plt.hist(X,bins = 100)
plt.show()
可以看到上图为两个一维高斯分布混在了一起。接下来将使用上图所示数据集作为我们手动实现GMM的训练集。
对于一维高斯分布概率密度函数,求解:
γ
t
(
z
k
(
i
)
)
=
α
k
(
t
)
N
(
x
∣
μ
k
(
t
)
,
Σ
k
(
t
)
)
∑
j
=
1
K
α
j
(
t
)
N
(
x
∣
μ
j
(
t
)
,
Σ
j
(
t
)
)
\gamma_t(z_k^{(i)}) = \frac{\alpha_k^{(t)}N(x|\mu_k^{(t)},\Sigma_k^{(t)})}{\sum_{j=1}^K \alpha_j^{(t)}N(x|\mu_j^{(t)},\Sigma_j^{(t)})}
γt(zk(i))=∑j=1Kαj(t)N(x∣μj(t),Σj(t))αk(t)N(x∣μk(t),Σk(t))
其中,
N
(
x
∣
μ
,
Σ
)
=
1
σ
2
π
e
−
(
x
−
μ
)
2
2
σ
2
N(x|\mu,\Sigma) = \frac {1}{\sigma\sqrt{2\pi}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}
N(x∣μ,Σ)=σ2π
1e−2σ2(x−μ)2
本来打算把一维高斯分布概率密度函数写成代码的形式,但是途中发现了scipy科学计算包,这里就顺带理一下scipy中的高斯分布使用方法:【Python笔记】Scipy.stats.norm函数解析。相关参数解释都在这篇文章里了,建议先看文章。当然如果觉得太麻烦了,就直接自己把上面的概率密度函数变成代码形式就好。
def E_step(data,theta):
Rz = []
# 计算分子
Rz1_up = theta['w1']*stats.norm(theta['mu1'],theta['sigma1']).pdf(data)
Rz2_up = theta['w2']*stats.norm(theta['mu2'],theta['sigma2']).pdf(data)
# 分母
Rz_down = theta['w1']*stats.norm(theta['mu1'],theta['sigma1']).pdf(data)+theta['w2']*stats.norm(theta['mu2'],theta['sigma2']).pdf(data)
# 第K个隐变量的后验概率
Rz1 = Rz1_up/Rz_down
Rz2 = Rz2_up/Rz_down
Rz.append(Rz1)
Rz.append(Rz2)
Rz = np.array(Rz)
return Rz
计算:
α k ( t + 1 ) = 1 N ∑ i = 1 n γ t ( z k ( i ) ) \alpha_k^{(t+1)} = \frac{1}{N}\sum_{i=1}^n\gamma_t(z^{(i)}_k) αk(t+1)=N1∑i=1nγt(zk(i))
μ k ( t + 1 ) = ∑ i = 1 n [ γ t ( z k ( i ) ) x i ] ∑ i = 1 n γ t ( z k ( i ) ) \mu_k^{(t+1)} = \frac{\sum_{i=1}^n[\gamma_t(z^{(i)}_k)x_i]}{\sum_{i=1}^n\gamma_t(z^{(i)}_k)} μk(t+1)=∑i=1nγt(zk(i))∑i=1n[γt(zk(i))xi]
Σ k ( t + 1 ) = ∑ i = 1 n γ t ( z k ( i ) ) ( x n − μ k ) ( x n − μ k ) T ∑ i = 1 n γ t ( z k ( i ) ) \Sigma_k^{(t+1)} = \frac{\sum_{i=1}^n\gamma_t(z^{(i)}_k)(x_n-\mu_k)(x_n-\mu_k)^T}{\sum_{i=1}^n\gamma_t(z^{(i)}_k)} Σk(t+1)=∑i=1nγt(zk(i))∑i=1nγt(zk(i))(xn−μk)(xn−μk)T
前两个没什么好说明的,最后一个需要注意:因为示例是一维的,所以 x n − μ k x_n-\mu_k xn−μk 是数字,倒置也是他自己,所以分子后面那一块变成了求平方。另外, Σ \Sigma Σ 是 σ 2 \sigma^2 σ2 ,求出来后需要开平方才是我们需要的 σ \sigma σ。
def M_step(data,theta,Rz,n):
theta['w1'] = np.sum(Rz[0])/n
theta['w2'] = np.sum(Rz[1])/n
theta['mu1'] = np.sum(Rz[0]*data)/np.sum(Rz[0])
theta['mu2'] = np.sum(Rz[1]*data)/np.sum(Rz[1])
Sigma1 = np.sum(Rz[0]*np.square(data-theta['mu1']))/(np.sum(Rz[0]))
Sigma2 = np.sum(Rz[1]*np.square(data-theta['mu2']))/(np.sum(Rz[1]))
theta['sigma1'],theta['sigma2'] = np.sqrt(Sigma1),np.sqrt(Sigma2)
def run_GMM(data,times):
# 初始化
theta = {}
theta['w1'],theta['w2'] = 0.5,0.5
theta['mu1'],theta['mu2'] = 0,10
theta['sigma1'],theta['sigma2'] = 0.8,0.8
n = len(data)
data = np.array(data)
for t in range(times):
Rz = E_step(data,theta)
M_step(data,theta,Rz,n)
if t % 100 == 0:
print(theta)
print("=========")
然后我们可以运行看看效果:
if __name__ == '__main__':
# 生成样本集
centers = [[3], [5]]
X_train, y_test = make_blobs(n_samples=1000, n_features=1, centers=centers, cluster_std=[0.5, 1], random_state=100)
# plt.hist(X, bins=100)
# plt.show()
run_GMM(data=X_train,times=1000)
可以看到,最后GMM收敛到了下面所示的值:
{'w1': 0.467708978482864, 'w2': 0.5322910215171359, 'mu1': 2.9671070095836107, 'mu2': 4.878609055231818, 'sigma1': 0.4970731536722904, 'sigma2': 1.0992431014967026}
=========
对比我们生成数据时的各个参数,发现mu和sigma差别都不大。它成功的从高斯混合模型中分离了两个高斯模型。并且与我们最初设置这两个高斯模型的参数差别不大。
这部分是可选内容,觉得比较好玩的可以试试。
def draw_GMM(thetas):
x = np.linspace(0, 10, 100) # 设置画图范围
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# ax.hist(X_train, bins=100)
plt.ion()
for theta in thetas:
y_1 = stats.norm(theta['mu1'], theta['sigma1']).pdf(x)
y_2 = stats.norm(theta['mu2'], theta['sigma2']).pdf(x)
lines1 = ax.plot(x, y_1,c='red')
lines2 = ax.plot(x, y_2,c='green')
plt.pause(0.3)
try:
ax.lines.remove(lines1[0])
ax.lines.remove(lines2[0])
except Exception:
pass
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets._samples_generator import make_blobs
from scipy import stats
def draw_GMM(thetas):
x = np.linspace(0, 10, 100) # 设置画图范围
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# ax.hist(X_train, bins=100)
plt.ion()
for theta in thetas:
y_1 = stats.norm(theta['mu1'], theta['sigma1']).pdf(x)
y_2 = stats.norm(theta['mu2'], theta['sigma2']).pdf(x)
lines1 = ax.plot(x, y_1,c='red')
lines2 = ax.plot(x, y_2,c='green')
plt.pause(0.3)
try:
ax.lines.remove(lines1[0])
ax.lines.remove(lines2[0])
except Exception:
pass
def E_step(data,theta):
Rz = []
# 计算分子
Rz1_up = theta['w1']*stats.norm(theta['mu1'],theta['sigma1']).pdf(data)
Rz2_up = theta['w2']*stats.norm(theta['mu2'],theta['sigma2']).pdf(data)
# 分母
Rz_down = theta['w1']*stats.norm(theta['mu1'],theta['sigma1']).pdf(data)+theta['w2']*stats.norm(theta['mu2'],theta['sigma2']).pdf(data)
# 第K个隐变量的后验概率
Rz1 = Rz1_up/Rz_down
Rz2 = Rz2_up/Rz_down
Rz.append(Rz1)
Rz.append(Rz2)
Rz = np.array(Rz)
return Rz
def M_step(data,theta,Rz,n):
theta['w1'] = np.sum(Rz[0])/n
theta['w2'] = np.sum(Rz[1])/n
theta['mu1'] = np.sum(Rz[0]*data)/np.sum(Rz[0])
theta['mu2'] = np.sum(Rz[1]*data)/np.sum(Rz[1])
Sigma1 = np.sum(Rz[0]*np.square(data-theta['mu1']))/(np.sum(Rz[0]))
Sigma2 = np.sum(Rz[1]*np.square(data-theta['mu2']))/(np.sum(Rz[1]))
theta['sigma1'],theta['sigma2'] = np.sqrt(Sigma1),np.sqrt(Sigma2)
def run_GMM(data,times):
# 初始化
theta = {}
draw_theta = []
theta['w1'],theta['w2'] = 0.5,0.5
theta['mu1'],theta['mu2'] = 0,10
theta['sigma1'],theta['sigma2'] = 0.8,0.8
n = len(data)
data = np.array(data)
for t in range(times):
Rz = E_step(data,theta)
M_step(data,theta,Rz,n)
if t<=50:
print(theta)
print("=========")
draw_theta.append(theta.copy())
draw_GMM(draw_theta)
if __name__ == '__main__':
# 生成样本集
centers = [[3], [5]]
X_train, y_test = make_blobs(n_samples=1000, n_features=1, centers=centers, cluster_std=[0.5, 1], random_state=100)
plt.hist(X_train,100)
plt.show()
run_GMM(data=X_train,times=650)
原始数据:
训练结果:
又到了令人愉悦的部分了。
这部分将使用sklearn的GMM来实现聚类,为了便于画图,这里特征只选两个。即二维高斯混合模型。另外顺带一提,如果你稍微查看了sklearn中GMM的源码,就会发现它在初始化时就是先用了一次Kmean。
关于sklearn的GMM用法就不多说了,推荐一篇文章: Scikit-Learn学习笔记——高斯混合模型(GMM)应用:分类、密度估计、生成模型_盐味橙汁的博客-CSDN博客_高斯混合模型分类
这篇文章还说明了GMM在密度估计、生成模型上的应用。
注:绘制边缘椭圆部分搬的是这篇文章的代码,因为本人还不太会。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
from sklearn.mixture import GaussianMixture
from sklearn.datasets._samples_generator import make_blobs
def creat_data(centers,size = 1000):
X,labels = make_blobs(n_samples=size,n_features=2,centers=centers,cluster_std=1,random_state=100)
# 把X训练集变为椭圆的形式
rng = np.random.RandomState(12)
X = np.dot(X, rng.randn(2, 2))
return X,labels
def draw_ellipse(position, covariance, ax=None, **kwargs):
"""用给定的位置和协方差画一个椭圆"""
ax = ax or plt.gca()
#将协方差转换为主轴
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)
#画出椭圆
for nsig in range(1, 4):
ax.add_patch(Ellipse(position, nsig * width, nsig * height, angle, **kwargs))
def plot_gmm(gmm, X,labels, label=True, ax=None):
ax = ax or plt.gca()
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)
w_factor = 0.2 / gmm.weights_.max()
for pos, covar, w in zip(gmm.means_, gmm.covariances_, gmm.weights_):
draw_ellipse(pos, covar, alpha=w * w_factor)
plt.show()
if __name__ == '__main__':
centers = 5
X,labels = creat_data(centers = centers)
# 创建并训练高斯混合模型
model = GaussianMixture(n_components = centers,covariance_type='full')
model.fit(X)
# 预测
labels_predict = model.predict(X)
# 绘制图像
plot_gmm(model, X,labels_predict)
原始数据:
经过聚类后的数据:
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。