HanLP中人名識別分析詳解
在看原始碼之前,先看幾遍論文《基於角色標註的中國人名自動識別研究》
關於命名識別的一些問題,可參考下列一些 issue:
l ·名字識別的問題 #387
l ·機構名識別錯誤
l ·關於層疊 HMM 中文實體識別的過程
HanLP 參考部落格:
詞性標註
層疊 HMM-Viterbi 角色標註模型下的機構名識別
分詞
在 HMM 與分詞、詞性標註、命名實體識別中說:
分詞:給定一個字的序列,找出最可能的標籤序列(斷句符號: [ 詞尾 ] 或 [ 非詞尾 ] 構成的序列)。結巴分詞目前就是利用 BMES 標籤來分詞的, B (開頭) ,M (中間 ),E( 結尾 ),S( 獨立成詞)
分詞也是採用了維特比演算法的動態規劃性質求解的,具體可參考:文字挖掘的分詞原理
角色觀察
以 “唱首張學友的歌情已逝”為例,
先將起始頂點 始 ## 始,角色標註為: NR.A 和 NR.K ,頻次預設為 1
iterator.next(); tagList.add( new EnumItem<NR>(NR.A, NR.K)); // 始 ## 始 A K
對於第一個詞 “唱首”,它不存在於 nr.txt 中, EnumItem<NR> nrEnumItem = PersonDictionary.dictionary.get(vertex.realWord); 返回 null ,於是根據它本身的詞性猜一個角色標註:
switch (vertex.guessNature()){
case nr:
case nnt:
default:{
nrEnumItem = new EnumItem<NR>(NR.A, PersonDictionary.transformMatrixDictionary.getTotalFrequency(NR.A));
}
}
由於 " 唱首 " 的 Attribute 為 nz 16 ,不是 nr 和 nnt ,故預設給它指定一個角色 NR.A ,頻率為 nr.tr.txt 中 NR.A 角色的總頻率。
此時,角色列表如下:
接下來是頂點 “張”,由於“張”在 nr.txt 中,因此 PersonDictionary.dictionary.get(vertex.realWord) 返回 EnumItem 物件,直接將它加入到角色列表中:
EnumItem<NR> nrEnumItem = PersonDictionary.dictionary.get(vertex.realWord);
tagList.add(nrEnumItem);
加入 “張”之後的角色列表如下:
“唱首張學友的歌情已逝” 整句的角色列表如下:
至此,角色觀察 部分 就完成了。
總結一下,對句子進行角色觀察,首先是透過分詞演算法將句子分成若干個詞,然後對每個詞查詢人名詞典 (PersonDictionary) 。
若這個詞在人名詞典中 (nr.txt) ,則記錄該詞的角色,所有的角色在 com.hankcs.hanlp.corpus.tag.NR.java 中定義。
若這個詞不在人名詞典中,則根據該詞的 Attribute “猜一個角色”。在猜的過程中,有些詞在核心詞典中可能已經標註為 nr 或者 nnt 了,這時會做分裂處理。其他情況下則是將這個詞標上 NR.A 角色,頻率為 NR.A 在轉移矩陣中的總詞頻。
維特比演算法 ( 動態規劃 ) 求解最優路徑
在上圖中,給每個詞都打上了角色標記,可以看出,一個詞可以有多個標記。而我們需要將這些詞選擇一條路徑最短的角色路徑。參考隱馬爾可夫模型維特比演算法詳解
List<NR> nrList = viterbiComputeSimply(roleTagList);
//some code....
return Viterbi.computeEnumSimply(roleTagList, PersonDictionary.transformMatrixDictionary);
而這個過程,其實就是:維特比演算法解碼隱藏狀態序列。在這裡,五元組是:
l 隱藏狀態集合 com.hankcs.hanlp.corpus.tag.NR.java 定義的各個人名標籤
l 觀察狀態集合 已經分好詞的各個 tagList 中元素 ( 相當於分詞結果 )
l 轉移機率矩陣 由 nr.tr.txt 檔案生成得到。具體可參考:
l 發射機率 某個人名標籤 ( 隱藏狀態 ) 出現的次數 除以 所有標籤出現的總次數
Math.log((item.getFrequency(cur) + 1e-8) / transformMatrixDictionary.getTotalFrequency(cur)
l 初始狀態 ( 始 ## 始 ) 和 結束狀態 ( 末 ## 末 )
維特比解碼隱藏狀態的動態規劃求解核心程式碼如下:
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);
}
}
總結
原始碼上的人名識別基本上是按照論文中的內容來實現的。對於一個給定的句子,先進行下面三大步驟處理:
l 角色觀察
l 維特比演算法解碼求解隱藏狀態(求解各個分詞 的 角色標記)
l 對角色標記進行最大匹配(可做一些後處理操作)
最後,再使用維特比演算法進行一次分詞,得到細分結果,即為最後的識別結果。
這篇文章裡面沒有寫維特比分詞演算法的詳細過程,以及轉移矩陣的生成過程,以後有時間再補上。看原始碼,對隱馬模型的理解又加深了一點,感受到了理論的東西如何用程式碼一步步來實現。由於我也是初學,對原始碼的理解不夠深入或者存在一些偏差,歡迎批評指正。
關於動態規劃的一個簡單示例,可參考:動態規劃之 Fib 數列類問題應用。
文章來源 hapjin 的部落格
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31524777/viewspace-2284164/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- HanLP中的人名識別分析詳解HanLP
- Hanlp實戰HMM-Viterbi角色標註中國人名識別HanLPHMMViterbi
- hanlp自然語言處理包的人名識別程式碼解析HanLP自然語言處理
- HanLP-基於HMM-Viterbi的人名識別原理介紹HanLPHMMViterbi
- Hanlp-地名識別除錯方法詳解HanLP地名識別除錯
- Hanlp分詞之CRF中文詞法分析詳解HanLP分詞CRF詞法分析
- HanLP 關鍵詞提取演算法分析詳解HanLP演算法
- HanLP-地名識別除錯方法HanLP地名識別除錯
- Java利用hanlp完成語句相似度分析的方法詳解JavaHanLP
- HanLP-命名實體識別總結HanLP
- HanLP-實詞分詞器詳解HanLP分詞
- HanLP分詞命名實體提取詳解HanLP分詞
- python呼叫hanlp進行命名實體識別PythonHanLP
- Hanlp漢字轉拼音使用python呼叫詳解HanLPPython
- 文字識別(六)--不定長文字識別CRNN演算法詳解RNN演算法
- hanlp中文智慧分詞自動識別文字提取例項HanLP分詞
- Oracle中number型別詳解Oracle型別
- 自然語言處理入門基礎之hanlp詳解自然語言處理HanLP
- Spring Boot中對自然語言處理工具包hanlp的呼叫詳解Spring Boot自然語言處理HanLP
- MyBatis中#{}和${}的區別詳解MyBatis
- Java中的Type型別詳解Java型別
- 自然語言處理工具python呼叫hanlp中文實體識別自然語言處理PythonHanLP
- 開源自然語言處理工具包hanlp中CRF分詞實現詳解自然語言處理HanLPCRF分詞
- hanlp原始碼解析之中文分詞演算法詳解HanLP原始碼中文分詞演算法
- 分析建模-如何識別分析類?
- JavaScript中的包裝型別詳解JavaScript型別
- 自然語言處理工具HanLP-基於層疊HMM地名識別自然語言處理HanLPHMM地名識別
- 詳解卷積神經網路(CNN)在語音識別中的應用卷積神經網路CNN
- 美顏SDK人臉表情識別技術詳解
- Python+OpenCV人臉識別技術詳解PythonOpenCV
- 一種融合指代消解序列標註方法在中文人名識別上的應用(下)
- 一文詳解深度學習在命名實體識別(NER)中的應用深度學習
- maven中的scope標籤類別詳解Maven
- Struts2中 Result型別配置詳解型別
- Java中Vector與ArrayList的區別詳解Java
- 中文自然語言處理工具hanlp隱馬角色標註詳解自然語言處理HanLP
- ASM 知識詳解ASM
- MySQL 中 blob 和 text 資料型別詳解MySql資料型別