機器學習(十三)-EM(Exceptation-Maximization Algorithm)最大期望算法及Python實例

原創不易,轉載前請註明博主的鏈接地址:Blessy_Zhu https://blog.csdn.net/weixin_42555080
本次代碼的環境:
運行平臺: Windows
Python版本: Python3.x
IDE: PyCharm
 

 
在這裏插入圖片描述

一、Exceptation-Maximization Algorithm理解

文章從最大似然到EM算法淺解中介紹的這個男女生的例子很具有代表性,形象的說明了Exceptation-Maximization Algorithm的背景,首先看一下這個例子:

再回到例子本身,如果沒有“男的左邊,女的右邊,其他的站中間!”這個步驟,或者說我抽到這200個人中,某些男生和某些女生一見鍾情,已經好上了,糾纏起來了。咱們也不想那麼殘忍,硬把他們拉扯開。那現在這200個人已經混到一起了,這時候,你從這200個人(的身高)裏面隨便給我指一個人(的身高),我都無法確定這個人(的身高)是男生(的身高)還是女生(的身高)。也就是說你不知道抽取的那200個人裏面的每一個人到底是從男生的那個身高分佈裏面抽取的,還是女生的那個身高分佈抽取的。用數學的語言就是,抽取得到的每個樣本都不知道是從哪個分佈抽取的。
這個時候,對於每一個樣本或者你抽取到的人,就有兩個東西需要猜測或者估計的了,一是這個人是男的還是女的?二是男生和女生對應的身高的高斯分佈的參數是多少?

實際上,Exceptation-Maximization Algorithm要解決的問題是:具有隱變量的混合模型的參數估計。也就是上文提的那個例子,如果男女分開,可以直接對男女驚喜見面,求出身高的分佈即可;但是,如果男女混合,隨便抽出來的一個人,並不知道TA是男還是女,並且對TA進行參數估計。這樣就是Exceptation-Maximization Algorithm要解決的問題了
既然已經知道,Exceptation-Maximization Algorithm是來幹什麼的,那Exceptation-Maximization Algorithm的又是怎麼解決具有隱變量的混合模型的參數估計問題呢?接下來還是看一下文章從最大似然到EM算法淺解中分蘋果的那個例子。

例如,小時候,老媽給一大袋糖果給你,叫你和你姐姐等分,然後你懶得去點糖果的個數,所以你也就不知道每個人到底該分多少個。咱們一般怎麼做呢?先把一袋糖果目測的分爲兩袋,然後把兩袋糖果拿在左右手,看哪個重,如果右手重,那很明顯右手這代糖果多了,然後你再在右手這袋糖果中抓一把放到左手這袋,然後再感受下哪個重,然後再從重的那袋抓一小把放進輕的那一袋,繼續下去,直到你感覺兩袋糖果差不多相等了爲止。呵呵,然後爲了體現公平,你還讓你姐姐先選了。

實際上,上面這個例子,可以這麼解讀:你拿到了待分的蘋果,此時不知道改如何分,但是也不能一直僵着呀,於是你打破僵局,將蘋果粗略的分到了兩個袋子(設置待估計值的初值),然後,爲了公平起見(爲了達到最優化,也就是待估計值達到最優解(最大值)),你一次次平衡兩個袋子中的蘋果,這個過程不是一蹴而就的,你這樣平衡很多次(迭代很多次),最終達到了,你認爲的兩個袋子裏的蘋果一樣多的結果(實現的待估計值的最優化問題)。這樣,可以總結出Exceptation-Maximization Algorithm的基本過程:設置初值、多次迭代、求得最優(個人總結,歡迎批評指正)
同時,最大期望算法經過兩個步驟交替進行計算 :
1)計算期望(E),利用概率模型參數的現有估計值,計算隱藏變量的期望;
2)最大化(M),利用E 步上求得的隱藏變量的期望,對參數模型進行最大似然估計。
3)M 步上找到的參數估計值被用於下一個 E 步計算中,這個過程不斷交替進行。
E步驟:估計未知參數的期望值,給出當前的參數估計。
M步驟:重新估計分佈參數,以使得數據的似然性最大,給出未知變量的期望估計。

二、Exceptation-Maximization Algorithm公式推導

