学习笔记【机器学习重点与实战】——12 EM算法

1 EM算法

之前章节的实例中都假设训练样本所有属性变量的值都已被观测到,即变量都是观测变量(observable variable),训练样本是”完整”的。但在现实应用中往往会遇到”不完整”的训练样本,即存在未观测变量(”隐变量” - hidden variable 或 latent variable)。 而EM算法就可以处理这类含有隐变量样本的问题。

EM算法是一种迭代算法,1977年由Dempster等人总结提出,用于含有隐变量的概率模型参数的极大似然估计,或极大后验概率估计。EM算法的每次迭代由两步组成:E步,求期望(expectation);M步,求极大(maximization)。所以这一算法称为期望极大算法(expectation maximization algorithm),简称EM算法。

简要来说, EM 算法使用两个步骤交替计算:第一步是期望 (E) 步,利用当前估计的参数值来计算对数似然的期望值;第二步是最大化 (M) 步,寻找能使 E 步产生的似然期望最大化的参数值。然后,新得到的参数值重新被用于 E 步,……直至收敛到局部最优解。具体算法流程如下:

输入:观测变量数据 Y ,隐变量数据 Z ,联合分布 P ( Y , Z | θ ) ,条件分布 P ( Z | Y , θ )

输出:模型参数 θ

(1)选择参数的初值 θ ( 0 ) ,开始迭代;

(2) E 步:记 θ ( i ) 为第 i 次迭代参数 θ 的估计值,在第 i + 1 次迭代的 E 步,计算

(31) Q ( θ , θ i ) = E Z [ l o g P ( Y , Z | θ ) | Y , θ i ] (32) = Z l o g P ( Y , Z | θ ) P ( Z | Y , θ i )

这里, P ( Z | Y , θ i ) 是在给定观测数据 Y 和当前的参数估计 θ i 下隐变量数据 Z 的条件概率分布;

(3) M 步:求使 Q ( θ , θ i ) 极大化的 θ ,确定第 i + 1 次迭代的参数的估计值 θ ( i + 1 )

θ ( i + 1 ) = a r g max θ Q ( θ , θ i )

(4)重复第(2)步和第(3)步,直到收敛。

事实上,隐变量估计问题也可通过梯度下降等优化算法求解,但由于求和的项数将随着隐变量的数目以指数级上升,会给梯度计算带来麻烦;而 EM 算法则可看作一种非梯度优化方法。

在一般条件下 EM 算法是收敛的,但不能保证收敛到全局最优。

EM 算法可以用于生成模型的非监督学习。生成模型由联合概率分布 P ( X , Y ) 表示,可以认为非监督学习训练数据是联合概率分布产生的数据。 X 为观测数据, Y 为未观测数据。

2 高斯混合模型的参数估计

EM 算法应用及其广泛,主要应用于含有隐变量的概率模型的学习。高斯混合模型的参数估计是 EM 算法的一个重要应用。

2.1 高斯混合模型

高斯混合模型是指具有如下形式的概率分布模型:

P ( y | θ ) = k = 1 K α k Φ ( y | θ k )

其中, α k 是系数(类别概率), α k 0 k = 1 K α k = 1 Φ ( y | θ k ) 是高斯分布密度函数,

Φ ( y | θ k ) = 1 2 π σ k exp ( ( y μ k ) 2 2 σ k 2 )

θ k = ( μ k , σ k 2 ) ,称为第 k 个分模型。

一般混合模型可以由任意概率分布密度代替上面的高斯分布密度函数。

2.2 高斯混合模型参数估计的 EM 算法

输入:观测数据 y 1 , y 2 , . . . , y N ,高斯混合模型;

输出:高斯混合模型参数

(1)取参数的初始值开始迭代

(2) E 步:依据当前模型参数,计算分模型 k 对观测数据 y j 的响应度

γ ^ j k = α k Φ ( y j | θ k ) k = 1 K α k Φ ( y j | θ k )       j = 1 , 2... , N ; k = 1 , 2... , K

(3) M 步:计算新一轮迭代的模型参数

(45) μ ^ k = j = 1 N γ ^ j k y j j = 1 N γ ^ j k (46) σ ^ k 2 = j = 1 N γ ^ j k ( y j μ k ) 2 j = 1 N γ ^ j k (47) α ^ k = j = 1 N γ ^ j k N (48) j = 1 , 2... , N ; k = 1 , 2... , K

(4)重复第(2)步和第(3)步,直到收敛。

3 实战

