日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于HMM的中文分词

發(fā)布時間:2023/12/19 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于HMM的中文分词 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

隱馬爾可夫模型(HMM)在中文分詞中的應(yīng)用

隱馬爾可夫模型的詳細(xì)解釋

隱馬爾可夫模型的一些范例介紹

隱馬爾可夫模型中有兩個序列,一個是狀態(tài)序列,另一個是觀測序列,其中狀態(tài)序列是隱藏的。用具體的例子來解釋。

? ? ? 假設(shè)一個房間內(nèi)有N個裝有球的盒子,在這些盒子中分別有M種不同顏色的球,我根據(jù)某一個概率分布(初始概率分布,在中文分詞中就是一句話中第一個字符對應(yīng)的狀態(tài)概率)隨機(jī)地選取一個初始的盒子,從中根據(jù)不同顏色的球的概率分布(發(fā)射概率,在中文分詞中就是每一個字對應(yīng)的狀態(tài)概率),隨機(jī)選擇出一個球,然后我將這個球拿給房間外的你看。這個時候你能看到的就只是不同顏色的球的序列(觀測序列),我選擇的盒子的序列(狀態(tài)序列)你是不知道的。

訓(xùn)練階段參數(shù)估計

一個HMM可以由下面幾個部分組成:

(1)模型中的狀態(tài)數(shù)量N(上例中盒子的數(shù)量)

在中文分詞中一般只有4個狀態(tài):STATES={‘B’,‘M’,‘E’,‘S’},例如:小明是中國人,對應(yīng)的狀態(tài)序列就是:B,E,S,B,M,E

(2)從每個狀態(tài)可能輸出的不同符號的數(shù)目M(上例中不同顏色球的個數(shù))

在中文分詞中就是對應(yīng)每一個字符

?

在中文分詞中就是狀態(tài)序列STATES={‘B’,‘M’,‘E’,‘S’}的轉(zhuǎn)移概率,這個狀態(tài)概率矩陣是在訓(xùn)練階段參數(shù)估計中得到。在中文分詞中狀態(tài)轉(zhuǎn)移矩陣是一個4*4的矩陣,我在實驗中是通過統(tǒng)計訓(xùn)練數(shù)據(jù)中狀態(tài)轉(zhuǎn)移的頻數(shù)確定矩陣,為了確保,需要對頻數(shù)矩陣除以對應(yīng)每一行狀態(tài)的統(tǒng)計數(shù),即A[key0][key1]/count(key0),為了保證數(shù)據(jù)的精度,我這里取了對數(shù),并且以字典輸出方便查看。

{'B': {'B': -3.14e+100, 'M':-1.9594399657636383, 'E': -0.15191340104766693, 'S': -3.14e+100},

'M': {'B': -3.14e+100, 'M':-1.0983633205740504, 'E': -0.40558961540346977, 'S': -3.14e+100},

'E': {'B':-0.78182902092047468, 'M': -3.14e+100, 'E': -3.14e+100, 'S':-0.62312924475978682},

'S': {'B': -0.74289298827998818,'M': -3.14e+100, 'E': -3.14e+100, 'S': -0.81330579119502522}}

可以看到對應(yīng)狀態(tài)‘B’后面只能接‘M’和‘E’;狀態(tài)‘M’后面只能接‘M’和‘E’;狀態(tài)‘E’后面只能接‘B’和‘S’;狀態(tài)‘S’后面只能接‘B’和‘S’。

?

在中文分詞中發(fā)射概率指的是每一個字符對應(yīng)狀態(tài)序列STATES={‘B’,‘M’,‘E’,‘S’}中每一個狀態(tài)的概率,通過對訓(xùn)練集每個字符對應(yīng)狀態(tài)的頻數(shù)統(tǒng)計得到。我是通過字典的形式保存,就可能出現(xiàn)某個字符在某一個狀態(tài)沒有頻數(shù),導(dǎo)致后面矩陣計算錯誤。為保證每個字符都在STATES的字典中,在構(gòu)建發(fā)射概率矩陣是先初始化。

?

在中文分詞初始狀態(tài)概率指的是每一句話第一個字符的對應(yīng)狀態(tài)概率。在我實驗中通過訓(xùn)練的得到的初始狀態(tài)概率分布為,

{'B': -0.48164247830489765,'M': -3.14e+100, 'E': -3.14e+100, 'S': -0.96172723110752845}

可以看到第一個字符的初始狀態(tài)只能是‘B’和‘S’。

這個時候我們就完成了訓(xùn)練階段的參數(shù)估計,得到了三個概率矩陣,分別是:

TransProbMatrix: 轉(zhuǎn)移概率矩陣(array_A)

EmitProbMatrix: 發(fā)射概率矩陣(array_B)

