⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议。
Skip to content

第9章 EM算法及其推广

习题9.1

  如例9.1的三硬币模型,假设观测数据不变,试选择不同的初值,例如,π(0)=0.46,p(0)=0.55,q(0)=0.67,求模型参数为θ=(π,p,q)的极大似然估计。

解答:

解答思路:

  1. 列出例9.1的三硬币模型;
  2. 写出三硬币模型的EM算法;
  3. 根据上述EM算法,编写代码,并求出模型参数的极大似然估计。

解答步骤:

第1步:例9.1的三硬币模型

  根据书中第9章的例9.1(三硬币模型):

  例9.1(三硬币模型) 假设有3枚硬币,分别记作A,B,C。这些硬币正面出现的概率分别是πpq。进行如下掷硬币试验:先掷硬币A,根据其结果选出硬币B或硬币C,正面选硬币B,反面选硬币C;然后掷选出的硬币,掷硬币的结果,出现正面记作1,出现方面记作0;独立地重复n次试验(这里,n=10),观测结果如下:

1,1,0,1,0,0,1,0,1,1

假设只能观测到掷硬币的结果,不能观测掷硬币的过程。

  三硬币模型可以写作

P(y|θ)=zP(y,z|θ)=zP(z|θ)P(y|z,θ)=πpy(1p)1y+(1π)qy(1q)1y

这里:

  1. 随机变量y是观测变量,表示一次试验观测的结果是1或0;
  2. 随机变量z是隐变量,表示未观测到的掷硬币A的结果;
  3. θ=(π,p,q)是模型参数。

第2步:三硬币模型的EM算法

  根据书中第9章的例9.1的三硬币模型的EM算法:

  EM算法首先选取参数的初值,记作θ(0)=(π(0),p(0),q(0)),然后通过下面的步骤迭代计算参数的估计值,直至收敛为止。第i次迭代参数的估计值为θ(i)=(π(i),p(i),q(i))。EM算法的第i+1次迭代如下:

  E步:计算在模型参数π(i),p(i),q(i)下观测数据yj来自掷硬币B的概率

μj(i+1)=π(i)(p(i))yj(1p(i))1yjπ(i)(p(i))yj(1p(i))1yj+(1π(i))(q(i))yj(1q(i))1yj

  M步:计算模型参数的新估计值

π(i+1)=1nj=1Nμj(i+1)p(i+1)=j=1nμj(i+1)yjj=1nμj(i+1)q(i+1)=j=1n(1μj(i+1))yjj=1n(1μj(i+1))

第3步:编写代码并求出模型参数的极大似然估计

python
import math


class ThreeCoinEM:
    def __init__(self, prob, tol=1e-6, max_iter=1000):
        """
        初始化模型参数
        :param prob: 模型参数的初值
        :param tol: 收敛阈值
        :param max_iter: 最大迭代次数
        """
        self.prob_A, self.prob_B, self.prob_C = prob
        self.tol = tol
        self.max_iter = max_iter

    def calc_mu(self, j):
        """
        (E步)计算mu
        :param j: 观测数据y的第j个
        :return: 在模型参数下观测数据yj来自掷硬币B的概率
        """
        # 掷硬币A观测结果为正面
        pro_1 = self.prob_A * \
            math.pow(self.prob_B, data[j]) * \
            math.pow((1 - self.prob_B), 1 - data[j])
        # 掷硬币A观测结果为反面
        pro_2 = (1 - self.prob_A) * math.pow(self.prob_C,
                                             data[j]) * math.pow((1 - self.prob_C), 1 - data[j])
        return pro_1 / (pro_1 + pro_2)

    def fit(self, data):
        count = len(data)
        print("模型参数的初值:")
        print("prob_A={}, prob_B={}, prob_C={}".format(
            self.prob_A, self.prob_B, self.prob_C))
        print("EM算法训练过程:")
        for i in range(self.max_iter):
            # (E步)得到在模型参数下观测数据yj来自掷硬币B的概率
            _mu = [self.calc_mu(j) for j in range(count)]
            # (M步)计算模型参数的新估计值
            prob_A = 1 / count * sum(_mu)
            prob_B = sum([_mu[k] * data[k] for k in range(count)]) \
                / sum([_mu[k] for k in range(count)])
            prob_C = sum([(1 - _mu[k]) * data[k] for k in range(count)]) \
                / sum([(1 - _mu[k]) for k in range(count)])
            print('第{}次:prob_A={:.4f}, prob_B={:.4f}, prob_C={:.4f}'.format(
                i + 1, prob_A, prob_B, prob_C))
            # 计算误差值
            error = abs(self.prob_A - prob_A) + \
                abs(self.prob_B - prob_B) + abs(self.prob_C - prob_C)
            self.prob_A = prob_A
            self.prob_B = prob_B
            self.prob_C = prob_C
            # 判断是否收敛
            if error < self.tol:
                print("模型参数的极大似然估计:")
                print("prob_A={:.4f}, prob_B={:.4f}, prob_C={:.4f}".format(self.prob_A, self.prob_B,
                                                                           self.prob_C))
                break