3.1 高斯混合模型参数估计的 EM 算法实现

生成两个三维高斯分布样本集,并分E步和M步实现 EM 算法。代码实现如下:

import numpy as np
from scipy.stats import multivariate_normal
from sklearn.mixture import GaussianMixture
from mpl_toolkits.mplot3d import Axes3D
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.metrics.pairwise import pairwise_distances_argmin

if __name__ == '__main__':
    # 生成高斯混合模型样本点
    np.random.seed(0)               # 撒固定的种子,保证每次样本集数据相同
    mu1_fact = (0, 0, 0)            # 均值点
    cov1_fact = np.diag((1, 2, 3))  # 分布的协方差矩阵,对角阵
    # 生成3维高斯分布样本集1
    data1 = np.random.multivariate_normal(mu1_fact, cov1_fact, 400)
    mu2_fact = (2, 2, 1)            # 均值点
    cov2_fact = np.array(((4, 1, 3), (1, 2, 1), (3, 1, 4)))  # 正定协方差(协方差一定是对称的)
    # 生成3维高斯分布样本集2
    data2 = np.random.multivariate_normal(mu2_fact, cov2_fact, 100)
    # 合并样本集,并样本结果赋值
    data = np.vstack((data1, data2))
    y = np.array([True] * 400 + [False] * 100)

    num_iter = 100          # 迭代次数
    n, d = data.shape
    mu1 = data.min(axis=0)  # 样本中的最小值
    mu2 = data.max(axis=0)  # 样本中的最大值
    sigma1 = np.identity(d) # 对角单位矩阵
    sigma2 = np.identity(d) # 对角单位矩阵
    pi = 0.5                # 类别概率

    # EM算法
    for i in range(num_iter):
        # E步
        norm1 = multivariate_normal(mu1, sigma1)    # 创建高斯分布1
        norm2 = multivariate_normal(mu2, sigma2)    # 创建高斯分布2
        tau1 = pi * norm1.pdf(data)
        tau2 = (1 - pi) * norm2.pdf(data)
        gamma = tau1 / (tau1 + tau2)                # 响应度计算

        # M步
        mu1 = np.dot(gamma, data) / np.sum(gamma)   # 高斯分布参数计算
        mu2 = np.dot((1 - gamma), data) / np.sum((1 - gamma))
        sigma1 = np.dot(gamma * (data - mu1).T, data - mu1) / np.sum(gamma)
        sigma2 = np.dot((1 - gamma) * (data - mu2).T, data - mu2) / np.sum(1 - gamma)
        pi = np.sum(gamma) / n                      # 计算类别概率
        print('第',i, "次迭代:\t mu1 - ", mu1, '\t mu2 - ', mu2)

    print('类别概率:\t', pi)
    print('均值:\t', mu1, mu2)
    print('方差:\n', sigma1, '\n\n', sigma2, '\n')

    # 计算出的高斯分布
    norm1 = multivariate_normal(mu1, sigma1)
    norm2 = multivariate_normal(mu2, sigma2)
    # 样本概率值
    tau1 = norm1.pdf(data)
    tau2 = norm2.pdf(data)

    # 类别
    order = pairwise_distances_argmin([mu1_fact, mu2_fact], [mu1, mu2], metric='euclidean')
    if order[0] == 0:
        c1 = tau1 > tau2
    else:
        c1 = tau1 < tau2
    c2 = ~c1    # c2取反,用于画图取样本点
    acc = np.mean(y == c1)
    print('准确率:%.2f%%' % (100*acc))

    # 画图略

输出结果如下:

