【机器学习】高斯混合模型(GMM)算法及其实现

∥☆過路亽.° 提交于 2019-11-29 19:32:41

相关视频可见皮皮学机器学习

高斯混合模型(GMM)

学完了EM算法之后,就情不自禁地想学习一下高斯混合模型了。

高斯混合模型是具有如下形式的概率分布模型。
P(x)=k=1KWkg(xμk,k) P(x)=\sum_{k=1}^KW_kg(x|\mu_k,\sum_k)
现在我们来解释一下上式的参数,K为GMM中成分的个数,也就是说有几个高斯分布,g是这几个高斯分布对应的分布密度函数,均值μ\mu是其均值,协方差矩阵\sum,W是每个成分的权重。

案例引入

依然使用一个男女身高为例:

在校园里随机抽取2000个学生,其中有男有女,已知男生,女生的身高都服从高斯分布,这两个高斯分布的均值和方差我们都不知道,另外由于某种原因,我们也不知道2000个学生里男生和女生的个数,现在我们要求出两个分布的均值和方差,还有男女比例。

1.初始化参数

2.计算每条身高数据为男/女分布的概率

该条样本所属于男生/女生分布的概率,这里用R表示,其实这一步就是EM算法中的E步,求期望

一维高斯函数:
g(hμ,σ)=12πσe12σ(xμ)2 g(h|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}e^{-\frac{1}{2\sigma}(x-\mu)^2}
例如:当u=1.75m,标准差为0.1,对于身高为1.80m的人有:

g(hμ,σ)=12π0.1e10.2(1.81.75)2=3.939865g(h|\mu,\sigma)=\frac{1}{\sqrt{2\pi}*0.1}e^{-\frac{1}{0.2}(1.8-1.75)^2}=3.939865

3.更新男女分布的期望

现在来更新男/女分布的期望值(这个期望值与E步求期望不同)u,这个计算的思想就是:所有样本中为男/女分布的概率乘以当前身高值之和,然后除以个数据为男/女分布的概率之和,这里需要注意一下,以往我们计算均值就是各个数据之和除以数据量即可,此时,由于每个身高前已经乘以相应概率值并将这些乘积结果数据相加,这就相当于以往的数据求和部分,相除的数是所有为男/女分布概率之和,而以往的计算只是一个确定的特例而已,可看下式:
x^=i=1Nri×xii=1Nri=ri=1i=1NxiN \hat{x}=\frac{\sum_{i=1}^N r_i\times x_i}{\sum_{i=1}^Nr_i}=(r_i=1)\frac{\sum_{i=1}^N x_i}{N}

实际计算公式如下:

4.更新男女分布的标准差

下面我就开始计算男\女分布的标准差,如果不理解该式,可以查看计算均值的解释:

5.更新两个分布的权重

更新男生/女身分布的权值,这个也应该好理解,总共有就是男分布可能性之和/n即可(每个数据都有可能是属于男/女分布的,但是概率之和肯定是1,那么n条数据的概率之和就是n了):

到目前为止已经完成了一次迭代,接着我们根据自己需求进行多次迭代更新。需要注意的是,以上是一维数据的高斯分布,也就是参数只有一个:身高h。当涉及到多维高斯分布的话将涉及协方差等相关知识。为什么这样计算,可以参考EM算法。总的来说,GMM中数据对高斯分布的响应度,也就是数据是哪个分布的概率,就相当于K-means中的距离计算,GMM中的根据分布响应度计算高斯分量参数就相当于k-means计算质心的为止,它们都是不断迭代达到最优。

程序实现

我们使用numpy,scipy,pandas,matplotlib来实现GMM

1.数据准备

# 导入必要的库
import numpy as np
import pandas as pd
import matplotlib.pylab as plt
from scipy import stats
# 产生身高数据
np.random.seed(100)  # 固定随机数种子,确保下次运行数据相同
sim_data_boy = np.random.normal(180,8,2000)  #产生满足正太分布的随机数,参数分别为:均值,方差,样本量
# 对数据绘制成直方图
pd.Series(sim_data_boy).hist(bins=200)       #hist(bins=200)绘制直方图,bins 指定像素
sim_data_girl = np.random.normal(160,6,2000)
pd.Series(sim_data_girl).hist(bins=200)