python
# 加载数据
data = [1, 1, 0, 1, 0, 0, 1, 0, 1, 1]
# 模型参数的初值
init_prob = [0.46, 0.55, 0.67]

# 三硬币模型的EM模型
em = ThreeCoinEM(prob=init_prob, tol=1e-5, max_iter=100)
# 模型训练
em.fit(data)
模型参数的初值:
prob_A=0.46, prob_B=0.55, prob_C=0.67
EM算法训练过程:
第1次:prob_A=0.4619, prob_B=0.5346, prob_C=0.6561
第2次:prob_A=0.4619, prob_B=0.5346, prob_C=0.6561
模型参数的极大似然估计:
prob_A=0.4619, prob_B=0.5346, prob_C=0.6561

  可见通过两次迭代,模型参数已经收敛,三硬币正面出现的概率分别为0.4619,0.5346,0.6561

习题9.2

证明引理9.2。

解答:

解答思路:

  1. 写出需要证明的引理9.2;
  2. 列出F函数定义;
  3. 根据引理9.1,进行公式推导;
  4. 根据约束条件zP~θ(Z)=1,可证明引理9.2。

解答步骤:

第1步:需要证明的引理9.2

  根据书中第9.4.1节的引理9.2:

  引理9.2P~θ(Z)=P(Z|Y,θ),则

(9.36)F(P~,θ)=logP(Y|θ)

第2步:F函数定义

  根据书中第9章的定义9.3的F函数定义:

  定义9.3(F函数) 假设隐变量数据Z的概率分布为P~(Z),定义分布P~与参数θ的函数F(P~,θ)如下:

(9.33)F(P~,θ)=EP~[logP(Y,Z|θ)]+H(P~)

称为F函数。式中H(P~)=EP~logP~(Z)是分布P~(Z)的熵。

第3步:引理9.1

  根据书中第9章的引理9.1:

  引理9.1 对于固定的θ,存在唯一的分布P~θ极大化F(P~,θ),这时P~θ由下式给出:

(9.34)P~θ(Z)=P(Z|Y,θ)

并且P~θθ连续变化。

$ \begin{aligned} \therefore F(\tilde{P}, \theta) &= E_{\tilde{P}}[\log P(Y, Z|\theta)] + H(\tilde{P}) \ &= E_{\tilde{P}}[\log P(Y,Z|\theta)] -E_{\tilde{P}} \log \tilde{P}(Z) \quad (F函数定义:H(\tilde{P}) = - E_{\tilde{P}} \log \tilde{P}(Z))\ &= \sum_Z \log P(Y,Z|\theta) \tilde{P}_{\theta}(Z) - \sum_Z \log \tilde{P}(Z) \cdot \tilde{P}(Z) \end{aligned} $

根据引理9.1:P~θ(Z)=P(Z|Y,θ)

F(P~,θ)=ZlogP(Y,Z|θ)P~θ(Z)ZlogP~(Z)P~(Z)=ZlogP(Y,Z|θ)P(Z|Y,θ)ZlogP(Z|Y,θ)P(Z|Y,θ)=ZP(Z|Y,θ)[logP(Y,Z|θ)logP(Z|Y,θ)]=ZP(Z|Y,θ)logP(Y,Z|θ)P(Z|Y,θ)=ZP(Z|Y,θ)logP(Y|θ)=logP(Y|θ)ZP(Z|Y,θ)

第4步:根据引理9.1,得证

根据引理9.1,可知:ZP(Z|Y,θ)=ZP~θ(Z)=1

F(P~,θ)=logP(Y|θ),引理9.2得证。

习题9.3