第 0 次迭代:     mu1 -  [-0.05498014  0.05278006 -0.22147695]    mu2 -  [2.53655062 2.21992108 2.48836929]
第 1 次迭代:     mu1 -  [-0.0476418   0.07074223 -0.16462639]    mu2 -  [2.59781374 2.20706534 2.2859227 ]
第 2 次迭代:     mu1 -  [-0.05110509  0.07462042 -0.13266706]    mu2 -  [2.64701043 2.21013144 2.13833637]
第 3 次迭代:     mu1 -  [-0.05698022  0.07382559 -0.11061553]    mu2 -  [2.68221212 2.21679115 2.0194979 ]
第 4 次迭代:     mu1 -  [-0.06272106  0.07097723 -0.09449537]    mu2 -  [2.70182207 2.22314359 1.92211961]
第 5 次迭代:     mu1 -  [-0.06741194  0.06725362 -0.08260728]    mu2 -  [2.70645078 2.22713641 1.84247397]
......
第 95 次迭代:    mu1 -  [-0.08011793 -0.06035502  0.06892191]    mu2 -  [1.82861573 1.93127578 0.70517931]
第 96 次迭代:    mu1 -  [-0.08011794 -0.06035504  0.06892194]    mu2 -  [1.82861559 1.93127571 0.70517919]
第 97 次迭代:    mu1 -  [-0.08011794 -0.06035507  0.06892196]    mu2 -  [1.82861548 1.93127565 0.70517908]
第 98 次迭代:    mu1 -  [-0.08011795 -0.06035509  0.06892197]    mu2 -  [1.82861538 1.9312756  0.70517899]
第 99 次迭代:    mu1 -  [-0.08011795 -0.06035511  0.06892199]    mu2 -  [1.8286153  1.93127556 0.70517891]
类别概率:    0.7670785478701622
均值:  [-0.08011795 -0.06035511  0.06892199] [1.8286153  1.93127556 0.70517891]
方差:
 [[ 0.83206246 -0.06865792 -0.01790841]
 [-0.06865792  2.03682771 -0.03791651]
 [-0.01790841 -0.03791651  2.5827817 ]] 

 [[4.14127447 0.80620157 3.63441567]
 [0.80620157 1.85697497 1.02708058]
 [3.63441567 1.02708058 5.15539735]] 

准确率:83.40%

输出图形如下:

这里写图片描述

3.2 EM算法估算GMM的参数

使用身高、体重样本集对性别使用GaussianMixture进行聚类。代码实现如下:

import numpy as np
from sklearn.mixture import GaussianMixture
from sklearn.model_selection import train_test_split
import matplotlib as mpl
import matplotlib.colors
import matplotlib.pyplot as plt

if __name__ == '__main__':
    # 加载数据
    data = np.loadtxt('.\\HeightWeight.csv', dtype=np.float, delimiter=',', skiprows=1)
    print('数据集大小:',data.shape)
    # 数据划分
    y, x = np.split(data, [1, ], axis=1)
    x, x_test, y, y_test = train_test_split(x, y, test_size=0.4, random_state=0)

    # 高斯混合模型:两个簇,方差类型full
    gmm = GaussianMixture(n_components=2, covariance_type='full')
    gmm.fit(x)  # 训练
    print('均值: \n', gmm.means_)
    print('方差: \n', gmm.covariances_)
    y_hat = gmm.predict(x)              # 训练集预测
    y_test_hat = gmm.predict(x_test)    # 测试集预测

    # 计算准确率
    acc = np.mean(y_hat.ravel() == y.ravel())
    acc_test = np.mean(y_test_hat.ravel() == y_test.ravel())
    print('训练集准确率:%.2f%%' % (acc * 100))
    print('测试集准确率:%.2f%%' % (acc_test * 100))

    # 若均值相反,则样本结果互换
    change = (gmm.means_[0][0] > gmm.means_[1][0])
    if change:
        z = y_hat == 0
        y_hat[z] = 1
        y_hat[~z] = 0
        z = y_test_hat == 0
        y_test_hat[z] = 1
        y_test_hat[~z] = 0

    # 预测后验概率,画后验概率等高线
    p = gmm.predict_proba(grid_test)
    print('后验概率:', p)
    p = p[:, 0].reshape(x1.shape)
    CS = plt.contour(x1, x2, p, levels=(0.1, 0.5, 0.8), colors=list('rgb'), linewidths=2)
    plt.clabel(CS, fontsize=12, fmt='%.1f', inline=True)

    # 画图略

输出结果如下:

数据集大小: (114, 3)
均值: 
 [[160.13983374  55.93370575]
 [173.50243688  65.03359308]]
方差: 
 [[[ 18.82128194  12.30370549]
  [ 12.30370549  31.23596113]]

 [[ 23.22794989  28.48688647]
  [ 28.48688647 105.81824734]]]
训练集准确率:77.94%
测试集准确率:82.61%
后验概率: [[9.99997750e-01 2.25031842e-06]
 [9.99997839e-01 2.16136597e-06]
 [9.99997923e-01 2.07669097e-06]
 ...
 [6.34944402e-11 1.00000000e+00]
 [5.78303161e-11 1.00000000e+00]
 [5.26521608e-11 1.00000000e+00]]

输出图形如下:

这里写图片描述

3.3 GMM调参

生成两个二维高斯分布样本集,使用GaussianMixture聚类。代码实现如下:

import numpy as np
from sklearn.mixture import GaussianMixture
import matplotlib as mpl
import matplotlib.colors
import matplotlib.pyplot as plt

if __name__ == '__main__':
    np.random.seed(0)       # 撒固定的种子,保证每次样本集数据相同
    cov1 = np.diag((1, 2))  # 分布的协方差矩阵,对角阵
    N1 = 500; N2 = 300      # 样本数量
    N = N1 + N2
    # 生成2维高斯分布样本集1,均值点为(1, 2)
    x1 = np.random.multivariate_normal(mean=(1, 2), cov=cov1, size=N1)
    # 样本集1变换
    m = np.array(((1, 1), (1, 3)))
    x1 = x1.dot(m)
    # 生成2维高斯分布样本集2,均值点为(-1, 10)
    x2 = np.random.multivariate_normal(mean=(-1, 10), cov=cov1, size=N2)
    # 合并样本集,并样本结果赋值
    x = np.vstack((x1, x2))
    y = np.array([0]*N1 + [1]*N2)

    # 高斯混合模型,方差类型集合
    types = ('spherical', 'diag', 'tied', 'full')
    err = np.empty(len(types))  # 错误率集
    bic = np.empty(len(types))  # BIC集
    # 遍历方差类型训练高斯混合模型
    for i, type in enumerate(types):
        # 高斯混合模型:两个簇,方差类型type
        gmm = GaussianMixture(n_components=2, covariance_type=type)
        gmm.fit(x)          # 训练
        err[i] = 1 - accuracy_rate(gmm.predict(x), y)   # 错误率,越小越优
        bic[i] = gmm.bic(x) # BIC值,越小越优
    print('错误率:', err.ravel())
    print('BIC:', bic.ravel())

    # 找到最优BIC的方差类型
    optimal = bic.argmin()
    gmm = GaussianMixture(n_components=2, covariance_type=types[optimal])
    gmm.fit(x)              # 训练
    print('均值 = \n', gmm.means_)
    print('方差 = \n', gmm.covariances_)
    y_hat = gmm.predict(x)  # 预测

    # 不同方差类型的误差率和BIC直方图
    # 画图略

输出结果如下:

错误率: [0.385   0.315   0.3     0.00125]
BIC: [7990.62231203 7855.56050855 8006.49834359 6845.79374805]
均值 = 
 [[-0.97642254 10.06927801]
 [ 2.88444448  6.69484552]]
方差 = 
 [[[ 0.91302546 -0.04298504]
  [-0.04298504  1.9603531 ]]

 [[ 2.87015473  6.64421303]
  [ 6.64421303 18.00318872]]]

输出图形如下:

这里写图片描述

这里写图片描述

3.4 GMM无监督分类鸢尾花数据

使用GaussianMixture无监督分类鸢尾花数据。代码实现如下:

import numpy as np
import pandas as pd
from sklearn.mixture import GaussianMixture
import matplotlib as mpl
import matplotlib.colors
import matplotlib.pyplot as plt
from sklearn.metrics.pairwise import pairwise_distances_argmin

if __name__ == '__main__':
    path = '.\\iris.data'                   # 数据文件路径
    data = pd.read_csv(path, header=None)   # 读取数据
    x_prime = data[np.arange(4)]            # 取特征值
    y = pd.Categorical(data[4]).codes       # 取标签值

    # 簇的大小
    n_components = 3
    # 特征值分为6组
    feature_pairs = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
    plt.figure(figsize=(8, 6), facecolor='w')
    for k, pair in enumerate(feature_pairs, start=1):
        x = x_prime[pair]
        m = np.array([np.mean(x[y == i], axis=0) for i in range(3)])  # 均值的实际值
        print('实际均值 = \n', m)

        # 高斯混合模型:3个簇,方差类型full
        gmm = GaussianMixture(n_components=n_components, covariance_type='full', random_state=0)
        gmm.fit(x)              # 训练
        print('预测均值 = \n', gmm.means_)
        print('预测方差 = \n', gmm.covariances_)
        y_hat = gmm.predict(x)  # 预测
        order = pairwise_distances_argmin(m, gmm.means_, axis=1, metric='euclidean')
        print('顺序:\t', order)

        # 变换顺序
        n_sample = y.size
        change = np.empty((n_components, n_sample), dtype=np.bool)
        for i in range(n_components):
            change[i] = y_hat == order[i]
        for i in range(n_components):
            y_hat[change[i]] = i
        acc = '准确率:%.2f%%' % (100*np.mean(y_hat == y))
        print(acc)

        # 画图略

