自然语言处理3 -- 词性标注
系列文章,請(qǐng)多關(guān)注
Tensorflow源碼解析1 – 內(nèi)核架構(gòu)和源碼結(jié)構(gòu)
NLP預(yù)訓(xùn)練模型1 – 綜述
Transformer家族1 – Transformer詳解和源碼分析
自然語(yǔ)言處理1 – 分詞
自然語(yǔ)言處理2 – jieba分詞用法及原理
自然語(yǔ)言處理3 – 詞性標(biāo)注
自然語(yǔ)言處理4 – 句法分析
自然語(yǔ)言處理5 – 詞向量
自然語(yǔ)言處理6 – 情感分析
1 概述
詞性標(biāo)注在自然語(yǔ)言處理中也屬于基礎(chǔ)性的模塊,為句法分析、信息抽取等工作打下基礎(chǔ)。和分詞一樣,中文詞性標(biāo)注也存在著很多難點(diǎn),比如一詞多詞性,未登錄詞處理等諸多問(wèn)題。通過(guò)基于字符串匹配的字典查詢(xún)算法和基于統(tǒng)計(jì)的詞性標(biāo)注算法,可以很好的解決這些問(wèn)題。一般需要先將語(yǔ)句進(jìn)行分詞,然后再進(jìn)行詞性標(biāo)注。
2 詞性標(biāo)注難點(diǎn)
詞性作為詞語(yǔ)基本的語(yǔ)法屬性,是詞語(yǔ)和語(yǔ)句的關(guān)鍵性特征。詞性種類(lèi)也很多,ICTCLAS 漢語(yǔ)詞性標(biāo)注集歸納的詞性種類(lèi)及其表示見(jiàn) https://www.cnblogs.com/chenbjin/p/4341930.html。詞性標(biāo)注中的難點(diǎn)主要有
3 詞性標(biāo)注算法
和分詞一樣,詞性標(biāo)注算法也分為兩大類(lèi),基于字符串匹配的字典查找算法和基于統(tǒng)計(jì)的算法。jieba分詞就綜合了兩種算法,對(duì)于分詞后識(shí)別出來(lái)的詞語(yǔ),直接從字典中查找其詞性。而對(duì)于未登錄詞,則采用HMM隱馬爾科夫模型和viterbi算法來(lái)識(shí)別。
3.1 基于字符串匹配的字典查找算法
先對(duì)語(yǔ)句進(jìn)行分詞,然后從字典中查找每個(gè)詞語(yǔ)的詞性,對(duì)其進(jìn)行標(biāo)注即可。jieba詞性標(biāo)注中,對(duì)于識(shí)別出來(lái)的詞語(yǔ),就是采用了這種方法。這種方法比較簡(jiǎn)單,通俗易懂,但是不能解決一詞多詞性的問(wèn)題,因此存在一定的誤差。
下圖即為jieba分詞中的詞典的一部分詞語(yǔ)。每一行對(duì)應(yīng)一個(gè)詞語(yǔ),分為三部分,分別為詞語(yǔ)名 詞數(shù) 詞性。因此分詞完成后只需要在字典中查找該詞語(yǔ)的詞性即可對(duì)其完成標(biāo)注。
3.2 基于統(tǒng)計(jì)的詞性標(biāo)注算法
和分詞一樣,我們也可以通過(guò)HMM隱馬爾科夫模型來(lái)進(jìn)行詞性標(biāo)注。觀測(cè)序列即為分詞后的語(yǔ)句,隱藏序列即為經(jīng)過(guò)標(biāo)注后的詞性標(biāo)注序列。起始概率 發(fā)射概率和轉(zhuǎn)移概率和分詞中的含義大同小異,可以通過(guò)大規(guī)模語(yǔ)料統(tǒng)計(jì)得到。觀測(cè)序列到隱藏序列的計(jì)算可以通過(guò)viterbi算法,利用統(tǒng)計(jì)得到的起始概率 發(fā)射概率和轉(zhuǎn)移概率來(lái)得到。得到隱藏序列后,就完成了詞性標(biāo)注過(guò)程。
4 jieba詞性標(biāo)注原理
jieba在分詞的同時(shí),可以進(jìn)行詞性標(biāo)注。利用jieba.posseg模塊來(lái)進(jìn)行詞性標(biāo)注,會(huì)給出分詞后每個(gè)詞的詞性。詞性標(biāo)示兼容ICTCLAS 漢語(yǔ)詞性標(biāo)注集,可查閱網(wǎng)站https://www.cnblogs.com/chenbjin/p/4341930.html
import jieba.posseg as pseg words = pseg.cut("我愛(ài)北京天安門(mén)") for word, flag in words: ... print('%s %s' % (word, flag)) ... 我 r # 代詞 愛(ài) v # 動(dòng)詞 北京 ns # 名詞 天安門(mén) ns # 名詞下面來(lái)對(duì)pseg.cut()進(jìn)行詳細(xì)的分析,其主要流程為
4.1 準(zhǔn)備工作
準(zhǔn)備工作中做的事情和jieba分詞基本一致,check字典是否初始化好,如果沒(méi)有則先初始化字典。將語(yǔ)句轉(zhuǎn)為UTF-8或者GBK。根據(jù)正則匹配,將輸入文本分隔成一個(gè)個(gè)語(yǔ)句。代碼如下。
def __cut_internal(self, sentence, HMM=True):# 如果沒(méi)有字典沒(méi)有初始化,則先加載字典。否則直接使用字典緩存即可。self.makesure_userdict_loaded()# 將語(yǔ)句轉(zhuǎn)為UTF-8或者GBKsentence = strdecode(sentence)# 根據(jù)正則匹配,將輸入文本分隔成一個(gè)個(gè)語(yǔ)句。分隔符包括空格 逗號(hào) 句號(hào)等。blocks = re_han_internal.split(sentence)# 根據(jù)是否采用了HMM模型來(lái)進(jìn)行不同方法的選擇if HMM:cut_blk = self.__cut_DAGelse:cut_blk = self.__cut_DAG_NO_HMM# 遍歷正則匹配分隔好的語(yǔ)句,對(duì)每個(gè)語(yǔ)句進(jìn)行單獨(dú)的分詞和詞性標(biāo)注for blk in blocks:if re_han_internal.match(blk):# 分詞和詞性標(biāo)注for word in cut_blk(blk):yield wordelse:tmp = re_skip_internal.split(blk)for x in tmp:if re_skip_internal.match(x):yield pair(x, 'x')else:for xx in x:if re_num.match(xx):yield pair(xx, 'm')elif re_eng.match(x):yield pair(xx, 'eng')else:yield pair(xx, 'x')4.2 遍歷語(yǔ)句,進(jìn)行分詞和詞性標(biāo)注
步驟和jieba分詞基本一致,主體步驟如下,詳細(xì)的每個(gè)步驟見(jiàn) 自然語(yǔ)言處理2jieba分詞用法及原理
其中word_tag_tab在初始化加載詞典階段構(gòu)建得到,它使用詞語(yǔ)為key,對(duì)應(yīng)詞性為value。代碼如下
def load_word_tag(self, f):self.word_tag_tab = {}f_name = resolve_filename(f)# 遍歷字典的每一行。每一行對(duì)應(yīng)一個(gè)詞語(yǔ)。包含詞語(yǔ) 詞數(shù) 詞性三部分for lineno, line in enumerate(f, 1):try:# 去除首尾空格符line = line.strip().decode("utf-8")if not line:continue# 利用空格將一行分隔為詞語(yǔ) 詞數(shù) 詞性三部分word, _, tag = line.split(" ")# 使用詞語(yǔ)為key,詞性為value,構(gòu)造Dictself.word_tag_tab[word] = tagexcept Exception:raise ValueError('invalid POS dictionary entry in %s at Line %s: %s' % (f_name, lineno, line))f.close()4.3 未登錄詞,HMM隱馬爾科夫模型處理
和分詞一樣,詞性標(biāo)注中,也使用HMM隱馬爾科夫模型來(lái)處理未登錄詞。通過(guò)大規(guī)模語(yǔ)料統(tǒng)計(jì),得到起始概率 發(fā)射概率和轉(zhuǎn)移概率。分別對(duì)應(yīng)prob_start.py prob_emit.py和prob_trans.py三個(gè)文件,他們給出了詞語(yǔ)在BEMS四種情況下,每種詞性對(duì)應(yīng)的概率。然后使用viterbi算法,利用得到的三個(gè)概率,將觀測(cè)序列(分詞后的語(yǔ)句)轉(zhuǎn)化得到隱藏序列(詞性標(biāo)注序列)。這樣就完成了未登錄詞的詞性標(biāo)注。代碼如下。
# 通過(guò)HMM隱馬爾科夫模型獲取詞性標(biāo)注序列,解決未登錄的問(wèn)題def __cut(self, sentence):# 通過(guò)viterbi算法,利用三個(gè)概率,由語(yǔ)句觀測(cè)序列,得到詞性標(biāo)注隱藏序列# prob為# pos_list對(duì)應(yīng)每個(gè)漢字,包含分詞標(biāo)注BEMS和詞語(yǔ)詞性?xún)刹糠帧?/span>prob, pos_list = viterbi(sentence, char_state_tab_P, start_P, trans_P, emit_P)begin, nexti = 0, 0# 遍歷語(yǔ)句的每個(gè)漢字,如果是E或者S時(shí),也就是詞語(yǔ)結(jié)束或者單字詞語(yǔ),則分隔得到詞語(yǔ)和詞性pairfor i, char in enumerate(sentence):pos = pos_list[i][0]if pos == 'B':# B表示詞語(yǔ)的開(kāi)始begin = ielif pos == 'E':# E表示詞語(yǔ)的結(jié)束,此時(shí)輸出詞語(yǔ)和他的詞性yield pair(sentence[begin:i + 1], pos_list[i][1])nexti = i + 1elif pos == 'S':# S表示單字詞語(yǔ),此時(shí)也輸出詞語(yǔ)和他的詞性yield pair(char, pos_list[i][1])nexti = i + 1# 一般不會(huì)走到這兒,以防萬(wàn)一。對(duì)剩余的所有漢字一起輸出一個(gè)詞語(yǔ)和詞性。if nexti < len(sentence):yield pair(sentence[nexti:], pos_list[nexti][1])觀測(cè)序列到隱藏序列的計(jì)算,則通過(guò)viterbi算法實(shí)現(xiàn)。代碼如下
# 通過(guò)viterbi算法,由觀測(cè)序列,也就是語(yǔ)句,來(lái)得到隱藏序列,也就是BEMS標(biāo)注序列和詞性標(biāo)注序列 # obs為語(yǔ)句,states為"BEMS"四種狀態(tài), # start_p為起始概率, trans_p為轉(zhuǎn)移概率, emit_p為發(fā)射概率,三者通過(guò)語(yǔ)料訓(xùn)練得到 def viterbi(obs, states, start_p, trans_p, emit_p):V = [{}] # 每個(gè)漢字的每個(gè)BEMS狀態(tài)的最大概率。mem_path = [{}] # 分詞路徑# 初始化每個(gè)state,states為"BEMS"all_states = trans_p.keys()for y in states.get(obs[0], all_states): # initV[0][y] = start_p[y] + emit_p[y].get(obs[0], MIN_FLOAT)mem_path[0][y] = ''# 逐字進(jìn)行處理for t in xrange(1, len(obs)):V.append({})mem_path.append({})#prev_states = get_top_states(V[t-1])prev_states = [x for x in mem_path[t - 1].keys() if len(trans_p[x]) > 0]prev_states_expect_next = set((y for x in prev_states for y in trans_p[x].keys()))obs_states = set(states.get(obs[t], all_states)) & prev_states_expect_nextif not obs_states:obs_states = prev_states_expect_next if prev_states_expect_next else all_states# 遍歷每個(gè)狀態(tài)for y in obs_states:# 計(jì)算前一個(gè)狀態(tài)到本狀態(tài)的最大概率和它的前一個(gè)狀態(tài)prob, state = max((V[t - 1][y0] + trans_p[y0].get(y, MIN_INF) +emit_p[y].get(obs[t], MIN_FLOAT), y0) for y0 in prev_states)# 將該漢字下的某狀態(tài)(BEMS)的最大概率記下來(lái)V[t][y] = prob# 記錄狀態(tài)轉(zhuǎn)換路徑mem_path[t][y] = statelast = [(V[-1][y], y) for y in mem_path[-1].keys()]# if len(last)==0:# print obsprob, state = max(last)route = [None] * len(obs)i = len(obs) - 1while i >= 0:route[i] = statestate = mem_path[i][state]i -= 1return (prob, route)5 總結(jié)
jieba可以在分詞的同時(shí),完成詞性標(biāo)注,因此標(biāo)注速度可以得到保證。通過(guò)查詢(xún)字典的方式獲取識(shí)別詞的詞性,通過(guò)HMM隱馬爾科夫模型來(lái)獲取未登錄詞的詞性,從而完成整個(gè)語(yǔ)句的詞性標(biāo)注。但可以看到查詢(xún)字典的方式不能解決一詞多詞性的問(wèn)題,也就是詞性歧義問(wèn)題。故精度上還是有所欠缺的。
系列文章,請(qǐng)多關(guān)注
Tensorflow源碼解析1 – 內(nèi)核架構(gòu)和源碼結(jié)構(gòu)
NLP預(yù)訓(xùn)練模型1 – 綜述
Transformer家族1 – Transformer詳解和源碼分析
自然語(yǔ)言處理1 – 分詞
自然語(yǔ)言處理2 – jieba分詞用法及原理
自然語(yǔ)言處理3 – 詞性標(biāo)注
自然語(yǔ)言處理4 – 句法分析
自然語(yǔ)言處理5 – 詞向量
自然語(yǔ)言處理6 – 情感分析
總結(jié)
以上是生活随笔為你收集整理的自然语言处理3 -- 词性标注的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Lambda-Stream应用
- 下一篇: 9.17整型常量