已知观测数据 -67,-48,6,8,14,16,23,24,28,29,41,49,56,60,75,试估计两个分量的高斯混合模型的5个参数。

解答:

解答思路:

  两个分量的高斯混合模型一共有6个参数μ1,μ2,σ1,σ2,α1,α2,其中α2可由α2=1α1得到,故仅估计5个参数即可。

  1. 写出高斯混合模型;
  2. 写出高斯混合模型参数估计的EM算法;
  3. 采用sklearn的GaussianMixture计算6个参数;
  4. 采用自编程实现高斯混合模型的EM算法。

解答步骤:

第1步:高斯混合模型

  根据书中第9章的定义9.2的高斯混合模型:

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

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

其中,αk是系数,αk0k=1Kαk=1ϕ(y|θ)是高斯分布密度,θk=(uk,σk2)

ϕ(y|θk)=12πσkexp((yμk)22σk2)

称为第k个分模型。

从上述描述中可知,如果是2个高斯混合分模型,一共需要估计的参数有6个μ1,μ2,σ1,σ2,α1,α2,其中α1+α2=1

第2步:高斯混合模型参数估计的EM算法

  根据书中第9章的算法9.2:

算法9.2(高斯混合模型参数估计的EM算法)
输入:观测数据y1,y2,,yN,高斯混合模型;
输出:高斯混合模型参数。
(1)取参数的初始值开始迭代;
(2)E步:依据当前模型参数,计算分模型k对观测数据yi的响应度

γ^jk=αkϕ(yj|θk)k=1Kαkϕ(yj|θk),j=1,2,,N;k=1,2,,K

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

u^k=j=1Nγ^jkyjj=1Nγ^jk,k=1,2,,Kσ^k2=j=1Nγ^jk(yjuk)2j=1Nγ^jk,k=1,2,,Kα^k=j=1Nγ^jkN,k=1,2,,K

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

第3步:采用sklearn的GaussianMixture计算6个参数

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

# 初始化观测数据
data = np.array([-67, -48, 6, 8, 14, 16, 23, 24, 28,
                29, 41, 49, 56, 60, 75]).reshape(-1, 1)

# 设置n_components=2,表示两个分量高斯混合模型
gmm_model = GaussianMixture(n_components=2)
# 对模型进行参数估计
gmm_model.fit(data)
# 对数据进行聚类
labels = gmm_model.predict(data)

# 得到分类结果
print("分类结果:labels = {}\n".format(labels))
print("两个分量高斯混合模型的6个参数如下:")
# 得到参数u1,u2
print("means =", gmm_model.means_.reshape(1, -1))
# 得到参数sigma1, sigma1
print("covariances =", gmm_model.covariances_.reshape(1, -1))
# 得到参数a1, a2
print("weights = ", gmm_model.weights_.reshape(1, -1))
分类结果:labels = [0 0 1 1 1 1 1 1 1 1 1 1 1 1 1]

两个分量高斯混合模型的6个参数如下:
means = [[-57.51107027  32.98489643]]
covariances = [[ 90.24987882 429.45764867]]
weights =  [[0.13317238 0.86682762]]
python
# 绘制观测数据的聚类情况
for i in range(0, len(labels)):
    if labels[i] == 0:
        plt.scatter(i, data.take(i), s=15, c='red')
    elif labels[i] == 1:
        plt.scatter(i, data.take(i), s=15, c='blue')
plt.title('Gaussian Mixture Model')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

png

第4步:自编程实现高斯混合模型的EM算法

python
import numpy as np
import itertools


