在看源碼之前,先看幾遍論文《基于角色標注的中國人名自動識別研究》
成都網站設計、成都做網站服務團隊是一支充滿著熱情的團隊,執著、敏銳、追求更好,是創新互聯的標準與要求,同時竭誠為客戶提供服務是我們的理念。創新互聯公司把每個網站當做一個產品來開發,精雕細琢,追求一名工匠心中的細致,我們更用心!
關于命名識別的一些問題,可參考下列一些 issue:
u u名字識別的問題 #387
u u機構名識別錯誤
u u關于層疊HMM中文實體識別的過程
HanLP參考博客:
詞性標注
層疊 HMM-Viterbi角色標注模型下的機構名識別
分詞
在 HMM與分詞、詞性標注、命名實體識別中說:
分詞:給定一個字的序列,找出最可能的標簽序列(斷句符號: [詞尾]或[非詞尾]構成的序列)。結巴分詞目前就是利用BMES標簽來分詞的,B(開頭),M(中間),E(結尾),S(獨立成詞)
分詞也是采用了維特比算法的動態規劃性質求解的,具體可參考:文本挖掘的分詞原理
角色觀察
以 “唱首張學友的歌情已逝”為例,
先將起始頂點 始 ##始,角色標注為:NR.A 和 NR.K,頻次默認為1
對于第一個詞 “唱首”,它不存在于 nr.txt中, EnumItem<NR> nrEnumItem = PersonDictionary.dictionary.get(vertex.realWord); 返回 null,于是根據它本身的詞性猜一個角色標注:
由于 "唱首"的Attribute為 nz 16,不是nr 和 nnt,故默認給它指定一個角色NR.A,頻率為nr.tr.txt中 NR.A 角色的總頻率。
此時,角色列表如下:
接下來是頂點 “張”,由于“張”在nr.txt中,因此 PersonDictionary.dictionary.get(vertex.realWord) 返回 EnumItem對象,直接將它加入到角色列表中:
加入 “張”之后的角色列表如下:
“唱首張學友的歌情已逝” 整句的角色列表如下:
至此,角色觀察 部分 就完成了。
總結一下,對句子進行角色觀察,首先是通過分詞算法將句子分成若干個詞,然后對每個詞查詢人名詞典 (PersonDictionary)。
u 若這個詞在人名詞典中 (nr.txt),則記錄該詞的角色,所有的角色在 com.hankcs.hanlp.corpus.tag.NR.java 中定義。
u 若這個詞不在人名詞典中,則根據該詞的 Attribute “猜一個角色”。在猜的過程中,有些詞在核心詞典中可能已經標注為nr或者nnt了,這時會做分裂處理。其他情況下則是將這個詞標上NR.A角色,頻率為 NR.A 在轉移矩陣中的總詞頻。
維特比算法 (動態規劃)求解最優路徑
在上圖中,給每個詞都打上了角色標記,可以看出,一個詞可以有多個標記。而我們需要將這些詞選擇一條路徑最短的角色路徑。參考 隱馬爾可夫模型維特比算法詳解
List<NR> nrList = viterbiComputeSimply(roleTagList); //some code.... return Viterbi.computeEnumSimply(roleTagList, PersonDictionary.transformMatrixDictionary);
而這個過程,其實就是:維特比算法解碼隱藏狀態序列。在這里,五元組是:
u 隱藏狀態集合 com.hankcs.hanlp.corpus.tag.NR.java 定義的各個人名標簽
u 觀察狀態集合 已經分好詞的各個 tagList中元素(相當于分詞結果)
u 轉移概率矩陣 由 nr.tr.txt 文件生成得到。具體可參考:
u 發射概率 某個人名標簽 (隱藏狀態)出現的次數 除以 所有標簽出現的總次數
Math.log((item.getFrequency(cur) + 1e-8) / transformMatrixDictionary.getTotalFrequency(cur)
u 初始狀態 (始##始) 和 結束狀態(末##末)
維特比解碼隱藏狀態的動態規劃求解核心代碼如下:
for (E cur : item.labelMap.keySet())
{
double now = transformMatrixDictionary.transititon_probability[pre.ordinal()][cur.ordinal()] - Math.log((item.getFrequency(cur) + 1e-8 ) / transformMatrixDictionary.getTotalFrequency(cur));
if (perfect_cost > now)
{
perfect_cost = now;
perfect_tag = cur;
}
}
transformMatrixDictionary.transititon_probability[pre.ordinal()][cur.ordinal()] 是前一個隱藏狀態 pre.ordinal() 轉換到當前隱藏狀態 cur.ordinal() 的轉移概率。 Math.log((item.getFrequency(cur) + 1e-8) / transformMatrixDictionary.getTotalFrequency(cur) 是當前隱藏狀態的發射概率。二者 “相減”得到一個概率 保存在 double now 變量中,然后通過 for 循環找出 當前觀察狀態 對應的 最可能的(perfect_cost最小) 隱藏狀態 perfect_tag。
至于為什么是上面那個公式來計算轉移概率和發射概率,可參考論文:《 基于角色標注的中國人名自動識別研究 》
在上面例子中,得到的最優隱藏狀態序列 (最優路徑)K->A->K->Z->L->E->A->A 如下:
nrList = {LinkedList@1065} size = 8
"K" 始##始
"A" 唱首
"K" 張
"Z" 學友
"L" 的
"E" 歌
"A" 情已逝
"A" 末##末
例如:
隱藏狀態---觀察狀態
"K"----------始##始
最大匹配
有了最優隱藏序列: KAKZLEAA,接下來就是:后續的“最大匹配處理”了。
PersonDictionary.parsePattern(nrList, pWordSegResult, wordNetOptimum, wordNetAll);
在最大匹配之前,會進行 “模式拆分”。在com.hankcs.hanlp.corpus.tag.NR.java 定義了隱藏狀態的具體含義。比如說,若最優隱藏序列中 存在 'U' 或者 'V',
U Ppf 人名的上文和姓成詞 這里【有關】天培的壯烈
V Pnw 三字人名的末字和下文成詞 龔學平等領導, 鄧穎【超生】前
則會做 “拆分處理”
switch (nr)
{
case U:
//拆分成K B
case V:
//視情況拆分
}
拆分完成之后,重新得到一個新的隱藏序列 (模式)
String pattern = sbPattern.toString();
接下來,就用 AC自動機進行最大模式匹配了,并將匹配的結果存儲到“最優詞網”中。當然,在這里就可以自定義一些針對特定應用的 識別處理規則
trie.parseText(pattern, new AhoCorasickDoubleArrayTrie.IHit<NRPattern>(){
//.....
wordNetOptimum.insert(offset, new Vertex(Predefine.TAG_PEOPLE, name, ATTRIBUTE, WORD_ID), wordNetAll);
}
將識別出來的人名保存到最優詞網后,再基于最優詞網調用一次維特比分詞算法,得到最終的分詞結果 ---細分結果。
if (wordNetOptimum.size() != preSize)
{
vertexList = viterbi(wordNetOptimum);
if (HanLP.Config.DEBUG)
{
System.out.printf( "細分詞網:\n%s\n" , wordNetOptimum);
}
}
總結
源碼上的人名識別基本上是按照論文中的內容來實現的。對于一個給定的句子,先進行下面三大步驟處理:
角色觀察
維特比算法解碼求解隱藏狀態(求解各個分詞 的 角色標記)
對角色標記進行最大匹配(可做一些后處理操作)
最后,再使用維特比算法進行一次分詞,得到細分結果,即為最后的識別結果。
這篇文章里面沒有寫維特比分詞算法的詳細過程,以及轉移矩陣的生成過程,以后有時間再補上。看源碼,對隱馬模型的理解又加深了一點,感受到了理論的東西如何用代碼一步步來實現。由于我也是初學,對源碼的理解不夠深入或者存在一些偏差,歡迎批評指正。
關于動態規劃的一個簡單示例,可參考:動態規劃之 Fib數列類問題應用
文章來源 hapjin 的博客
分享名稱:HanLP中的人名識別分析詳解
路徑分享:http://vcdvsql.cn/article42/phoeec.html
成都網站建設公司_創新互聯,為您提供面包屑導航、品牌網站制作、靜態網站、網站內鏈、品牌網站設計、企業網站制作
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