來自https://www.jianshu.com/p/f4ff48025c52
https://blog.csdn.net/wendaomudong_l2d4/article/details/79005461
1、引言
概率模型有時既含有觀測變量,又含有隱變量或潛在變量,如果概率模型的變量都是觀測變量,那麼給定數據,可以直接用極大似然估計法,或貝葉斯估計法估計模型參數,但是,當模型中含有隱變量時,就不能簡單的使用這些方法。EM算法就是含有隱變量的概率模型參數的極大似然估計法,或極大後驗概率估計法。
EM算法流程
E步:對完全數據的對數似然函數$\log(P(Y,Z|\theta)$
求關於$P(Z|Y,\theta^{(i)})$
的數學期望,
其中$\theta^{(i)}$
是第i次迭代時,$`的估計值
M步:對E步的結果求極值
EM算法步驟
一般的,用Y表示觀測隨機變量的數據,Z表示隱隨機變量的數據,Y和Z連在一起稱爲完全數據,只有觀測數據Y稱爲不完全數據,假設給定觀測數據Y,其概率分佈爲P(Y|θ),那麼不完全數據的似然函數就是P(Y|θ),對數似然函數是L(θ) = log(P(Y|θ)),假設Y和Z的聯合概率分佈是P(Y,Z|θ),那麼完全數據的對數似然函數是logP(Y,Z|θ)。
EM算法通過迭代求L(θ) = log(P(Y|θ))的極大似然估計,每次迭代包括兩步:E步,求期望,M步,求最大化,下面介紹EM算法的步驟:
KaTeX parse error: Can't use function '$' in math mode at position 6: \quad$̲`輸入:觀測變量數據Y,隱變量…,條件分佈`$;
$輸出:模型參數
`.
KaTeX parse error: Can't use function '$' in math mode at position 10: \quad (1)$̲`選擇參數的初值`$\thet…,開始迭代;
$E步:記
爲第i次迭代參數
`的估計值,在第i+1次迭代的E步,計算
這裏,$P(Z|Y,\theta^{(i)})$
是在給定觀測數據Y和當前的參數估計$\theta^{(i)}$
下隱變量數據Z的條件概率分佈;
KaTeX parse error: Can't use function '$' in math mode at position 6: \quad$̲`(3) M步: 求使`$Q(…
$`(4) 重複第(2)步和第(3)步,直到收斂.
2、三硬幣模型描述
三硬幣問題是這樣的:
假設有三枚硬幣,分別記爲A、B、C。這些硬幣正面的概率分別爲π,p,q,進行如下的拋硬幣實驗:先擲硬幣A,根據其結果選出硬幣B或者硬幣C,正面選硬幣B,反面選硬幣C,然後擲選出的硬幣,擲硬幣的記錄,出現正面記作1,出現反面記作0,獨立地重複n次實驗(這裏n=10),然後觀測結果如下:
1,1,0,1,0,0,1,0,1,1
假設只能觀測到擲硬幣的結果,不能觀測擲硬幣的過程,問如何估計三硬幣正面出現的概率,即三硬幣模型的參數π,p,q。
3、三硬幣問題表示
符號標記:
KaTeX parse error: Can't use function '$' in math mode at position 10: \quad y_j$̲`爲第j次實驗的觀測
\quad Z$爲隱變量,表示擲硬幣A出現的結果.該變量只有兩個取值0,1 $$\quad z_j$
爲第j次實驗時,擲硬幣A出現的結果,$z_j=1$
表示硬幣A擲出正面,$z_j=0$
表示硬幣A擲出反面
KaTeX parse error: Can't use function '$' in math mode at position 12: \quad\theta$̲`表示參數集合`$\pi,p,…\quad\theta^{(i)}`的估計值
4、EM算法求解三硬幣模型
假設當前模型的參數爲π,p,q時,隱含變量來自於硬幣A的後驗概率,
對數似然函數爲:
期望爲:
`$
對於概率`$:
,
所以最終結果:
上式中`$.
EM算法的具體實現
E-Step:
,
那麼隱含變量來自於硬幣C的後驗概率自然爲`$.
def cal_u(phi, p, q, yj):
return phi * math.pow(p, yj) * math.pow(1 - p, 1 - yj) / \
float(phi * math.pow(p, yj) * math.pow(1 - p, 1 - yj) +
(1 - phi) * math.pow(q, yj) * math.pow(1 - q, 1 - yj))
def e_step(phi,p,q,y):
"""
e步計算
:param phi: 下一次迭代開始的 pi
:param p: 下一次迭代開始的 p
:param q: 下一次迭代開始的 q
:param y: 觀察數據
:return:
"""
return [cal_u(phi,p,q,yj) for yj in y]
M-Step:
下面分別對$\pi,p,q$
求偏導:
(1) 估計參數$\pi$
$$\qquad \pi$`求偏導:
\begin{array}{lll}
\frac{\partial Q}{\partial \pi}&=&\sum\limits_{j=1}^n(\mu_j^{(i)}* \frac{1}{\pi}-(1-\mu_j^{(i)})* \frac{1}{1-\pi})\\
&=&\sum\limits_{j=1}^n(\frac{\pi-\mu_j^{(i)}}{\pi(1-\pi)})\\
&=&\frac{n\pi-\sum\limits_{j=1}^n\mu_j^{(i)}}{\pi(1-\pi)}=0
\end{array}
所以π的估計爲:
(2) 估計參數p
對p求偏導:
\begin{array}{lll}
\frac{\partial Q}{\partial p}&=&\sum\limits_{j=1}^n\mu_j^{(i)}*\frac{\pi(y_j p^{y_j-1}(1-p)^{1-y_j}+p^{y_j}(-(1-y_j)(1-p)^{-y_j})}{\pi p^{y_j}(1-p)^{1-y_j}}\\
&=&\sum\limits_{j=1}^n \mu_j^{(i)}* \frac{y_j p^{y_j-1}(1-p)^{-y_j}(1-p)+p^{y_j-1}*p(y_j-1)(1-p)^{-y_j}}{p^{y_j}(1-p)^{1-y_j}}\\
&=&\sum\limits_{j=1}^n\mu_j^{(i)}*\frac{y_j(1-p)+p*(y_j-1)}{p(1-p)}\\
&=&\sum\limits_{j=1}^n\mu_j^{(i)}*\frac{y_j-p}{p(1-p)}=0
\end{array}
p的估計爲:
(3) 估計參數q
對q求偏導:
\begin{array}{lll}
\frac{\partial Q}{\partial q}&=&\sum\limits_{j=1}^n(1-\mu_j^{(i)})*\frac{(1-\pi)(y_j q^{y_j-1}(1-q)^{1-y_j}+q_{y_j}(-(1-y_j)(1-q){-y_j}))}{(1-\pi)q^{y_j}(1-q)^{1-y_j}}\\
&=&\sum\limits_{j=1}^n(1-\mu_j^{(i)})*\frac{y_j q^{y_j-1}(1-q)^{-y_j}*(1-q)+q^{y_j-1}*q(y_j-1)(1-q)^{-y_j}}{q^{y_j}(1-q)^{1-y_j}}\\
&=&\sum\limits_{j=1}^n(1-\mu_j^{(i)})*\frac{y_j(1-q)+q*(y_j-1)}{q(1-q)}\\
&=&\sum\limits_{j=1}^n(1-\mu_j^{(i)})*\frac{y_j-q}{p(1-q)}=0
\end{array}
q的估計爲:
更新$\pi,p,q$
的代碼如下:
def m_step(u,y):
phi1=sum(u)/len(u)
p1=sum([u[i]*y[i] for i in range(len(u))]) / sum(u)
q1=sum([(1-u[i])*y[i] for i in range(len(u))]) / sum([1-u[i] for i in range(len(u))])
return [phi1,p1,q1]
算法首先選取參數初始值θ(0)=π(0),p(0),q(0) ,然後迭代到收斂爲止.
完整的python代碼如下:
# -*- coding: utf-8 -*-
import math
def cal_u(phi, p, q, yj):
"""
u值計算
:param phi: 下一次迭代開始的 pi
:param p: 下一次迭代開始的 p
:param q: 下一次迭代開始的 q
:param yj: 觀察數據第j個值,從0開始
:return:
"""
return phi * math.pow(p, yj) * math.pow(1 - p, 1 - yj) / \
float(phi * math.pow(p, yj) * math.pow(1 - p, 1 - yj) +
(1 - phi) * math.pow(q, yj) * math.pow(1 - q, 1 - yj))
def e_step(phi,p,q,y):
"""
e步計算
:param phi: 下一次迭代開始的 pi
:param p: 下一次迭代開始的 p
:param q: 下一次迭代開始的 q
:param y: 觀察數據
:return:
"""
return [cal_u(phi,p,q,yj) for yj in y]
def m_step(u,y):
"""
m步計算
:param u: m步計算的u
:param y: 觀察數據
:return:
"""
phi1=sum(u)/len(u)
p1=sum([u[i]*y[i] for i in range(len(u))]) / sum(u)
q1=sum([(1-u[i])*y[i] for i in range(len(u))]) / sum([1-u[i] for i in range(len(u))])
return [phi1,p1,q1]
def run(observed_y, start_phi, start_p, start_q, iter_num):
"""
:param observed_y: 觀察數據
:param start_phi: 下一次迭代開始的pi $\pi$
:param start_p: 下一次迭代開始的p
:param start_q: 下一次迭代開始的q
:param iter_num: 迭代次數
:return:
"""
for i in range(iter_num):
u=e_step(start_phi, start_p, start_q, observed_y)
print (i,[start_phi,start_p,start_q])
if [start_phi,start_p,start_q]==m_step(u, observed_y):
break
else:
[start_phi,start_p,start_q]=m_step(u, observed_y)
if __name__ =="__main__":
# 觀察數據
y = [1, 1, 0, 1, 0, 0, 1, 0, 1, 1]
# 初始化 pi,p q
[phi, p, q] = [0.4, 0.6, 0.7]
# 迭代計算
run(y,phi,p,q,100)
結果如下:
0 [0.4, 0.6, 0.7]
1 [0.40641711229946526, 0.5368421052631579, 0.6432432432432431]
2 [0.40641711229946537, 0.5368421052631579, 0.6432432432432431]
3 [0.40641711229946537, 0.536842105263158, 0.6432432432432431]