InitStatus: 初始狀態(tài)分布(array_pi)

測試階段Viterbi算法

在這里我們要做的是對測試數(shù)據(jù)進(jìn)行分詞,首先要做的就是對測試數(shù)據(jù)中每一個字符進(jìn)行狀態(tài)標(biāo)注,這里使用的是Viterbi算法進(jìn)行標(biāo)注。

給一個測試語句‘小明是中國人’,首先標(biāo)注第一個字‘小’,由于是第一個字,這里只需要用到初始狀態(tài)概率矩陣和發(fā)射矩陣,

初始狀態(tài)概率矩陣:

{'B': -0.48164247830489765,'M': -3.14e+100, 'E': -3.14e+100, 'S': -0.96172723110752845}

發(fā)射概率矩陣:

{‘B’:{‘小’:-6.04291273032},‘M’:{‘小’:-6.50192547556},‘E’:{‘小’:-7.88369576318},‘S’:{‘小’:-6.60403172082}}

然后標(biāo)注概率矩陣tab為:

for state in STATES:tab[0][state] = array_pi[state] + array_b[state]['小']

這里我對矩陣取對數(shù),所有只用‘+’即可,則得到第一個字‘小’的標(biāo)注概率:

tab[0][‘B’]=-6.52455520862

tab [0][‘M’]=-3.14e+100

tab [0][‘E’]=-3.14e+100

tab [0][‘S’]=-7.56575895193

這里可以看到path[0][‘B’]> path[0][‘S’],則這句語句第一個字‘小’標(biāo)注為‘B’

然后計算第二個字明,這時就需要狀態(tài)轉(zhuǎn)移概率矩陣array_A。這里有兩個迭代過程,對于‘明’每一個狀態(tài)state0都需要計算前一個字‘小’可能的每一個狀態(tài)state1的概率prob,然后取max,

for state0 in STATES:for state1 in STATES:prob=tab[i-1][state1]+array_a[state1][state0]+array_b[state0][sentence[i]]

這實際上就是一個動態(tài)規(guī)劃最優(yōu)路徑的問題,對前一個字‘小’的每個狀態(tài)state根據(jù)狀態(tài)轉(zhuǎn)移矩陣array_a和‘小’的標(biāo)注概率tab,再加上這個字‘明’的發(fā)射概率array_b,得到‘明’這個字的標(biāo)記概率

tab[1][‘B’]= -14.742836926340019

tab [1][‘M’]=-14.836146344717278

tab [1][‘E’]= -12.832943424756692

tab [1][‘S’]=-17.961024112859207

這里可以看到tab [1][‘E’]最大,這個就是‘明’這個字的標(biāo)注為‘E’。然后對測試數(shù)據(jù)依次進(jìn)行這個操作。

在測試階段使用Viterbi標(biāo)注時需要注意:若測試集中出現(xiàn)某一個字符,但這個字符在訓(xùn)練集中未出現(xiàn),這個時候發(fā)射概率是沒有的,這里需要做平滑處理,我這里采用的方式是:將所有未在訓(xùn)練集中出現(xiàn)的字符統(tǒng)一發(fā)射概率為發(fā)射概率矩陣的中位數(shù)。

在這里測試數(shù)據(jù)的標(biāo)注已經(jīng)得到,最后一步需要做的就是根據(jù)標(biāo)注的狀態(tài)進(jìn)行分詞。這里有幾種情況:

(1)測試數(shù)據(jù)只有一個字符,直接輸出

(2)測試數(shù)據(jù)標(biāo)注的最后一個字符的狀態(tài)不是‘S’和‘E’,這里需要進(jìn)行修改

if tag[-1] == 'B' or tag[-1] == 'M': #最后一個字狀態(tài)不是'S'或'E'則修改if tag[-2] == 'B' or tag[-2] == 'M':tag[-1] = 'S'else:tag[-1] = 'E'

最后整個分詞器就已經(jīng)實現(xiàn),這里需要對最后的分詞結(jié)果進(jìn)行測試,測試資源

這里寫一下score腳本的使用方法,

(1)這是一個perl的腳本,在windows系統(tǒng)中首先要下載一個ActivePerl的解釋器,配置環(huán)境變量。

(2)然后score需要GNU diffutils的支持,下載地址

(3)然后需要修改score腳本中的語句,指定diff的安裝目錄$diff,$tmp1和$tmp2分別指定黃金標(biāo)準(zhǔn)分詞文件和測試集切分文件。

?

我這里指定了tmp的路徑,但是評分腳本“score”是用來比較兩個分詞文件的,需要三個參數(shù):

1. 訓(xùn)練集詞表(The training setword list)

2. “黃金”標(biāo)準(zhǔn)分詞文件(The gold standard segmentation)

3. 測試集的切分文件(The segmentedtest file)

