期望最大化算法

期望最大化算法

  • 问题场景
  • 算法思路

期望最大化算法(Expectation Maximization,EM) 是一种基于不完整、包含隐变量观测数据进行统计模型参数估计的方法。

我们知道,统计模型中的参数都需要根据观测数据集(训练数据)来进行估计。但是,在有些场景下,观测数据集中包含的信息不完整,有缺失,此时就不太容易去估计相应的参数。EM 算法就是针对这种问题的的方法。

问题场景

桌子上放着一个盒子,其中有两种类型的硬币 A A A 和$ B$。随机从盒子中抓取硬币是 A A A 的概率为 τ \tau τ, 硬币 A A A B B B 正面朝上的概率分别是 p 1 p_1 p1 p 2 p_2 p2。接下来,估计 p 1 p_1 p1 p 2 p_2 p2 τ \tau τ的值。

假设得到的完整的观测数据如下表,1 表示正面朝上,0 表示反面朝上。

数据 硬币
1 A
0 A
1 B
1 A
0 B

估计参数:

  • τ = 3 5 \tau=\dfrac{3}{5} τ=53
  • p 1 = 2 3 p_1=\dfrac{2}{3} p1=32
  • p 2 = 1 2 p_2=\dfrac{1}{2} p2=21
数据 硬币 A 概率 硬币 B 概率
1 0.6 0.4
0 0.4 0.6
1 0.6 0.4
1 0.6 0.4
0 0.4 0.6

估计参数

  • τ = ( 0.6 × 3 + 0.4 × 2 ) / 5 = 0.52 \tau=(0.6 \times 3 + 0.4 \times 2) / 5=0.52 τ=(0.6×3+0.4×2)/5=0.52
  • p 1 = ( 0.6 × 1 + 0.4 × 0 + 0.6 × 1 + 0.6 × 1 + 0.4 × 0 ) / ( 0.6 + 0.4 + 0.6 + 0.6 + 0.4 ) = 0.6923 p_1=(0.6 \times 1 + 0.4 \times 0 + 0.6 \times 1 + 0.6 \times 1 + 0.4 \times 0 ) / (0.6 + 0.4 + 0.6 + 0.6 + 0.4) = 0.6923 p1=(0.6×1+0.4×0+0.6×1+0.6×1+0.4×0)/(0.6+0.4+0.6+0.6+0.4)=0.6923
  • p 2 = ( 0.4 × 1 + 0.6 × 0 + 0.4 × 1 + 0.4 × 1 + 0.6 × 0 ) / ( 0.4 + 0.6 + 0.4 + 0.4 + 0.6 ) = 0.5000 p_2=(0.4 \times 1 + 0.6 \times 0 +0.4 \times 1 + 0.4 \times 1 + 0.6 \times 0)/(0.4+0.6+0.4+0.4+0.6)=0.5000 p2=(0.4×1+0.6×0+0.4×1+0.4×1+0.6×0)/(0.4+0.6+0.4+0.4+0.6)=0.5000

假设:每次不知道抛掷的是哪个硬币、也不知道可能硬币的概率,如下表所示:

数据 硬币 硬币 A 概率 硬币 B 概率
1 ?
0 ?
1 ?
1 ?
0 ?

我们需要在包含缺失信息、隐藏变量的观测数据中去估计 p 1 p_1 p1 p 2 p_2 p2 τ \tau τ 这三个参数。这样的问题有个特点:

  • 想估计参数值,必须知道隐藏变量的信息
  • 想知道隐藏变量的信息,必须知道参数值

算法思路

对于这样的问题,我们可以假设一组参数,并在此参数的基础上,根据观测数据去估计隐藏变量的分布信息,然后再根据新的隐藏变量信息反过来去更新参数。这个过程可以反复进行,但是什么时候停止这个循环的过程?

我们可以设定一个条件,观测数据的对数似然值最大。即:当 p 1 p_1 p1 p 2 p_2 p2 τ \tau τ 是什么值的时候,这组观测数据出现的可性能最大。例如:

数据 硬币 A 硬币 B
1 τ * p1 (1 – τ ) * p2
0 τ * (1 – p1) (1 – τ ) * (1 – p2)
1 τ * p1 (1 – τ ) * p2
1 τ * p1 (1 – τ ) * p2
0 τ * (1 – p1) (1 – τ ) * (1 – p2)

每个样本的对数似然值计算公式:

ℓ ( x t ) = log ⁡ ( τ ⋅ p 1 x t ( 1 − p 1 ) 1 − x t + ( 1 − τ ) ⋅ p 2 x t ( 1 − p 2 ) 1 − x t ) \ell(x_t) = \log\left(\tau \cdot p_1^{x_t}(1-p_1)^{1-x_t} + (1-\tau) \cdot p_2^{x_t}(1-p_2)^{1-x_t}\right) (xt)=log(τp1xt(1p1)1xt+(1τ)p2xt(1p2)1xt)

其中:

  • x t x_t xt是第 t t t次实验的结果,取值为1(正面)或0(反面)。
  • τ \tau τ是选择硬币A的概率
  • p 1 p_1 p1是硬币A正面朝上的概率
  • p 2 p_2 p2是硬币B正面朝上的概率