输出结果如下:

实际均值 = 
 [[5.006 3.418]
 [5.936 2.77 ]
 [6.588 2.974]]
预测均值 = 
 [[5.01494511 3.44040237]
 [6.69225795 3.03018616]
 [5.90652226 2.74740414]]
预测方差 = 
 [[[0.11948421 0.08969613]
  [0.08969613 0.12149899]]

 [[0.3588512  0.05091598]
  [0.05091598 0.08956947]]

 [[0.27590209 0.08910477]
  [0.08910477 0.09414053]]]
顺序:  [0 2 1]
准确率:78.67%
实际均值 = 
 [[5.006 1.464]
 [5.936 4.26 ]
 [6.588 5.552]]
预测均值 = 
 [[5.0060006  1.46399865]
 [6.04240777 4.41742864]
 [6.58888904 5.63329718]]
预测方差 = 
 [[[0.12176525 0.01581631]
  [0.01581631 0.0295045 ]]

 [[0.28119672 0.23746926]
  [0.23746926 0.31503012]]

 [[0.48521779 0.36602418]
  [0.36602418 0.32601109]]]
顺序:  [0 1 2]
准确率:91.33%
实际均值 = 
 [[5.006 0.244]
 [5.936 1.326]
 [6.588 2.026]]
预测均值 = 
 [[5.00605757 0.23727675]
 [6.57289666 2.05192938]
 [5.977111   1.33910201]]
预测方差 = 
 [[[0.12407758 0.01055895]
  [0.01055895 0.00905628]]

 [[0.4171415  0.05278455]
  [0.05278455 0.06790735]]

 [[0.30671183 0.08640131]
  [0.08640131 0.05609398]]]
顺序:  [0 2 1]
准确率:96.00%
实际均值 = 
 [[3.418 1.464]
 [2.77  4.26 ]
 [2.974 5.552]]
预测均值 = 
 [[3.41800009 1.46400001]
 [2.97017899 5.56073357]
 [2.80062882 4.43004172]]
预测方差 = 
 [[[0.14227691 0.01144799]
  [0.01144799 0.029505  ]]

 [[0.11477629 0.07760424]
  [0.07760424 0.38871245]]

 [[0.09376548 0.10702236]
  [0.10702236 0.34454954]]]
顺序:  [0 2 1]
准确率:92.67%
实际均值 = 
 [[3.418 0.244]
 [2.77  1.326]
 [2.974 2.026]]
预测均值 = 
 [[3.41800003 0.244     ]
 [2.93629236 1.98607968]
 [2.79657869 1.31224583]]
预测方差 = 
 [[[0.14227697 0.011208  ]
  [0.011208   0.011265  ]]

 [[0.11263095 0.06192916]
  [0.06192916 0.08966439]]

 [[0.09554395 0.04869984]
  [0.04869984 0.03787478]]]
顺序:  [0 2 1]
准确率:93.33%
实际均值 = 
 [[1.464 0.244]
 [4.26  1.326]
 [5.552 2.026]]
预测均值 = 
 [[1.4639995  0.24399977]
 [5.57721357 2.04303223]
 [4.30594389 1.34787855]]
预测方差 = 
 [[[0.02950483 0.00558393]
  [0.00558393 0.01126496]]

 [[0.30034404 0.04402642]
  [0.04402642 0.07200287]]

 [[0.24667106 0.08489917]
  [0.08489917 0.04585074]]]
顺序:  [0 2 1]
准确率:97.33%

输出图形如下:

这里写图片描述

对比学习笔记【机器学习重点与实战】——4 集成学习-Bagging学习笔记【机器学习重点与实战】——4 集成学习-Bagging中分别使用随机森林和AdaBoost对鸢尾花数据有监督学习的训练结果,可知使用GMM无监督分类鸢尾花效果也还不错。

4 参考

  1. 《统计学习方法》第9章 EM算法及其推广
  2. 《机器学习 - 周志华》7.6 EM算法

===========文档信息============
学习笔记由博主整理编辑,供非商用学习交流用
如本文涉及侵权,请随时留言博主,必妥善处置
版权声明:非商用自由转载-保持署名-注明出处
署名(BY) :dkjkls(dkj卡洛斯)
文章出处:http://blog.csdn.net/dkjkls

猜你喜欢

转载自blog.csdn.net/dkjkls/article/details/80301697
今日推荐