實際上,對於解決一般性問題,可以直接用MLE(MaximumLikelihood Estimation)來求得解析解。如下
在這裏插入圖片描述
但是對於含有隱變量的混合模型來講,求得解析解就比較困難了,而是通過EM來完成,EM的完整表達式如下:
在這裏插入圖片描述
E-STEP:
在這裏插入圖片描述
M-STEP:
在這裏插入圖片描述
收斂性:
在這裏插入圖片描述
可以看出這是一個迭代算法,其中,
θ是待估計量parameter,
Z是隱變量unobserved data(latent variable),
X是給定數據observed data,
θ(t)是t時刻的參數,他是一個常數(因爲在當前時刻,已經得到了t時刻的值了),
(X,Z):complete data
給出EM的完整表達式之後,接下來,瞭解一些推導過程中用到的內容:
在這裏插入圖片描述
在E-Step中通過上一個θ(t)求得的當前的期望,然後華東M-Step中的θ,使得M-Step中的θ(t+1)達到最大,並不斷更新,知道找到最終是θ最大的那個值。
接下來通過利用Jensen Inquality來推導出EM(Exceptation-Maximization Algorithm)表達式:

2.1 Jensen Inquality

Jensen不等式表述如下:
如果f是凸函數,X是隨機變量,那麼
在這裏插入圖片描述
特別地,如果f是嚴格凸函數,那麼上面不等式等號成立是滿足:
在這裏插入圖片描述
在這裏插入圖片描述
如上圖所示:
圖中,實線f是凸函數,X是隨機變量,有0.5的概率是a,有0.5的概率是b。(就像擲硬幣一樣)。X的期望值就是a和b的中值了,圖中可以看到:
在這裏插入圖片描述
Jensen不等式應用於凹函數時,不等號方向反向,也就是
在這裏插入圖片描述

2.2 一種推導

在這裏插入圖片描述
這裏直接給出手寫版本,如果有不明白的地方歡迎交流討論。

2.3 另外一種推導

在這裏插入圖片描述
這裏依然直接給出手寫版本,如果有不明白的地方歡迎交流討論。

三 實例驗證

我們用論What is the expectation maximization algorithm?
中介紹的拋硬幣的例子來深刻認識EM算法。

如下圖:H表示正面向上,T表示反面向上,參數θ表示正面朝上的概率。硬幣有兩個,A和B,硬幣是有偏的。本次實驗總共做了5組,每組隨機選一個硬幣,連續拋10次。如果知道每次拋的是哪個硬幣,那麼計算參數θ就非常簡單了,如上圖所示。
在這裏插入圖片描述

如果不知道每次拋的是哪個硬幣呢?那麼,我們就需要用EM算法,基本步驟爲:1、給θA和θB一個初始值;2、(E-step)估計每組實驗是硬幣A的概率(本組實驗是硬幣B的概率=1-本組實驗是硬幣A的概率)。分別計算每組實驗中,選擇A硬幣且正面朝上次數的期望值,選擇B硬幣且正面朝上次數的期望值;3、(M-step)利用第三步求得的期望值重新計算θA和θB;4、當迭代到一定次數,或者算法收斂到一定精度,結束算法,否則,回到第2步。
在這裏插入圖片描述

用代碼實現如上過程:
首先這裏面用到的是scipy庫和numpy庫

#導入的時候用from scipy import stats #利用這個進行概率的計算
此處注意:不能用 import scipy,因爲stats根本不在scipy裏邊,他是一個單獨的stats.py文件,並不是scipy裏邊的一個子類,所以用import scipy後,scipy.stats就是錯誤的寫法,必須用from import。
from scipy import stats
import numpy as np

構建觀測數據集

針對這個問題,首先採集數據,用1表示H(正面),0表示T(反面):

