HMM(隱馬爾科夫)用于中文分詞

隱馬爾可夫模型(Hidden Markov Model,HMM)是用來描述一個含有隱含未知參數(shù)的馬爾可夫過程。
本文閱讀了2篇blog,理解其中的意思,附上自己的代碼,共同學習。

一、理解隱馬爾科夫

1.1 舉例理解

來源:< http://www.cnblogs.com/skyme/p/4651331.html >
假設我手里有三個不同的骰子。第一個骰子是我們平常見的骰子(稱這個骰子為D6),6個面,每個面(1,2,3,4,5,6)出現(xiàn)的概率是1/6。第二個骰子是個四面體(稱這個骰子為D4),每個面(1,2,3,4)出現(xiàn)的概率是1/4。第三個骰子有八個面(稱這個骰子為D8),每個面(1,2,3,4,5,6,7,8)出現(xiàn)的概率是1/8。

image.png

當我們無法觀測到時使用哪個骰子投擲,僅僅能看到投擲的結果的時候。例如我們得到一個序列值:1 6 3 5 2 7 3 5 2 4。
它其實包含了:1、隱含的狀態(tài),選擇了哪個骰子;2、可見狀態(tài),使用該骰子投出數(shù)值。如下:

image.png

而假設,每個狀態(tài)間轉移的概率(選擇骰子的概率)是固定的(即為不因觀測值的數(shù)值而改變)。可以得到狀態(tài)轉移矩陣。

image.png

那么我們得到觀測值序列(1 6 3 5 2 7 3 5 2 4)出現(xiàn)概率的計算公式:

image.png

舉前3個觀測值(1 6 3)的例子,計算如下:

image.png

以上計算中,假設選擇3個骰子的概率是相同的,都是1/3。

1.2 例子抽象

通過以上例子可以抽象一下,上面的例子中:

3種不同情況的骰子,即為:狀態(tài)值集合(StatusSet)
所有可能出現(xiàn)的結果值(1、2、3、4、5、6、7、8):觀察值集合(ObservedSet)
選擇不同骰子之間的概率:轉移概率矩陣(TransProbMatrix ),狀態(tài)間轉移的概率
在拿到某個骰子,投出某個觀測值的概率:發(fā)射概率矩陣(EmitProbMatrix )-即:拿到D6這個骰子,投出6的概率是1/6。
最初一次的狀態(tài):初始狀態(tài)概率分布(InitStatus )

所以,很容易得到,計算概率的方法就是,初始狀態(tài)概率分布(InitStatus )、發(fā)射概率矩陣(EmitProbMatrix )、轉移概率矩陣(TransProbMatrix )的乘積。
當某個狀態(tài)序列的概率值最大,則該狀態(tài)序列即為,出現(xiàn)該觀測值的情況下,最可能出現(xiàn)的狀態(tài)序列。

二、中文分詞

該篇文章講了怎么使用隱馬爾科夫鏈作分詞,原理使用上面的作為理解。下文中提到的SBME4個狀態(tài)可以類比為上文提到的3個骰子。中文文字即為上文提到的投出的數(shù)字。

來源:< http://blog.csdn.net/taoyanqi8932/article/details/75312822 >

2.1 模型

HMM的典型模型是一個五元組:
StatusSet: 狀態(tài)值集合
ObservedSet: 觀察值集合
TransProbMatrix: 轉移概率矩陣
EmitProbMatrix: 發(fā)射概率矩陣
InitStatus: 初始狀態(tài)分布

2.2 基本假設

HMM模型的三個基本假設如下:
有限歷史性假設:
P(Status[i]|Status[i-1],Status[i-2],… Status[1]) = P(Status[i]|Status[i-1])
齊次性假設(狀態(tài)和當前時刻無關):
P(Status[i]|Status[i-1]) = P(Status[j]|Status[j-1])
觀察值獨立性假設(觀察值只取決于當前狀態(tài)值):
P(Observed[i]|Status[i],Status[i-1],…,Status[1]) = P(Observed[i]|Status[i])

2.3 五元組

2.3.1 狀態(tài)值集合(StatusSet)

為(B, M, E, S): {B:begin, M:middle, E:end, S:single}。分別代表每個狀態(tài)代表的是該字在詞語中的位置,B代表該字是詞語中的起始字,M代表是詞語中的中間字,E代表是詞語中的結束字,S則代表是單字成詞。
如:

給你一個隱馬爾科夫鏈的例子。
可以標注為:
給/S 你/S 一個/BE 隱馬爾科夫鏈/BMMMME 的/S 例子/BE 。/S
2.3.2 觀察值集合(ObservedSet)

