1. 程式人生 > >em算法系列1:python實現三硬幣模型

em算法系列1:python實現三硬幣模型

三硬幣模型

假設有3枚硬幣,分別記做A,B,C。這些硬幣正面出現的概率分別是π,p和q。進行如下擲硬幣實驗:先擲硬幣A,根據其結果選出硬幣B或C,正面選B,反面選硬幣C;然後投擲選重中的硬幣,出現正面記作1,反面記作0;獨立地重複n次(n=10),結果為1111110000 我們只能觀察投擲硬幣的結果,而不知其過程,估計這三個引數π,p和q。

EM演算法

可以看到投擲硬幣時到底選擇了B或者C是未知的。我們設隱藏變數Z 來指示來自於哪個硬幣,Z={z1,z2,,zn}Z=\{z_1,z_2,…,z_n\},令θ={π,p,q}θ=\{π,p,q\},觀察資料X

=x1,x2,,xnX={x_1,x_2,…,x_n}

寫出生成一個硬幣時的概率: P(xθ)=zP(x,zθ)=zP(zπ)P(xz,θ)=πpx(1p)1x+(1π)qx(1q)1x\begin{aligned}P(x|θ) &=∑zP(x,z|θ) = ∑zP(z|π)P(x|z,θ) \\ &= πp^x(1−p)^{1−x}+(1−π)q^x(1−q)^{1−x} \end{aligned}

x(1q)1x 有了一個硬幣的概率,我們就可以寫出所有觀察資料的log似然函式: L(θX)=logP(Xθ)=j=1nlog[πpxj(1p)1xj+(1π)qxj(1q)1xj]\begin{aligned} L(θ|X)=logP(X|θ)=∑_{j=1}^nlog[πp^{x_j}(1−p)^{1−x_j} + (1−π)q^{x_j}(1−q)^{1−x_j}] \end{aligned} 然後求似然函式最大值 θ=argmaxL(θ
X) θ^*= arg\quad maxL(θ|X)
其中L(θX)=logP(Xθ)=logZP(X,Zθ)L(θ|X)=logP(X|θ)=log∑_ZP(X,Z|θ)。因為loglog裡面帶著加和所以這個極大似然是求不出解析解的。 如果我們知道隱藏變數Z的話,求以下的似然會容易很多: L(θ|X,Z)=logP(X,Z|θ) 但是隱藏變數Z的值誰也不知道,所以我們轉用EM演算法求它的後驗概率期望EZX,θ(L(θX,Z))E_{Z|X,θ}(L(θ|X,Z))的最大值。

  • E步::假設當前模型的引數為π,p,qπ,p,q時,隱含變數來自於硬幣B的後驗概率 μ=P(ZX,θ)=πpxi(1p)1xiπpxi(1p)1xi+(1π)qxi(1q)1xi\begin{aligned} μ=P(Z|X,θ)= \frac{πp^{x_i}(1−p)^{1−x_i}}{πp^{x_i}(1−p)^{1−x_i}+(1−π)q^{x_i}(1−q)^{1−x_i}} \end{aligned} 那麼隱含變數來自於硬幣C的後驗概率自然為1μ1−μ
def cal_u(pi1,p1,q1,xi):
    return pi1*math.pow(p1,xi) * math.pow(1-p1,1-xi)/\
           float(pi1* math.pow(p1,xi) * math.pow(1-p1,1-xi)+
                 (1-pi1) * math.pow(q1,xi) * math.pow(1-q1,1-xi))
  • M步: 先寫出似然關於後驗概率的期望,它是似然期望下界函式的最大值, EZX,θ(L(θX,Z))=j=1n[μlogπpxi(1p)1xiμ+(1μ)log(1π)qxi(1q)1xi1μ]E_{Z|X,θ}(L(θ|X,Z))=∑_{j=1}^n [μlog \frac{πp^{x_i}(1−p)^{1−x_i}}{μ} + (1−μ)\frac{log(1−π)q^{x_i}(1−q)^{1−x_i}}{1−μ}] 要注意這裡把μμ看做固定值。然後我們分別求偏導,獲得引數π,p,qπ,p,q的新估計值 π=1nj=1nμjp=j=1nμjxjj=1nμjq=j=1n(1μj)xjj=1n(1μj) \begin{aligned} π &=\frac{1}{n} ∑_{j=1}^nμ_j \\ p&=\frac{∑_{j=1}^nμ_jx_j}{∑_{j=1}^nμ_j }\\ q&=\frac{∑^n_{j=1}(1−μ_j)xj}{∑^n_{j=1}(1−μ_j)} \end{aligned}
def cal_u(pi, p, q, xi):
    """
      u值計算
    :param pi: 下一次迭代開始的 pi
    :param p:  下一次迭代開始的 p
    :param q:  下一次迭代開始的 q
    :param xi: 觀察資料第i個值,從0開始
    :return: 
    """
    return pi * math.pow(p, xi) * math.pow(1 - p, 1 - xi) / \
           float(pi * math.pow(p, xi) * math.pow(1 - p, 1 - xi) +
                 (1 - pi) * math.pow(q, xi) * math.pow(1 - q, 1 - xi))

def e_step(pi,p,q,x):
    """
        e步計算
    :param pi: 下一次迭代開始的 pi
    :param p:  下一次迭代開始的 p
    :param q:  下一次迭代開始的 q
    :param x: 觀察資料
    :return:
    """
    return [cal_u(pi,p,q,xi) for xi in x]

演算法首先選取引數初始值θ(0)=π(0),p(0),q(0)θ(0)=π(0),p(0),q(0),然後迭代到收斂為止

python實現

# -*- coding: utf-8 -*-
import math

def cal_u(pi, p, q