本篇文章将重點講解HanLP的ViterbiSegment分詞器類,而不涉及感覺機和條件随機場分詞器,也不涉及基于字的分詞器。因為這些分詞器都不是我們在實踐中常用的,而且ViterbiSegment也是作者直接封裝到HanLP類中的分詞器,作者也推薦使用該分詞器,同時文本分類包以及其他一些自然語言處理任務包中的分詞器也都間接使用了ViterbiSegment分詞器。
今天的文章還會介紹各分詞詞典檔案的使用位置以及作用,相信小夥伴們看了今天的文章應該不會再在github上提出幹預自定義不生效的問題了。進入正題,本篇的内容比較多,建議收藏後再細讀。
-
分詞器配置變量
分詞器的相關配置定義在Config.java類中,這裡我們将分詞相關的所有配置變量列于下表

這種配置類什麼時候執行個體化呢,不用想肯定是分詞開始前就會執行個體化,拿HanLP類中的ViterbiSegment分詞類舉例。該類的繼承關系用如下圖所示:
由繼承關系圖可以看到,隻要執行個體化ViterbiSegment則首先會執行Segment()初始化,在該方法中執行個體化分詞器配置對象config。這些配置變量都是公有變量,是以可以在ViterbiSegment類執行個體化為對象後直接在外部修改。那麼什麼時候來使用這些配置變量呢,當然是在分詞的時候,具體是哪個類的哪個方法呢,當然是ViterbiSegment類的List segSentence(char[] sentence)方法。
另外請注意上邊的3個類,所有ViterbiSegment的分詞方法都集中在這3個類中。
-
詞典的使用條件和先後順序(也介紹分詞流程)
我們知道了詞典配置變量使用的位置後,就可以确定每個詞典的使用條件了以及每個詞典的使用順序
-
詞語粗分
(1)建構詞圖
對應方法為void generateWordNet(final WordNet wordNetStorage),在此方法中系統使用CoreNatureDictionary.txt檔案切分出所有可能的分詞路徑。此時如果配置變量useCustomDictionary為true,則将CustomDictionary.txt中的詞也考慮進來,說明CustomDictionary.txt優先級會高。另外大家可以看到CoreNatureDictionary.txt實際上也充當了隐馬詞性标注的發射矩陣,裡邊某些多詞性詞也列出了詞性序列以及各詞性對應的頻次。
(2)使用者定制詞典幹預
如果配置變量useCustomDictionary為true,即需要使用CustomDictionary.txt進行幹預,則執行下邊對應的方法,否則跳過該步驟。使用者詞典幹預根據是否進行全切分有兩種不同方法:當配置變量indexMode>0時,即系統處于全切分模式時,對應方法為
List combineByCustomDictionary(List vertexList, DoubleArrayTrie dat, final WordNet wordNetAll),
如果indexMode=0,即系統處于普通分詞模式,對應方法為
List combineByCustomDictionary(List vertexList, DoubleArrayTrie dat)。
從調用的方法我們不難看出,全切分時系統會根據CustomDictionary.txt添加分詞路徑。而普通切分時,系統會根據CustomDictionary.txt合并路徑。這也就是為什麼有的時候明明已經在CustomDictionary.txt中添加了新詞卻不生效的原因,因為一旦根據CoreNatureDictionary.txt建構了詞圖就不會再有新的路徑插到已有分詞路徑中間,此時就去查找并修改CoreNatureDictionary.txt中的相關字或詞吧。
(3)維特比選擇最優路徑
對應方法為List viterbi(WordNet wordNet),至此就得到了一個粗分的分詞結果。需要注意HanLP的Viterbi分詞隻是用viterbi方法求解最優路徑,并不是隐馬。
-
數字識别
如果配置變量numberQuantifierRecognize為true,則在粗分結果的基礎上進行數字合并操作,否則直接跳過該步。對應方法為
void mergeNumberQuantifier(List termList, WordNet wordNetAll, Config config)。
-
實體識别
配置變量ner為true時,則需要進行各種實體的識别,繼續向下執行。需要注意該變量受其他實體識别變量影響,隻要其他任意實體配置變量為true,則ner就會為true。如果ner為false,則跳過下邊各項實體識别繼續詞性标注環節。
(1)中國人名識别
執行此步,配置變量nameRecognize必須為true。調用方法為
PersonRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。人名使用隐馬,是以有轉移矩陣nr.tr.txt和發射矩陣nr.txt。由于HanLP不提供訓練語料,我們自己也很難得到有角色标注的語料,是以我們一般隻修改nr.txt檔案,删除nr.txt.bin檔案後生效。
(2)音譯人名識别
執行此步,配置變量translatedNameRecognize必須為true。調用方法為
TranslatedPersonRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。需要注意音譯人名的識别沒有用隐馬,就是比對分詞法。涉及到的詞典為nrf.txt,如果使用者修改該詞典,則需要删除nrf.txt.trie.dat使其生效。
(3)日本人名識别
執行此步,配置變量japaneseNameRecognize必須為true。調用方法為
JapanesePersonRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。需要注意日本人名的識别沒有用隐馬,就是比對分詞法。涉及到的詞典為nrj.txt,如果使用者修改該詞典,則需要删除nrj.txt.trie.dat和nrj.txt.value.dat使其生效。
(4)地名識别
執行此步,配置變量placeRecognize必須為true。調用方法為
PlaceRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。地名使用隐馬,是以有轉移矩陣ns.tr.txt和發射矩陣ns.txt。由于HanLP不提供訓練語料,我們自己也很難得到有角色标注的語料,是以我們一般隻修改ns.txt檔案,删除ns.txt.bin檔案後生效。
(5)機構名識别
執行此步,配置變量organizationRecognize必須為true。調用方法為
OrganizationRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。注意這裡在調用機構名識别之前先進行了一次識别,也就是層疊隐馬,而人名和地名的識别就是普通的隐馬。機構名的識别使用層疊隐馬,涉及的檔案有轉移矩陣nt.tr.txt和發射矩陣nt.txt。由于HanLP不提供訓練語料,我們自己也很難得到有角色标注的語料,是以我們一般隻修改nt.txt檔案,删除ns.txt.bin檔案後生效。機構名的識别需要人名地名識别具有較高準确率。
至此,分詞流程已全部介紹了。
還需要注意下邊的内容
其他沒有在系統中使用的詞典有
機構名詞典.txt
全國地名大全.txt
人名詞典.txt
上海地名.txt
現代漢語補充詞庫.txt
這些詞典是對系統中的詞典的更新記錄,如果你添加了新的人名、地名、機構名可以在這裡添加儲存。
另外,如果需要添加人名、地名、機構名可以直接在CoreNatureDictionary.txt中添加,最好是3字以上實體,
如果要去掉錯誤識别的命名實體可以直接在相應的nr.txt,ns.txt,nt.txt中添加。
-
多線程分詞
HanLP的ViterbiSegment分詞器類是支援多線程的,線程數量由配置變量threadNumber決定的,該變量預設為1。HanLP作者說ViterbiSegmet分詞效率最高的原因肯定也有ViterbiSegment分詞器支援多線程分詞這個因素。另外由于ViterbiSegment分詞器内部所具有的相關命名實體功能,是以這些命名實體識别的效率也會很高。在哪裡實作的多線程分詞呢,在Segment類的List seg(String text)這個方法中實作的,需要注意HanLP的多線程分詞指的是一次輸入了一個長文本,而不是一次處理多個輸入文本。
本文分享自 baiziyu 的專欄,正文内容已經做了部分修改,便于大家閱讀,歡迎一起交流學習!