上面提到的解决思路就是 EM 算法的思想:

  1. 根据一定的策略对参数进行初始化(当然不准)
  2. E 步(Expectation Step):基于当前参数估计值,计算每个观测数据属于不同类别的概率;
  3. M 步(Maximization Step):基于 E 步中的计算结果,更新参数值,最大化对数似然函数。

E 步:

数据 硬币 A 概率 硬币 B 概率
1 γ 0 A = τ p 1 τ p 1 + ( 1 − τ ) p 2 \gamma_{0}^{A}=\dfrac{\tau p_1}{\tau p_1+(1-\tau)p_2} γ0A=τp1+(1τ)p2τp1 γ 0 B = 1 − γ 0 A \gamma_{0}^{B}=1-\gamma_{0}^{A} γ0B=1γ0A
0 γ 1 A = τ ( 1 − p 1 ) τ ( 1 − p 1 ) + ( 1 − τ ) ( 1 − p 2 ) \gamma_{1}^{A}=\dfrac{\tau (1-p_1)}{\tau(1 -p_1)+(1-\tau)(1-p_2)} γ1A=τ(1p1)+(1τ)(1p2)τ(1p1) γ 1 B = 1 − γ 1 A \gamma_{1}^{B}=1-\gamma_{1}^{A} γ1B=1γ1A
1
1
0

M 步:

  1. 更新 τ \tau τ(硬币A选择概率)

    τ = 1 N ∑ i = 1 N γ i A \tau=\dfrac{1}{N}\displaystyle \sum_{i=1}^{N}\gamma_{i}^{A} τ=N1i=1NγiA

  2. 更新 p 1 p_1 p1(硬币A正面朝上的概率)

    p 1 = ∑ i = 1 N γ i A ⋅ x i ∑ i = 1 N γ i A \displaystyle p_1=\dfrac{\sum^{N}_{i=1}\gamma_{i}^{A}\cdot x_i}{\sum_{i=1}^{N}\gamma_{i}^{A}} p1=i=1NγiAi=1NγiAxi

  3. 更新 p 2 p_2 p2(硬币B正面朝上的概率)

    p 2 = ∑ i = 1 N γ i B ⋅ x i ∑ i = 1 N γ i B \displaystyle p2=\dfrac{\sum^{N}_{i=1}\gamma_{i}^{B}\cdot x_i}{\sum_{i=1}^{N}\gamma_{i}^{B}} p2=i=1NγiBi=1NγiBxi

EM 重复执行 E 步和 M 步,直至收敛:

  • 似然函数的收敛:EM算法每次迭代会使得似然函数的值逐步增加,一个常见的收敛条件是似然函数的值在两次迭代间的变化小于某个预设的阈值;
  • 参数的收敛:参数的变化小于某个阈值也是常用的收敛条件之一,即当两次迭代间模型参数 θ 的变化量小于给定的阈值时,算法认为已经收敛;
  • 最大迭代次数:当达到最大迭代次数时即停止。

注意:EM 算法可能收敛到局部最优解,对初始值敏感,且收敛速度较慢。因此,实际应用时,需要进行一些改进来提升其性能。

接下来,我们来观察下上面提到问题的计算过程:

import numpy as np


def e_step(coins, pi, p1, p2):
    resp = []
    for coin in coins:

        a = pi * p1 ** coin * (1 - p1) ** (1 - coin)
        b = (1 - pi) * p2 ** coin * (1 - p2) ** (1 - coin)

        ap = a / (a + b)
        bp = b / (a + b)
        resp.append((ap, bp))

    return np.array(resp)


def m_step(coins, resp):

    pi = sum(resp[:, 0]) / len(resp[:, 0])
    p1 = np.dot(coins, resp[:, 0]) / sum(resp[:, 0])
    p2 = np.dot(coins, resp[:, 1]) / sum(resp[:, 1])

    return pi, p1, p2


def em(coins):
    # 初始化参数
    pi, p1, p2 = 0.5, 0.6, 0.4
    # 迭代计算
    for _ in range(3):
        # 打印参数
        print('pi: %.5f p1: %.5f p2: %.5f' % (pi, p1, p2))
        resp = e_step(coins, pi, p1, p2)
        # 输出概率
        print(resp)
        print('-' * 30)
        pi, p1, p2 = m_step(coins, resp)


if __name__ == '__main__':
    coins = [1, 0, 1, 1, 0]
    em(coins)

输出如下

pi: 0.50000 p1: 0.60000 p2: 0.40000
[[0.6 0.4]
 [0.4 0.6]
 [0.6 0.4]
 [0.6 0.4]
 [0.4 0.6]]
------------------------------
pi: 0.52000 p1: 0.69231 p2: 0.50000
[[0.6 0.4]
 [0.4 0.6]
 [0.6 0.4]
 [0.6 0.4]
 [0.4 0.6]]
------------------------------
pi: 0.52000 p1: 0.69231 p2: 0.50000
[[0.6 0.4]
 [0.4 0.6]
 [0.6 0.4]
 [0.6 0.4]
 [0.4 0.6]]
------------------------------

我们发现,迭代一次之后参数就不再发生变化,这是因为我们的问题、观测数据都过于简单,在较为复杂的问题中,需要迭代更多的步骤才能收敛。

你可能感兴趣的:(杂,算法,机器学习,概率论)