sim_datas = list(sim_data_boy)  # 转化为list
sim_datas.extend(sim_data_girl) # 混合数据
sim_datas = np.array(sim_datas) # 再转成numpy格式的数据
ax2 = pd.Series(sim_datas).hist(bins=200)


2.GMM实现

# 编程实现EM算法
# 两个正太分布的初始化参数
w1, w2 = 0.5, 0.5
mu1, mu2 = 170, 160
std1, std2 = 10, 5
# 一个维度
d = 1    
n = len(sim_datas)  # 样本长度
# 开始EM算法的主循环
for t in range(1000):
    # E-step
    rz1_up = w1 * stats.norm(mu1,std1).pdf(sim_datas)
    rz_down = (w1*stats.norm(mu1,std1).pdf(sim_datas)+
              w2*stats.norm(mu2,std2).pdf(sim_datas))
    rz2_up = w2*stats.norm(mu2, std2).pdf(sim_datas)
    rz1 = rz1_up/rz_down      # 为男分布的概率
    rz2 = rz2_up/rz_down      # 为女分布的概率
    
    # M-step
    mu1 = np.sum(rz1*sim_datas)/np.sum(rz1)
    mu2 = np.sum(rz2*sim_datas)/np.sum(rz2)
    std1 = np.sqrt(np.sum(rz1*np.square(sim_datas-mu1))/(d*np.sum(rz1)))
    std2 = np.sqrt(np.sum(rz2*np.square(sim_datas-mu2))/(d*np.sum(rz2)))
    w1 = np.sum(rz1)/n
    w2 = np.sum(rz2)/n
    if t %100 == 0:
        print(mu1, mu2, std1, std2, w1, w2)

部分程序说明:stats是scipy中的科学计算包,其中包含很多分布,如:卡方分布:stats.chi2,正太分布:stats.norm,t/f分布:stats.t/f,其中pdf函数指对数概率密度函数进行求解并返回。

打印结果:

175.82534688605685 159.74537319307973 11.180683924445757 5.376979089118282 0.6428526326566194 0.35714736734338054
179.1730774164662 159.72571037409548 8.657523393338522 5.848079825606313 0.5325510758345432 0.4674489241654568
179.19407495539568 159.73749661630256 8.645747701001799 5.8541424994539595 0.5316931794513514 0.46830682054864853
179.19417763509628 159.73755438932045 8.645690151545104 5.854172255274996 0.5316889829577561 0.46831101704224387
179.19417813677356 159.73755467159384 8.645689870368132 5.854172400659975 0.5316889624543018 0.46831103754569825
179.19417813922468 159.73755467297298 8.645689868994335 5.854172401370298 0.5316889623541249 0.4683110376458751
179.1941781392366 159.73755467297968 8.645689868987642 5.854172401373757 0.5316889623536369 0.46831103764636306
179.19417813923658 159.7375546729797 8.645689868987644 5.854172401373765 0.5316889623536367 0.46831103764636334
179.19417813923658 159.73755467297968 8.645689868987638 5.854172401373764 0.5316889623536365 0.46831103764636356
179.19417813923664 159.73755467297968 8.645689868987624 5.85417240137377 0.5316889623536354 0.4683110376463646

既然模型已经中的参数已经训练好了,我们可以进行测试了:

# 生成测试数据
data_test = range(int(min(sim_datas)), int(max(sim_datas)))  # 生成一组身高数据
m_predict = stats.norm(mu1, std1).pdf(data_test)  # 男生分布的预测
f_predict = stats.norm(mu2, std2).pdf(data_test)  # 女生分别的预测
plt.plot(data_test, m_predict, color='b')
plt.plot(data_test, f_predict, color='r')

我们使用sklearn来实现GMM,生成的数据与上面相同,主要看如何构建:

# 用之前的生成的数据
sim_datas_array = sim_datas.reshape(len(sim_datas), 1)  # 把横排的数据变成竖排的数据
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=2).fit(sim_datas_array) # 拟合混合高斯分布
labels = gmm.predict(sim_datas_array)  # 预测哪种类型概率最大
# 划分不同分布的数据
predict_0 = [sim_datas[i] for i in range(len(sim_datas)) if labels[i]==0]
predict_1 = [sim_datas[i] for i in range(len(sim_datas)) if labels[i]==1] 
# 绘制结果
pd.Series(predict_0).hist(bins=200)
pd.Series(predict_1).hist(bins=200)

个人订阅号
更多编程,人工智能知识等着你
image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!