為就是所有漢字(東南西北你我他…),甚至包括標點符號所組成的集合。
狀態(tài)值也就是我們要求的值,在HMM模型中文分詞中,我們的輸入是一個句子(也就是觀察值序列),輸出是這個句子中每個字的狀態(tài)值。

2.3.3 初始狀態(tài)概率分布(InitStatus )

如:

B  -0.26268660809250016
E  -3.14e+100
M  -3.14e+100
S  -1.4652633398537678

數(shù)值是對概率值取【對數(shù)】之后的結果(可以讓概率【相乘】的計算變成對數(shù)【相加】)。其中-3.14e+100作為負無窮,也就是對應的概率值是0。
也就是句子的第一個字屬于{B,E,M,S}這四種狀態(tài)的概率。

2.3.4 轉移概率矩陣(TransProbMatrix )

【有限歷史性假設】

轉移概率是馬爾科夫鏈。Status(i)只和Status(i-1)相關,這個假設能大大簡化問題。所以,它其實就是一個4x4(4就是狀態(tài)值集合的大小)的二維矩陣。矩陣的橫坐標和縱坐標順序是BEMS x BEMS。(數(shù)值是概率求對數(shù)后的值)

2.3.5 發(fā)射概率矩陣(EmitProbMatrix )

【觀察值獨立性假設】
P(Observed[i], Status[j]) = P(Status[j]) * P(Observed[i]|Status[j])
其中,P(Observed[i]|Status[j])這個值就是從EmitProbMatrix中獲取。

2.4 使用Viterbi算法

這五元的關系是通過一個叫Viterbi的算法串接起來,ObservedSet序列值是Viterbi的輸入,而StatusSet序列值是Viterbi的輸出,輸入和輸出之間Viterbi算法還需要借助三個模型參數(shù),分別是InitStatus, TransProbMatrix, EmitProbMatrix。

定義變量
二維數(shù)組 weight[4][15],4是狀態(tài)數(shù)(0:B,1:E,2:M,3:S),15是輸入句子的字數(shù)。比如 weight[0][2] 代表 狀態(tài)B的條件下,出現(xiàn)’碩’這個字的可能性。

二維數(shù)組 path[4][15],4是狀態(tài)數(shù)(0:B,1:E,2:M,3:S),15是輸入句子的字數(shù)。比如 path[0][2] 代表 weight[0][2]取到最大時,前一個字的狀態(tài),比如 path[0][2] = 1, 則代表 weight[0][2]取到最大時,前一個字(也就是明)的狀態(tài)是E。記錄前一個字的狀態(tài)是為了使用viterbi算法計算完整個 weight[4][15] 之后,能對輸入句子從右向左地回溯回來,找出對應的狀態(tài)序列。

B:-0.26268660809250016
E:-3.14e+100
M:-3.14e+100
S:-1.4652633398537678

且由EmitProbMatrix可以得出

Status(B) -> Observed(小)  :  -5.79545
Status(E) -> Observed(小)  :  -7.36797
Status(M) -> Observed(小)  :  -5.09518
Status(S) -> Observed(小)  :  -6.2475

所以可以初始化 weight[i][0] 的值如下:

weight[0][0] = -0.26268660809250016 + -5.79545 = -6.05814
weight[1][0] = -3.14e+100 + -7.36797 = -3.14e+100
weight[2][0] = -3.14e+100 + -5.09518 = -3.14e+100
weight[3][0] = -1.4652633398537678 + -6.2475 = -7.71276

注意上式計算的時候是相加而不是相乘,因為之前取過對數(shù)的原因。

//遍歷句子,下標i從1開始是因為剛才初始化的時候已經對0初始化結束了
for(size_t i = 1; i < 15; i++)
{
    // 遍歷可能的狀態(tài)
    for(size_t j = 0; j < 4; j++) 
    {
        weight[j][i] = MIN_DOUBLE;
        path[j][i] = -1;
        //遍歷前一個字可能的狀態(tài)
        for(size_t k = 0; k < 4; k++)
        {
            double tmp = weight[k][i-1] + _transProb[k][j] + _emitProb[j][sentence[i]];
            if(tmp > weight[j][i]) // 找出最大的weight[j][i]值
            {
                weight[j][i] = tmp;
                path[j][i] = k;
            }
        }
    }
}