我在cmd中的命令輸入為:

perl score CTB_training_words.utf8CTB_test_gold.utf8 output.txt

最后的分詞結(jié)果為

實驗代碼:

GitHub鏈接:https://github.com/CQUPT-Wan/HMMwordseg.git

這個項目里面有我實現(xiàn)HMM中文分詞的訓(xùn)練集,測試集和黃金標(biāo)準(zhǔn)分詞文件

import pandas as pd import codecs from numpy import * import numpy as np import sys import re STATES = ['B', 'M', 'E', 'S'] array_A = {} #狀態(tài)轉(zhuǎn)移概率矩陣 array_B = {} #發(fā)射概率矩陣 array_E = {} #測試集存在的字符,但在訓(xùn)練集中不存在,發(fā)射概率矩陣 array_Pi = {} #初始狀態(tài)分布 word_set = set() #訓(xùn)練數(shù)據(jù)集中所有字的集合 count_dic = {} #‘B,M,E,S’每個狀態(tài)在訓(xùn)練集中出現(xiàn)的次數(shù) line_num = 0 #訓(xùn)練集語句數(shù)量#初始化所有概率矩陣 def Init_Array():for state0 in STATES:array_A[state0] = {}for state1 in STATES:array_A[state0][state1] = 0.0for state in STATES:array_Pi[state] = 0.0array_B[state] = {}array_E = {}count_dic[state] = 0#對訓(xùn)練集獲取狀態(tài)標(biāo)簽 def get_tag(word):tag = []if len(word) == 1:tag = ['S']elif len(word) == 2:tag = ['B', 'E']else:num = len(word) - 2tag.append('B')tag.extend(['M'] * num)tag.append('E')return tag#將參數(shù)估計的概率取對數(shù),對概率0取無窮小-3.14e+100 def Prob_Array():for key in array_Pi:if array_Pi[key] == 0:array_Pi[key] = -3.14e+100else:array_Pi[key] = log(array_Pi[key] / line_num)for key0 in array_A:for key1 in array_A[key0]:if array_A[key0][key1] == 0.0:array_A[key0][key1] = -3.14e+100else:array_A[key0][key1] = log(array_A[key0][key1] / count_dic[key0])# print(array_A)for key in array_B:for word in array_B[key]:if array_B[key][word] == 0.0:array_B[key][word] = -3.14e+100else:array_B[key][word] = log(array_B[key][word] /count_dic[key])#將字典轉(zhuǎn)換成數(shù)組 def Dic_Array(array_b):tmp = np.empty((4,len(array_b['B'])))for i in range(4):for j in range(len(array_b['B'])):tmp[i][j] = array_b[STATES[i]][list(word_set)[j]]return tmp#判斷一個字最大發(fā)射概率的狀態(tài) def dist_tag():array_E['B']['begin'] = 0array_E['M']['begin'] = -3.14e+100array_E['E']['begin'] = -3.14e+100array_E['S']['begin'] = -3.14e+100array_E['B']['end'] = -3.14e+100array_E['M']['end'] = -3.14e+100array_E['E']['end'] = 0array_E['S']['end'] = -3.14e+100def dist_word(word0,word1,word2,array_b):if dist_tag(word0,array_b) == 'S':array_E['B'][word1] = 0array_E['M'][word1] = -3.14e+100array_E['E'][word1] = -3.14e+100array_E['S'][word1] = -3.14e+100return#Viterbi算法求測試集最優(yōu)狀態(tài)序列 def Viterbi(sentence,array_pi,array_a,array_b):tab = [{}] #動態(tài)規(guī)劃表path = {}if sentence[0] not in array_b['B']:for state in STATES:if state == 'S':array_b[state][sentence[0]] = 0else:array_b[state][sentence[0]] = -3.14e+100for state in STATES:tab[0][state] = array_pi[state] + array_b[state][sentence[0]]# print(tab[0][state])#tab[t][state]表示時刻t到達(dá)state狀態(tài)的所有路徑中,概率最大路徑的概率值path[state] = [state]for i in range(1,len(sentence)):tab.append({})new_path = {}# if sentence[i] not in array_b['B']:# print(sentence[i-1],sentence[i])for state in STATES:if state == 'B':array_b[state]['begin'] = 0else:array_b[state]['begin'] = -3.14e+100for state in STATES:if state == 'E':array_b[state]['end'] = 0else:array_b[state]['end'] = -3.14e+100for state0 in STATES:items = []# if sentence[i] not in word_set:# array_b[state0][sentence[i]] = -3.14e+100# if sentence[i] not in array_b[state0]:# array_b[state0][sentence[i]] = -3.14e+100# print(sentence[i] + state0)# print(array_b[state0][sentence[i]])for state1 in STATES:# if tab[i-1][state1] == -3.14e+100:# continue# else:if sentence[i] not in array_b[state0]: #所有在測試集出現(xiàn)但沒有在訓(xùn)練集中出現(xiàn)的字符if sentence[i-1] not in array_b[state0]:prob = tab[i - 1][state1] + array_a[state1][state0] + array_b[state0]['end']else:prob = tab[i - 1][state1] + array_a[state1][state0] + array_b[state0]['begin']# print(sentence[i])# prob = tab[i-1][state1] + array_a[state1][state0] + array_b[state0]['other']else:prob = tab[i-1][state1] + array_a[state1][state0] + array_b[state0][sentence[i]] #計算每個字符對應(yīng)STATES的概率 # print(prob)items.append((prob,state1))# print(sentence[i] + state0)# print(array_b[state0][sentence[i]])# print(sentence[i])# print(items)best = max(items) #bset:(prob,state)# print(best)tab[i][state0] = best[0]# print(tab[i][state0])new_path[state0] = path[best[1]] + [state0]path = new_pathprob, state = max([(tab[len(sentence) - 1][state], state) for state in STATES])return path[state]#根據(jù)狀態(tài)序列進(jìn)行分詞 def tag_seg(sentence,tag):word_list = []start = -1started = Falseif len(tag) != len(sentence):return Noneif len(tag) == 1:word_list.append(sentence[0]) #語句只有一個字,直接輸出else:if tag[-1] == 'B' or tag[-1] == 'M': #最后一個字狀態(tài)不是'S'或'E'則修改if tag[-2] == 'B' or tag[-2] == 'M':tag[-1] = 'E'else:tag[-1] = 'S'for i in range(len(tag)):if tag[i] == 'S':if started:started = Falseword_list.append(sentence[start:i])word_list.append(sentence[i])elif tag[i] == 'B':if started:word_list.append(sentence[start:i])start = istarted = Trueelif tag[i] == 'E':started = Falseword = sentence[start:i + 1]word_list.append(word)elif tag[i] == 'M':continuereturn word_listif __name__ == '__main__':trainset = open('CTBtrainingset.txt', encoding='utf-8') #讀取訓(xùn)練集testset = open('CTBtestingset.txt', encoding='utf-8') #讀取測試集# trainlist = []Init_Array()for line in trainset:line = line.strip()# trainlist.append(line)line_num += 1word_list = []for k in range(len(line)):if line[k] == ' ':continueword_list.append(line[k])# print(word_list)word_set = word_set | set(word_list) #訓(xùn)練集所有字的集合line = line.split(' ')# print(line)line_state = [] #這句話的狀態(tài)序列for i in line:line_state.extend(get_tag(i))# print(line_state)array_Pi[line_state[0]] += 1 # array_Pi用于計算初始狀態(tài)分布概率for j in range(len(line_state)-1):# count_dic[line_state[j]] += 1 #記錄每一個狀態(tài)的出現(xiàn)次數(shù)array_A[line_state[j]][line_state[j+1]] += 1 #array_A計算狀態(tài)轉(zhuǎn)移概率for p in range(len(line_state)):count_dic[line_state[p]] += 1 # 記錄每一個狀態(tài)的出現(xiàn)次數(shù)for state in STATES:if word_list[p] not in array_B[state]:array_B[state][word_list[p]] = 0.0 #保證每個字都在STATES的字典中# if word_list[p] not in array_B[line_state[p]]:# # print(word_list[p])# array_B[line_state[p]][word_list[p]] = 0# else:array_B[line_state[p]][word_list[p]] += 1 # array_B用于計算發(fā)射概率Prob_Array() #對概率取對數(shù)保證精度print('參數(shù)估計結(jié)果')print('初始狀態(tài)分布')print(array_Pi)print('狀態(tài)轉(zhuǎn)移矩陣')print(array_A)print('發(fā)射矩陣')print(array_B)output = ''for line in testset:line = line.strip()tag = Viterbi(line, array_Pi, array_A, array_B)# print(tag)seg = tag_seg(line, tag)# print(seg)list = ''for i in range(len(seg)):list = list + seg[i] + ' '# print(list)output = output + list + '\n'print(output)outputfile = open('output.txt', mode='w', encoding='utf-8')outputfile.write(output)

參考鏈接:

http://www.leexiang.com/hidden-markov-model

http://www.52nlp.cn/category/hidden-markov-model

最后如果轉(zhuǎn)載,麻煩留個本文的鏈接,因為如果讀者或我自己發(fā)現(xiàn)文章有錯誤,我會在這里更正,留個本文的鏈接,防止我暫時的疏漏耽誤了他人寶貴的時間。

總結(jié)

以上是生活随笔為你收集整理的基于HMM的中文分词的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。