#硬幣投擲結果觀測序列
observations = np.array([[1, 0, 0, 0, 1, 1, 0, 1, 0, 1],
                         [1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
                         [1, 0, 1, 1, 1, 1, 1, 0, 1, 1],
                         [1, 0, 1, 0, 0, 0, 1, 1, 0, 0],
                         [0, 1, 1, 1, 0, 1, 1, 1, 0, 1]])

第一步:參數的初始化

參數賦初值
在這裏插入圖片描述

第一個迭代的E步

拋硬幣是一個二項分佈,可以用scipy中的binom來計算。對於第一行數據,正反面各有5次,所以:

#二項分佈求解公式
contribution_A = scipy.stats.binom.pmf(num_heads,len_observation,theta_A)
contribution_B = scipy.stats.binom.pmf(num_heads,len_observation,theta_B)

將兩個概率正規化,得到數據來自硬幣A,B的概率:

 weight_A = contribution_A / (contribution_A + contribution_B)
 weight_B = contribution_B / (contribution_A + contribution_B)

這個值類似於三硬幣模型中的μ,只不過多了一個下標,代表是第幾行數據(數據集由5行構成)。同理,可以算出剩下的4行數據的μ。
有了μ,就可以估計數據中AB分別產生正反面的次數了。μ代表數據來自硬幣A的概率的估計,將它乘上正面的總數,得到正面來自硬幣A的總數,同理有反面,同理有B的正反面。

 #更新在當前參數下A,B硬幣產生的正反面次數
 counts['A']['H'] += weight_A * num_heads
 counts['A']['T'] += weight_A * num_tails
 counts['B']['H'] += weight_B * num_heads
 counts['B']['T'] += weight_B * num_tails

第一個迭代的M步

當前模型參數下,AB分別產生正反面的次數估計出來了,就可以計算新的模型參數了:

new_theta_A = counts['A']['H']/(counts['A']['H'] + counts['A']['T'])
new_theta_B = counts['B']['H']/(counts['B']['H'] + counts['B']['T'])
### 完整的EM單個迭代
於是就可以整理一下,給出EM算法單個迭代的代碼:
```java
def singleCircle_EM(priors, observations):
    """
    EM算法單次迭代
    :param priors:[theta_A, theta_B]
    :param observations:[m X n matrix]
    :return:new_priors: [new_theta_A, new_theta_B]
    """
    #利用counts變量來記錄A硬幣正面、背面朝上的次數,B硬幣正面、背面朝上的次數
    counts = {'A': {'H': 0, 'T': 0}, 'B': {'H': 0, 'T': 0}}
    #拿到獲取來的初值
    theta_A = priors[0]
    theta_B = priors[1]
    # E step
    for observation in observations:
        len_observation = len(observation)
        #正面朝上的個數
        num_heads = observation.sum()
        #反面朝上的個數
        num_tails = len_observation - num_heads
        #二項式分佈函數stats.binom.pmf(k,n,p) 表示從n次測量中獲得k次的概率,p爲隨機一次的概率(相當於抽樣概率)
        #此概率是變化的,所以是可以進行迭代的
        contribution_A = stats.binom.pmf(num_heads, len_observation, theta_A)
        contribution_B = stats.binom.pmf(num_heads, len_observation, theta_B)  # 兩個二項分佈
        #計算權值
        weight_A = contribution_A / (contribution_A + contribution_B)
        weight_B = contribution_B / (contribution_A + contribution_B)
        # 更新在當前參數下A、B硬幣產生的正反面次數
        counts['A']['H'] += weight_A * num_heads
        counts['A']['T'] += weight_A * num_tails
        counts['B']['H'] += weight_B * num_heads
        counts['B']['T'] += weight_B * num_tails
    # M step
    #求權值
    new_theta_A = counts['A']['H'] / (counts['A']['H'] + counts['A']['T'])
    new_theta_B = counts['B']['H'] / (counts['B']['H'] + counts['B']['T'])
    return [new_theta_A, new_theta_B]

多次迭代,求最優化

def iterationForResult(observations, prior, tol=1e-6, iterations=10000):
    """
    EM算法
    :param observations: 觀測數據
    :param prior: 模型初值
    :param tol: 迭代結束閾值
    :param iterations: 最大迭代次數
    :return: 局部最優的模型參數
    """
    #給定循環的兩個終止條件:模型參數變化小於閾值;循環達到最大次數,就可以寫出EM算法的主循環了
    iteration = 0
    while iteration < iterations:
        new_prior = singleCircle_EM(prior, observations)
        print(new_prior[0])
        #判斷初值的theta_A與當前的theta_A兩個值的變化(絕對值)是否超過閾值
        delta_change = np.abs(prior[0] - new_prior[0])
        if delta_change < tol:
            break
        else:
            prior = new_prior
            iteration += 1
    return [new_prior, iteration]

通過函數調用求解

 result = iterationForResult(observations, [0.6, 0.4])
    print(result)

結果

可以看到共有13次迭代就已經達到約束條件下的最優解,前十三次迭代第一個變量的值,如下。13次迭代的最終結果展示如下最後一列。

0.7261346175962778
0.7679650080356558
0.7851830994553255
0.7922578783453181
0.7950515886589887
0.79612806787534
0.7965383712512938
0.7966940909872233
0.7967530973878143
0.7967754449561374
0.7967839077490712
0.7967871126803284
0.7967883265564044
0.7967887863838075
[[0.7967887863838075, 0.51958314107701], 13]

四 總結

算法裏已知的是觀察數據,未知的是隱含數據和模型參數,在E步,所做的事情是固定模型參數的值,優化隱含數據的分佈,而在M步,我們所做的事情是固定隱含數據分佈,優化模型參數的值。 EM算法有很多的應用,最廣泛的就是GMM混合高斯模型、聚類、HMM等等。
這篇文章就到這裏了,歡迎大佬們多批評指正,也歡迎大家積極評論多多交流。
 

 
在這裏插入圖片描述

參考文章

1 從最大似然到EM算法淺解
2 (EM算法)The EM Algorithm
3 【機器學習算法系列之一】EM算法實例分析
4 EM算法整理及其python實現
5 EM算法詳解和numpy代碼實現
6 EM算法–應用到三個模型: 高斯混合模型 ,混合樸素貝葉斯模型,因子分析模型
7 EM算法**********
8 EM算法整理及其python實現
9 EM算法的python實現

附完整代碼

from scipy import stats
import numpy as np

# 硬幣投擲結果觀測序列
observations = np.array([[1, 0, 0, 0, 1, 1, 0, 1, 0, 1],
                         [1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
                         [1, 0, 1, 1, 1, 1, 1, 0, 1, 1],
                         [1, 0, 1, 0, 0, 0, 1, 1, 0, 0],
                         [0, 1, 1, 1, 0, 1, 1, 1, 0, 1]])

def singleCircle_EM(priors, observations):
    """
    EM算法單次迭代
    :param priors:[theta_A, theta_B]
    :param observations:[m X n matrix]
    :return:new_priors: [new_theta_A, new_theta_B]
    """
    #利用counts變量來記錄A硬幣正面、背面朝上的次數,B硬幣正面、背面朝上的次數
    counts = {'A': {'H': 0, 'T': 0}, 'B': {'H': 0, 'T': 0}}
    #拿到獲取來的初值
    theta_A = priors[0]
    theta_B = priors[1]
    # E step
    for observation in observations:
        len_observation = len(observation)
        #正面朝上的個數
        num_heads = observation.sum()
        #反面朝上的個數
        num_tails = len_observation - num_heads
        #二項式分佈函數stats.binom.pmf(k,n,p) 表示從n次測量中獲得k次的概率,p爲隨機一次的概率(相當於抽樣概率)
        #此概率是變化的,所以是可以進行迭代的
        contribution_A = stats.binom.pmf(num_heads, len_observation, theta_A)
        contribution_B = stats.binom.pmf(num_heads, len_observation, theta_B)  # 兩個二項分佈
        #計算權值
        weight_A = contribution_A / (contribution_A + contribution_B)
        weight_B = contribution_B / (contribution_A + contribution_B)
        # 更新在當前參數下A、B硬幣產生的正反面次數
        counts['A']['H'] += weight_A * num_heads
        counts['A']['T'] += weight_A * num_tails
        counts['B']['H'] += weight_B * num_heads
        counts['B']['T'] += weight_B * num_tails
    # M step
    #求權值
    new_theta_A = counts['A']['H'] / (counts['A']['H'] + counts['A']['T'])
    new_theta_B = counts['B']['H'] / (counts['B']['H'] + counts['B']['T'])
    return [new_theta_A, new_theta_B]

def iterationForResult(observations, prior, tol=1e-6, iterations=10000):
    """
    EM算法
    :param observations: 觀測數據
    :param prior: 模型初值
    :param tol: 迭代結束閾值
    :param iterations: 最大迭代次數
    :return: 局部最優的模型參數
    """
    #給定循環的兩個終止條件:模型參數變化小於閾值;循環達到最大次數,就可以寫出EM算法的主循環了
    iteration = 0
    while iteration < iterations:
        new_prior = singleCircle_EM(prior, observations)
        print(new_prior[0])
        #判斷初值的theta_A與當前的theta_A兩個值的變化(絕對值)是否超過閾值
        delta_change = np.abs(prior[0] - new_prior[0])
        if delta_change < tol:
            break
        else:
            prior = new_prior
            iteration += 1
    return [new_prior, iteration]


if __name__ == "__main__":
    result = iterationForResult(observations, [0.6, 0.4])
    print(result)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章