確定邊界條件和路徑回溯
邊界條件如下:
對于每個句子,最后一個字的狀態(tài)只可能是 E 或者 S,不可能是 M 或者 B。
所以在本文的例子中我們只需要比較 weight[1(E)][14] 和 weight[3(S)][14] 的大小即可。

在本例中:
weight[1][14] = -102.492;
weight[3][14] = -101.632;
所以 S > E,也就是對于路徑回溯的起點是 path[3][14]。

回溯的路徑是:
SEBEMBEBEMBEBEB
倒序一下就是:
BE/BE/BME/BE/BME/BE/S
所以切詞結果就是:
小明/碩士/畢業(yè)于/中國/科學院/計算/所

三、練習與實例

這里可以通過理解上文提到的所有,進行分詞。
給出我的github練習源碼:https://github.com/longgb246/pythonstudy/blob/master/longgb/Algorithm/TextMining/NLP/HMM/HMM.py
以及數(shù)據(jù):https://github.com/longgb246/pythonstudy/tree/master/longgb/Algorithm/TextMining/Data

3.1 預料信息

首先,需要一個完整的預料信息,該預料庫需要特征:
1、覆蓋范圍廣,理論上需要覆蓋你所有可能會被分詞的文字,否則發(fā)射矩陣為出現(xiàn)極端情況,無法分詞。
2、需要文本標注正確,如一些專有名詞,"太平洋保險"等等,需要被分為一個詞,因為他是一個公司名稱,而不應該被分為"太平洋/保險"。
提取該語料庫,可能需要人工干預。
將分詞的結果進行標注,按照上文提到的信息,打上SBME的標注:

image.png

我這里的練習為了方便,直接使用jieba分詞的結果,僅僅作為練習。

3.2 計算初始狀態(tài)概率分布(InitStatus )

初始狀態(tài)即為第一次選擇的狀態(tài)的概率。
這里選擇的是語料庫中,每個句子的第一個字的狀態(tài),統(tǒng)計該狀態(tài)的頻率,計算出該狀態(tài)的概率。當然,為了確保不會出現(xiàn)一些問題,默認,ME是不會出現(xiàn)在句首,即將其概率設置為0,在矩陣中為:-3.14e+100(取了log值,方便轉化為加法計算)。
偽代碼:

content = f.readlines()
content_str = ''.join(content)            # 1、將換行拼接在一起。
content_list = content.split(split_list)   # 2、按照斷句拆分。split_list為。!?等斷句的符號
initStatus.append(firstStatus(content_list))    # 將每一句話的第一個字的狀態(tài)記錄下來,語料庫中,觀測與狀態(tài)按照/劃分開。
statusCount(initStatus)                   # 統(tǒng)計出現(xiàn)狀態(tài)的概率

3.3 計算轉移概率矩陣(TransProbMatrix )

轉移概率矩陣是一個SBEMSBEM的44的矩陣,但是其中有一些是不可能轉移的信息,如:B->S,E->M等等,將這些情況的概率的log值設置為-3.14e+100。其他的按照詞前后的狀態(tài)序列統(tǒng)計,統(tǒng)計前后之間的關系,這里已知假設,當前狀態(tài)僅與前一狀態(tài)有關,與更前面的狀態(tài)無關。所以,思路:

內容按照/拆分 -> 取出狀態(tài)序列 -> 分拆為2元組 -> 統(tǒng)計前一狀態(tài)出現(xiàn)后一狀態(tài)的概率

3.4 計算發(fā)射概率矩陣(EmitProbMatrix )

回想一下上面舉的例子,發(fā)射概率矩陣是在某狀態(tài)下,出現(xiàn)某個觀測值的概率,所以有,在某狀態(tài)下,所有該狀態(tài)下觀測值的概率之和為1【該處理解對于計算發(fā)射矩陣很重要,即,當矩陣的列為SBEM,行為觀測值時候,某一行的概率和為1,而不是某一列的概率和為1。根據(jù)隱馬爾科夫鏈的計算公式,不理解的看看本文第一部分】。
所以,統(tǒng)計方法:

內容按照/拆分 -> 取出狀態(tài):觀測的key:value -> 統(tǒng)計某狀態(tài)下,某觀測出現(xiàn)的次數(shù),即為概率值 

3.5 使用Viterbi算法

第二部分給出了,Viterbi算法的方法,可以根據(jù)初始狀態(tài)概率分布(InitStatus )、轉移概率矩陣(TransProbMatrix )、發(fā)射概率矩陣(EmitProbMatrix )以及觀測值,得出一個最有可能的狀態(tài)序列。按照該狀態(tài)序列,將文本劃分出來即可。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容