文章目錄
- 分詞
- 1. 基于AC自動機的分詞
- 2. 基于切分的新詞發現
- 3. 字标注法與HMM(隐馬爾科夫模型)
- HMM
- 代碼
- 4. 基于雙向LSTM的seq2seq字标注
- 5. 基于語言模型的無監督分詞
- 6. 基于全卷積網絡的中文分詞
- 7. 深度學習分詞?隻需一個詞典
- 8. 更好的新詞發現算法
本文主要是對蘇神的多篇分詞部落格的學習和總結
【中文分詞系列】 1. 基于AC自動機的快速分詞
分詞
中文分詞主要有兩種思路:查詞典和字标注。機械的最大比對法、最少詞數法,以及基于有向無環圖的最大機率組合,還有基于語言模型的最大機率組合。最大機率法尤其是結合了語言模型的最大機率法,能夠很好地而解決歧義問題 (歧義問題這裡有較詳細的描述漫話中文自動分詞和語義識别(上):中文分詞算法)
另外一個問題則是未登入詞,人們也提出了基于字标注的思路(通過sbme标注字),後面詳細總結一下。
1. 基于AC自動機的分詞
AC自動機實際上是字典樹+KMP來完成的快速比對詞表,這個我之前寫過一篇,可以簡單看下字典樹trie與分詞 蘇神寫了三種基于AC自動機的分詞法
- 最大比對法:左到右逐漸比對詞庫中的詞語,比對到最長的詞語為止
- 最大機率組合:分詞的結果機率作乘法後的機率最大
- 最少詞比對:以分詞的個數最少為目标獲得的分詞結果
後兩種方法都會用到動态規劃的方式來擷取每個位置的最優分詞路徑。
2. 基于切分的新詞發現
新詞發現這塊是比較有趣的,這裡分為兩個思路,第一個思路是網際網路時代的社會語言學:基于SNS的文本資料挖掘,總結而言就是通過片段的頻數、片段的凝固度和片段的左右資訊熵各取一個門檻值來判斷一個片段是否可以成詞。
- 定義“電影院”的凝合程度就是 p(電影院) 與 p(電) · p(影院) 比值和 p(電影院) 與 p(電影) · p(院) 的比值中的較小值,“的電影”的凝合程度則是 p(的電影) 分别除以 p(的) · p(電影) 和 p(的電) · p(影) 所得的商的較小值
- “資訊熵”是一個非常神奇的概念,可以具體看下文章,考慮一個片段的左鄰字和右鄰字的資訊熵,資訊熵越大,表示成詞的機率越大
以上思路蘇神有代碼實作過新詞發現的資訊熵方法與實作
第二種思路,也巧妙的令人興奮,我們知道第一種思路判斷的是片段的凝固度大于一定程度可以成詞,反過來思考,當片段的額凝固度低于一定程度時,則不成詞,我們就可以在語料中斷開。
簡化為以下思路,如果a,b是語料相鄰兩字,統計(a,b)成對出現的次數,繼而估計它的頻率,然後我們分别統計a,b出現的次數,,然後估計它們的頻率,。若
則把這兩個字斷開,初步分詞後,再統計詞頻,根據詞頻篩選。
這個思路的優勢在于
- 隻需要計算兩個字的凝固度,省去了很多片段如對于一個三字片段,21和12的凝固度
- 分詞的長度沒有限制,隻要一個詞的相鄰字凝固度比較高,就不會被分開
【中文分詞系列】 2. 基于切分的新詞發現
3. 字标注法與HMM(隐馬爾科夫模型)
通過字标注法來進行分詞的模型有隐馬爾科夫模型(HMM)、最大熵模型(ME)、條件随機場模型(CRF),它們在精度上都是遞增的。
關于字标注法,複制了一段蘇神的了解:“字标注法有效有兩個主要的原因,第一個原因是它将分詞問題變成了一個序列标注問題,而且這個标注是對齊的,也就是輸入的字跟輸出的标簽是一一對應的,這在序列标注中是一個比較成熟的問題;第二個原因是這個标注法實際上已經是一個總結語義規律的過程,以4tag标注為為例,我們知道,“李”字是常用的姓氏,一半作為多字詞(人名)的首字,即标記為b;而“想”由于“理想”之類的詞語,也有比較高的比例标記為e,這樣一來,要是“李想”兩字放在一起時,即便原來詞表沒有“李想”一詞,我們也能正确輸出be,也就是識别出“李想”為一個詞,也正是因為這個原因,即便是常被視為最不精确的HMM模型也能起到不錯的效果”
HMM
按蘇神的思路推導一下,假設輸入n個字,輸出n個标簽(sbme),用表示輸入,表示輸出,用機率來表示最優的輸出,我們希望
簡言之,o有很多可能性,最優的o應該是最大機率的o
要涉及2n個變量,直接計算是很困難的,簡化一下,假設每個字的輸出僅僅與目前字有關,則有
,這樣要使最大,隻需每個最大,以上假設稱為馬爾科夫假設。
以上假設沒有考慮上下文,會出現不合理的情況,例如b後面隻能接m或e,上述簡化會出現bbb的情況,我們需要把類似于轉移機率放進上述公式,提出了一種隐馬爾科夫模型。
由貝葉斯公式
由于x是給定的輸入,是常數可以忽略,最大化就等價于最大化
我們可以對作馬爾可夫假設,得到
同理,對于有
又可以作另一個馬爾可夫假設:每個輸出僅僅與上一個輸出有關,
這裡後面省去了,因為這個要估的話大概是s和b都為0.5,對結果影響不大。
最終,
我們稱為發射機率,為轉移機率,可以通過設定來排除bb、bs等不合理的組合。
當然,也可以把馬爾可夫假設加強——比如假設每個狀态跟前面兩個狀态有關,那樣肯定會得到更精确的模型,但是模型的參數就更難估計了。
代碼
蘇神的代碼分為三部分
- 根據文本或詞典估計
- 估計轉移機率
- 通過viterbi算法動态規劃求最大機率路徑,一句話總結,算出每一個位置分别為sbme的最優路徑
from collections import Counter
def read_dict(path):
hmm_d = {c:Counter() for c in 'sbme'}
with open(path,'r') as f:
for line in f:
line = line.strip().split('\t')
if len(line[0])==1:
# 根據詞頻擷取字的頻數
hmm_d['s'][line[0]] += int(line[1])
else:
hmm_d['b'][line[0]] += int(line[1])
hmm_d['e'][line[0][-1]] += int(line[1])
for m in line[0][1:-1]:
hmm_d['m'][m] += int(line[1])
return hmm_d
path = 'dict.txt'
hmm_d = read_dict(path)
# 計算sbme的總數
log_total = {i:log(sum(hmm_d[i].values())) for i in 'sbme'}
trans = {'ss':0.3, 'sb':0.7, 'bm':0.3, 'be':0.7, 'mm':0.3, 'me':0.7, 'es':0.3, 'eb':0.7}
trans = {i: log(j) for i,j in trans.items()}
# viterbi算法計算最大機率路徑
def viterbi(nodes):
"""nodes = [{'s':,'b':},{},{}]"""
paths = nodes[0]
for i in range(1,len(nodes)):
paths_ = paths
paths = {}
# j可能為sbme,找到目前位置每個标注對應的最大機率
for j in nodes[i].keys():
tmp = {}
# k表示目前的标注路徑 v表示value
for k,v in paths.items():
if k[-1]+j in trans:
tmp[k+j] = v+trans[k[-1]+j]+nodes[i][j]
k = tmp.keys().index(max(tmp.values()))
paths[tmp.keys()[k]] = tmp.values()[k]
return paths.keys()[paths.values().index(max(paths.values))]
def hmm_cut(s):
"""分詞,計算分詞結果"""
nodes = [{i: log(j[c]+1)-log_total(i)} for i,j in hmm_d for c in s]
res = viterbi(nodes)
words = [s[0]]
for i in range(1,len(res)):
if res[i] in ['s','b']:
words.append(s[i])
else:
words[-1] += s[i]
return words
【中文分詞系列】 3. 字标注法與HMM模型
4. 基于雙向LSTM的seq2seq字标注
一句話總結下,利用雙向LSTM直接預測smbe标簽,并結合viterbi算法(加入轉移機率)輸出最終預測結果。
值得實踐下!
【中文分詞系列】 4. 基于雙向LSTM的seq2seq字标注
5. 基于語言模型的無監督分詞
本篇部落格介紹了一種應用語言模型來完成無監督分詞的思路。
總結前面的分詞思路,字标注的方式輸出了每個位置不同标注的機率值,通過viterbi算法結合轉移機率輸出最大機率路徑,也就得到了分詞結果。
這裡采用了四字元标注,為了保證可以出現大于4個字的情況e後面可以繼續跟e,但是轉移機率設的很小
- b:單字詞或者多字詞的首字
- c:多字詞的第二字
- d:多字詞的第三字
-
e:多字詞的其餘部分
轉移機率還是根據直覺設計,發射機率則是通過語言模型擷取
擷取的代碼如下,因為
model.score
輸出的機率值是log後的,是以每一個位置的機率通過減前一個位置獲得
def cp(s):
return (model.score(' '.join(s), bos=False, eos=False) - model.score(' '.join(s[:-1]), bos=False, eos=False)) or -100.0
【中文分詞系列】 5. 基于語言模型的無監督分詞 熟悉了分詞套路的同學覺得更多的新意在于語言模型,貼兩篇關于kenlm的文章
原理推導 圖解N-gram語言模型的原理–以kenlm為例
kenlm實踐 python | 高效使用統計語言模型kenlm:新詞發現、分詞、智能糾錯等
6. 基于全卷積網絡的中文分詞
基于全卷積網絡的中文分詞與前面的分詞類似,通過模型訓練一個字标注“smbe”的标簽機率值,通過viterbi算法輸出最終結果。
這裡蘇神提供了一種寫死字典的方式,将字典的取值融入到深度學習訓練結果。具體操作如下
添加一個add_dict.txt檔案,每一行是一個詞,包括詞語和倍數,這個倍數就是要将相應的标簽機率擴大的倍數,比如詞表中指定詞語“科學空間,10”,而對“科學空間挺好”進行分詞時,先用模型得到這六個字的标簽機率,然後查找發現“科學空間”這個詞在這個句子裡邊,是以将第一個字為s的機率乘以10,将第二、三個字為m的機率乘以10,将第4個字為e的機率乘以10(不用歸一化,因為隻看相對值就行了),同樣地,如果某些地方切漏了(該切的沒有切),也可以加入到詞表中,然後設定小于1的倍數就行了。
這種思路值得借鑒下。
【中文分詞系列】 6. 基于全卷積網絡的中文分詞
7. 深度學習分詞?隻需一個詞典
隻有一個詞典,怎麼訓練分詞呢?蘇神給出的答案是:随機組合。把詞典中的詞随機組合,就有了有标注的資料,采用之前的lstm方式訓練即可
- 首先得準備一個帶詞頻的詞表,詞頻是必須的,如果沒有詞頻,則效果會大打折扣,因為需要以正比于詞頻的機率,随機挑選詞表中的詞語,組合成“句子”。
這邊文章主要的特點就是利用詞典和詞頻,以詞頻作為權重,随機抽取一些詞組成句子訓練,在有詞典的情況下,相當于無監督學習,思路奇特,簡單且确實效果可以,并且詞典的優化有助于效果的提升。
【中文分詞系列】 7. 深度學習分詞?隻需一個詞典!
8. 更好的新詞發現算法
- 第一步,統計:選取某個固定的n,統計2grams、3grams、…、ngrams,計算它們的内部凝固度(凝固度計算可以參考前面的文章),隻保留高于某個門檻值的片段,構成一個集合G。多字的凝固度計算有很大的好處,比如前面新詞發現算法會受制于“和國”比較低,導緻分不出“共和國”,并且不會把“共和”分為一個詞。
- 第二步,切分:用上述grams對語料進行切分(粗糙的分詞),并統計頻數。注意這裡切分的粒度很粗。比如“各項目”,主要“各項”和“項目”都在G中,就算“各項目”不在G中,也不會切分。統計分詞的結果,篩選出高頻部分。
- 第三步,回溯:經過第二步,“各項目”會被切出來(因為第二步保證甯放過,不切錯)。回溯就是檢查,如果它是一個小于等于n字的詞,那麼檢測它在不在G中,不在就出局;如果它是一個大于n字的詞,那個檢測它每個n字片段是不是在G中,隻要有一個片段不在,就出局。還是以“各項目”為例,回溯就是看看,“各項目”在不在3gram中,不在的話,就得出局。上面的“各項目”由于不在凝固度高的ngrams中,是以會被移除。