class MyGMM:
    def __init__(self, alphas_init, means_init, covariances_init, tol=1e-6, n_components=2, max_iter=50):
        # (1)设置参数的初始值
        # 分模型权重
        self.alpha_ = np.array(
            alphas_init, dtype="float16").reshape(n_components, 1)
        # 分模型均值
        self.mean_ = np.array(
            means_init, dtype="float16").reshape(n_components, 1)
        # 分模型标准差(方差的平方)
        self.covariances_ = np.array(
            covariances_init, dtype="float16").reshape(n_components, 1)
        # 迭代停止的阈值
        self.tol = tol
        # 高斯混合模型分量个数
        self.K = n_components
        # 最大迭代次数
        self.max_iter = max_iter
        # 观测数据
        self._y = None
        # 实际迭代次数
        self.n_iter_ = 0

    def gaussian(self, mean, convariances):
        """计算高斯分布概率密度"""
        return 1 / np.sqrt(2 * np.pi * convariances) * np.exp(
            -(self._y - mean) ** 2 / (2 * convariances))

    def update_r(self, mean, convariances, alpha):
        """更新r_jk 分模型k对观测数据yi的响应度"""
        r_jk = alpha * self.gaussian(mean, convariances)
        return r_jk / r_jk.sum(axis=0)

    def update_params(self, r):
        """更新u al si 每个分模型k的均值、权重、方差"""
        u = self.mean_[-1]
        _mean = ((r * self._y).sum(axis=1) / r.sum(axis=1)).reshape(self.K, 1)
        _covariances = ((r * (self._y - u) ** 2).sum(axis=1) /
                        r.sum(axis=1)).reshape(self.K, 1)
        _alpha = (r.sum(axis=1) / self._y.size).reshape(self.K, 1)
        return _mean, _covariances, _alpha

    def judge_stop(self, mean, covariances, alpha):
        """中止条件判断"""
        a = np.linalg.norm(self.mean_ - mean)
        b = np.linalg.norm(self.covariances_ - covariances)
        c = np.linalg.norm(self.alpha_ - alpha)
        return True if np.sqrt(a ** 2 + b ** 2 + c ** 2) < self.tol else False

    def fit(self, y):
        self._y = np.copy(np.array(y))
        """迭代训练获得预估参数"""
        # (2)E步:计算分模型k对观测数据yi的响应度
        r = self.update_r(self.mean_, self.covariances_, self.alpha_)
        # 更新r_jk 分模型k对观测数据yi的响应度
        _mean, _covariances, _alpha = self.update_params(r)
        # 更新u al si 每个分模型k的均值、权重、方差
        for i in range(self.max_iter):
            if not self.judge_stop(_mean, _covariances, _alpha):
                # (4)未达到阈值条件,重复迭代
                r = self.update_r(_mean, _covariances, _alpha)
                # (3)M步:计算新一轮迭代的模型参数
                _mean, _covariances, _alpha = self.update_params(r)
            else:
                # 达到阈值条件,停止迭代
                self.n_iter_ = i
                break

            self.mean_ = _mean
            self.covariances_ = _covariances
            self.alpha_ = _alpha

    def score(self):
        """计算该局部最优解的score,即似然函数值"""
        return (self.alpha_ * self.gaussian(self.mean_, self.covariances_)).sum()
python
# 观测数据
y = np.array([-67, -48, 6, 8, 14, 16, 23, 24, 28,
             29, 41, 49, 56, 60, 75]).reshape(1, 15)
# 预估均值和方差,以其邻域划分寻优范围
y_mean = y.mean() // 1
y_std = (y.std() ** 2) // 1

# 网格搜索,对不同的初值进行参数估计
alpha = [[i, 1 - i] for i in np.linspace(0.1, 0.9, 9)]
mean = [[y_mean + i, y_mean + j]
        for i in range(-10, 10, 5) for j in range(-10, 10, 5)]
covariances = [[y_std + i, y_std + j]
               for i in range(-1000, 1000, 500) for j in range(-1000, 1000, 500)]
results = []
for i in itertools.product(alpha, mean, covariances):
    init_alpha = i[0]
    init_mean = i[1]
    init_covariances = i[2]
    clf = MyGMM(alphas_init=init_alpha, means_init=init_mean, covariances_init=init_covariances,
                n_components=2, tol=1e-6)
    clf.fit(y)
    # 得到不同初值收敛的局部最优解
    results.append([clf.alpha_, clf.mean_, clf.covariances_, clf.score()])
# 根据score,从所有局部最优解找到相对最优解
best_value = max(results, key=lambda x: x[3])

print("alpha : {}".format(best_value[0].T))
print("mean : {}".format(best_value[1].T))
print("std : {}".format(best_value[2].T))
alpha : [[0.56950675 0.43049325]]
mean : [[27.41762854 12.35515017]]
std : [[ 268.17311145 2772.33989897]]

习题9.4

  EM算法可以用到朴素贝叶斯法的非监督学习,试写出其算法。

解答:

解答思路:
参考: http://www.cs.columbia.edu/~mcollins/em.pdf

  1. 列出EM算法;
  2. 列出朴素贝叶斯算法;
  3. 推导朴素贝叶斯的EM算法。

解答步骤:

第1步:EM算法

  根据书中第9章的算法9.1的EM算法:

算法9.1(EM算法)
输入:观测变量数据Y,隐变量数据Z,联合分布P(Y,Z|θ),条件分布P(Z|Y,θ)
输出:模型参数θ
(1)选择参数的初值θ(0),开始迭代;
(2)E步:记θ(i)为第i次迭代参数θ的估计值,在第i+1次迭代的E步,计算

Q(θ,θ(i))=EZ[logP(Y,Z|θ)|Y,θ(i)]=zlogP(Y,Z|θ)P(Z|Y,θ(i))

这里,P(Z|Y,θ)是在给定观测数据Y和当前的参数估计θ(i)下隐变量数据Z的条件概率分布;
(3)M步:求使Q(θ,θ(i))极大化的θ,确定第i+1次迭代的参数的估计值θ(i+1)

θ(i+1)=argmaxθQ(θ,θ(i))

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

第2步:朴素贝叶斯算法

  根据书中第4章的算法4.1的朴素贝叶斯算法:

算法4.1(朴素贝叶斯算法)
输入:训练数据T=(x1,y1),(x2,y2),,(xN,yN),其中xi=(xi(1),xi(2),,xi(n))Txi(j)是第i个样本的第j个特征,xi(j){aj1,aj2,,ajSj}ajl是第j个特征可能取的第l个值,j=1,2,,nl=1,2,,Sjyi{c1,c2,,cK};实例x
输出:实例x的分类。
(1)计算先验概率及条件概率

P(Y=ck)=i=1NI(yi=ck)N,k=1,2,,KP(X(j)=ajl|Y=ck)=i=1NI(xi(j)=ajl,yi=ck)i=1NI(yi=ck)j=1,2,,n;l=1,2,,Sj;k=1,2,,K

(2)对于给定的实例x=(x(1),x(2),,x(n))T,计算

P(Y=ck)j=1nP(X(j)=x(j)|Y=ck),k=1,2,,K

(3)确定实例x的类

y=argmaxckP(Y=ck)j=1nP(X(j)=x(j)|Y=ck)

第3步:推导朴素贝叶斯的EM算法

推导思路:

  1. 假设隐变量数据是yY={c1,c2,,cK}
  2. 设置初值,P(0)(Y=y)0Pj(0)(X=x|Y=y)0,其中j=1,2,,n,满足
yYP(0)(Y=y)=1x{1,+1}Pj(0)(X=x|Y=y)=1
  1. 根据概率公式,可知概率
δ(y|i)=P(Y=y|X=xi,θ(t))=P(t)(Y=y)j=1nPj(t)(X=xi(j)|Y=y)yYP(t)(Y=y)j=1nPj(t)(X=xi(j)|Y=y)

其中θ表示朴素贝叶斯模型中所有的参数向量 4. 迭代更新参数

P(t+1)(Y=y)=1Ni=1Nδ(y|i)Pj(t+1)(X=xi(j)|y)=i=1NP(X=xi(j))δ(y|i)i=1Nδ(y|i)
  1. 计算似然函数,得到使得似然函数最大的θ,重复第3步和第4步,直至收敛
θ=argmaxθΩL(θ)=argmaxθΩi=1NyYδ(y|i)log(P(Y=y)j=1nPj(X=xi(j)|Y=y))

所以,朴素贝叶斯的EM算法如下:

输入:隐变量数据是yY={c1,c2,,cK}xX=(x1,x2,,xN),输入空间XRnn维向量的集合,x=(x(1),x(2),,x(n))Tx(i)取值范围是{1,+1}
输出:参数P(t+1)(Y=y)Pj(t+1)(X=xi(j)|y)
(1)选择参数的初值P(0)(Y=y)0Pj(0)(X=x|Y=y)0,开始迭代;
(2)E步:记θ(t)为第t次迭代参数θ的估计值,在第t+1次迭代的E步,计算

δ(y|i)=P(Y=y|X=xi,θ(t))=P(t)(Y=y)j=1nPj(t)(X=xi(j)|Y=y)yYP(t)(Y=y)j=1nPj(t)(X=xi(j)|Y=y)

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

P(t+1)(Y=y)=1Ni=1Nδ(y|i)Pj(t+1)(X=xi(j)|y)=i=1NP(X=xi(j))δ(y|i)i=1Nδ(y|i)

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