史上最全的分词算法与工具介绍
分詞(word tokenization),也叫切詞,即通過某種方式將句子中的各個詞語識別并分離開來,使得文本從“字序列”的表示升級為“詞序列”表示。分詞技術不僅僅適用于中文,對于英文、日文、韓文等語言也同樣適用。
雖然英文中有天然的單詞分隔符(空格),但是常有單詞與其他標點黏滯的情況,比如"Hey, how are you."中的"Hey"和"you"是需要與身后的標點分隔開的目錄
- 為什么需要分詞?
- 能不能不分詞?
- 中文分詞難在哪?
- 從古至今的分詞算法:詞典到預訓練
- 從中到外的分詞工具
為什么需要分詞?
對于中文來說,如果不進行分詞,那么神經網絡將直接基于原始的漢字序列進行處理和學習。然而我們知道一個字在不同的詞語中可能含義迥然不同,比如“哈哈”與“哈士奇”中“哈”的含義相去甚遠,如果模型在訓練階段沒見過“哈士奇”,那么預測的時候就有可能以為“哈士奇”所在的句子在表達歡快的氣氛了╮( ̄▽ ̄"")╭
顯然,要解決上述問題的話,分詞就是了,分詞之后,最小的輸入單位就不再是字了,而是詞,由于“哈哈”與“哈士奇”是兩個不同的詞,自然就不會有前面這個問題啦。因此可以認為,分詞緩解了“一字多義”的問題。
除此之外,從特征(feature)與NLP任務的角度來說,字相比詞來說,是更原始和低級的特征,往往與任務目標的關聯比較小;而到了詞級別后,往往與任務目標能發生很強的關聯。比如對于情感分類任務,“我今天走狗屎運了”這句中的每個字都跟正向情感關系不大,甚至“狗”這個字還往往跟負面情感密切相關,但是“狗屎運”這個詞卻表達了“幸運”、“開心”、“驚喜”的正向情感,因此,分詞可以看作是給模型提供了更high-level、更直接的feature,丟給模型后自然容易獲得更佳的表現。
能不能不分詞?
答案是,當然可以。從前面分詞的目的可以看出,只要模型本身能夠學習到字的多義性,并且自己學到由字組詞的規律,那么就相當于隱含的內置了一個分詞器在模型內部,這時候這個內置的分詞器是與解決目標任務的網絡部分一起“端到端訓練”的,因此甚至可能獲得更佳的性能。
然而,從上面這段的描述也能看出,要滿足這個條件,是很難得的。這需要訓練語料非常豐富,且模型足夠大(可以有額外容量來內置一個隱含的分詞模型),才有可能獲得比“分詞器+詞級模型“更好的表現。這也是為什么BERT等大型預訓練模型往往是字級別的,完全不需要使用分詞器。
此外,分詞也并不是百利而無一害的,一旦分詞器的精度不夠高,或者語料本身就噪聲很大(錯字多、句式雜亂、各種不規范用語),這時強行分詞反而容易使得模型更難學習。比如模型終于學會了“哈士奇”這個詞,卻有人把哈士奇打成了“蛤士奇”,結果分詞器沒認出來,把它分成了“蛤”、“士”、“奇”這三個字,這樣我們這個已經訓練好的“word level模型”就看不到“哈士奇”了(畢竟模型訓練的時候,“哈士奇”是基本單位)。
中文分詞難在哪?
1 歧義問題
首先,前面提到分詞可以緩解“一字多義”的問題,但是分詞本身又會面臨“切分歧義”的問題。例如,切分書名《無線電法國別研究》
?
雖然切分成《無線電/法國/別/研究》看起來沒毛病,但是考慮到這是個書名,顯然正解《無線電法/國別/研究》對分詞器來說太南了(。 ?︿ ?。)而如果不告訴你這是一個書名,顯然兩種切分方式看起來都沒毛病。
2 未登錄詞問題
此外,中文詞典也是與時俱進的,例如“青青草原”、“累覺不愛”等網絡詞在10年前是沒有的,今天訓練的分詞器也一定會在不久的將來遇到不認識的詞(即“未登錄詞”,out-of-vocabulary word),那個時候分詞器很容易因為“落伍”而出現切分錯誤。
3 規范問題
最后,分詞時的切分邊界也一直沒有一個確定的規范。盡管在 1992 年國家頒布了《信息處理用現代詞漢語分詞規范》,但是這種規范很容易受主觀因素影響,在實際場景中也難免遇到有所不及的問題。
算法篇
1 基于詞典
對于中文分詞問題,最簡單的算法就是基于詞典直接進行greedy匹配。
比如,我們可以直接從句子開頭的第一個字開始查字典,找出字典中以該字開頭的最長的單詞,然后就得到了第一個切分好的詞。比如這句“夕小瑤正在講NLP”,查字典發現“夕小瑤”是最長的詞了,于是得到
夕小瑤/ 正在講NLP
然后從下一個詞的開頭開始繼續匹配字典,發現“正在”就是最長的詞了,于是
夕小瑤/正在/ 講NLP
依此類推,最終得到
夕小瑤/正在/講/NLP
這種簡單的算法即為前向最大匹配法(FMM)
雖然做法很樸素,但是名字聽起來還有點高端╮(╯▽╰)╭不過,由于中文句子本身具有重要信息后置的特點,從后往前匹配的分詞正確率往往要高于從前往后,于是就有了反向進行的“后向最大匹配法(BMM)”。當然了,無論是FMM還是BMM,都一定存在不少切分錯誤,因此一種考慮更周到的方法是“雙向最大匹配”。
雙向最大匹配算法是指對待切分句子分別使用FMM和RMM進行分詞,然后對切分結果不重合的歧義句進行進一步的處理。通常可對兩種方法得到的詞匯數目進行比較,根據數目的相同與否采取相應的措施,以此來降低歧義句的分詞錯誤率.
2 基于統計
2.1 基于語言模型
基于詞典的方法雖然簡單,但是明顯能看出來太!不!智!能!了!稍微復雜一些的句子,例如“沒關系,除夕小瑤在家做飯。”,這時候如果使用后向最大匹配法,就會切分成“沒關系/,/除/夕小瑤/在家/做飯/。”,這明顯錯的很不可原諒。
犯這種錯誤的根本原因在于,基于詞典的方法在切分時是沒有考慮詞語所在的上下文的,沒有從全局出發找最優解。其實上面這個句子無非就是在糾結兩種切分方式:
a. 沒關系/,/除/夕小瑤/在家/做飯/。
b. 沒關系/,/除夕/小瑤/在家/做飯/。
我們日常說話中很少會有人說出“沒關系/,/除/xxxx/做飯/。”這種句子,而第二個句子出現的頻率則會非常高,比如里面的“小瑤”可以替換成“我”、“老王”等。顯然給定一個句子,各種切分組合是數量有限的,如果有一個東西可以評估出任何一個組合的存在合理性的分值,那么不就找到了最佳的分詞組合嘛!
所以,這種方法的本質就是在各種切詞組合中找出那個最合理的組合,這個過程就可以看作在切分詞圖中找出一條概率最大的路徑:
?
而這個可以給詞序列存在合理性打分的東西就叫做“語言模型”(language model)。這種利用語言模型來評估各種切分組合的方法是不是就顯得智能多啦╮(╯▽╰)╭給定一個句子分詞后得到的單詞序列{w1,w2...wm},語言模型就能計算出這個句子(或者說詞序列)存在的可能性:
這個表達式可通過鏈式法則進行展開:
?
顯然,當m取值稍微一大,乘法鏈的后面幾項會變得非常難計算(估計出這幾項的概率需要依賴極其龐大的語料才能保證估計誤差可接受)。計算困難怎么辦?當然是用合理的假設來簡化計算,比如我們可以假設當前位置取什么詞僅取決于相鄰的前面n個位置,即
?
這種簡化的語言模型就稱為n-gram語言模型。這樣乘法鏈中的每個乘子都可以在已經完成人工標注的分詞語料中計算得到啦。當然了,在實際計算中可能還會引入一些平滑技巧,來彌補分詞語料規模有限導致的估計誤差,這里就不展開講啦。
2.2 基于統計機器學習
NLP是一門跟機器學習強綁定的學科,分詞問題自然也不例外。中文分詞同樣可以建模成一個“序列標注”問題,即一個考慮上下文的字分類問題。因此可以先通過帶標簽的分詞語料來訓練一個序列標注模型,再用這個模型對無標簽的語料進行分詞。
樣本標簽
一般用{B:begin, M:middle, E:end, S:single}這4個類別來描述一個分詞樣本中每個字所屬的類別。它們代表的是該字在詞語中的位置。其中,B代表該字是詞語中的起始字,M代表是詞語中的中間字,E代表是詞語中的結束字,S則代表是單字成詞。
一個樣本如下所示:
人/b 們/e 常/s 說/s 生/b 活/e 是/s 一/s 部/s 教/b 科/m 書/e之后我們就可以直接套用統計機器學習模型來訓練出一個分詞器啦。統計序列標注模型的代表就是生成式模型的代表——隱馬爾可夫模型(HMM),和判別式模型的代表——(線性鏈)條件隨機場(CRF)。已經對這兩個模型很熟悉的小伙伴可以跳過。
隱馬爾可夫模型(HMM)
HMM模型的詳細介紹見
《如果你跟夕小瑤戀愛了... (上)》 / 《下》
在了解了HMM模型的基本概念之后,我們來看看HMM模型是如何進行分詞的吧~基本思路:將分詞問題轉換為給每個位置的字進行分類的問題,即序列標注問題。其中,類別有4個(前面講到的B、M、E、S)。給所有的位置分類完成后,便可以根據類別序列得到分詞結果啦。
舉個栗子!
我們的輸入是一個句子:
小Q碩士畢業于中國科學院通過算法,我們成功預測出各個字位對應的分詞標簽:
BEBEBMEBEBME根據這個狀態序列我們可以進行切詞:
BE/BE/BME/BE/BME所以切詞結果如下:
小Q/碩士/畢業于/中國/科學院那么問題又來了,假如一個完美的HMM分詞模型給你了,那么如何用這個模型對輸入的字序列進行序列標注呢?首先看下HMM模型中的兩個核心概念:觀測序列和狀態序列。
觀測序列就是我可以直接看到的序列,也就是“小Q碩士畢業于中國科學院”這個字序列,而狀態序列則是不能通過肉眼直接觀察到的內在序列,也就是上面這句話所對應的標注結果“BEBEBMEBEBME”,而我們的HMM模型,就可以幫助我們完成從觀測序列->狀態序列的華麗變身!
用數學抽象表示如下:用 代表輸入的句子,n為句子長度, 表示字, 代表輸出的標簽,那么理想的輸出即為:
?
?
3 基于神經網絡
眾所周知,深度學習已經成功占領NLP,席卷了NLP中的分類、序列標注和生成問題。如前所述,分詞也可以建模為序列標注問題,那么擅長處理序列數據的LSTM(長短時記憶網絡)和最近超級火的預訓練模型同樣可以用于中文分詞。
3.1 基于(Bi-)LSTM
對LSTM模型還不熟悉的小伙伴見小夕以前寫的這篇的《step-by-step to LSTM》,本文對lstm的基本理論不再贅述啦。
如前面語言模型一節中所述,字的上下文信息對于排解切分歧義來說非常重要,能考慮的上下文越長,自然排解歧義的能力就越強。而前面的n-gram語言模型也只能做到考慮一定距離的上下文,那么有沒有在理論上能考慮無限長上下文距離的分詞模型呢?答案就是基于LSTM來做。當然啦,LSTM是有方向的,為了讓每個位置的字分類時既能考慮全部歷史信息(左邊的所有的字),又能考慮全部未來信息(右邊所有的字),我們可以使用雙向LSTM(Bi-LSTM)來充當序列標注的骨架模型,如圖
?
LSTM完成對每個位置的上下文信息的編碼后,最終通過softmax分類層完成對每個位置的分類,從而跟HMM和CRF一樣完成了基于序列標注的中文分詞。
3.2 基于預訓練模型+知識蒸餾
最近的一年多的時間里,BERT、ERNIE、XLNet等大型預訓練席卷了NLP的絕大部分領域,在分詞問題上也有顯著的優越性。
?
然而,眾所周知,預訓練模型太大了,過于消耗計算資源,如果要對海量的文本進行分詞,哪怕用上8卡的32G Tesla V100都會顯得力不從心,因此一種解決方案就是,將預訓練模型中的分詞知識通過知識蒸餾(Knowledge Distillation)來遷移到小模型(比如LSTM、GRU)上。近期Jieba分詞器中就上線了這么一個用這種方法得到的先進分詞模型(其實是個通用的詞法分析模型),感興趣的小伙伴可以自行了解一下。預訓練模型和知識蒸餾的資料很多了,這里就不贅述啦。
?
工具篇
下面列了幾個較為主流的分詞工具(排名不分先后,大家自行試用),相關的paper請在訂閱號「夕小瑤的賣萌屋」后臺回復【中文分詞】領取。
1 Jieba
說到分詞工具第一個想到的肯定是家喻戶曉的“結巴”中文分詞,主要算法是前面講到的基于統計的最短路徑詞圖切分,近期還內置了百度飛槳的預訓練模型+大規模蒸餾的前沿分詞模型。
github項目地址:https://github.com/fxsjy/jieba
?
2 THULAC(THU Lexical Analyzer for Chinese)
由清華大學自然語言處理與社會人文計算實驗室研制推出的一套中文詞法分析工具包,具有中文分詞和詞性標注功能。該工具所采用的分詞模型為結構化感知機。更多算法細節請參考github項目和閱讀論文原文。
github項目地址:https://github.com/thunlp/THULAC
論文鏈接:https://www.mitpressjournals.org/doi/pdf/10.1162/coli.2009.35.4.35403
使用示例:
#THULAC #pip install thulac import thulacsentence = "不會講課的程序員不是一名好的算法工程師" thu1 = thulac.thulac(seg_only=True) #只分詞 text = thu1.cut(sentence, text=True) #進行一句話分詞 print("THULAC: " + text)#output #Model loaded succeed #THULAC: 不 會 講課 的 程序員 不 是 一 名 好 的 算法 工程師?
3 NLPIR-ICTCLAS漢語分詞系統
北京理工大學海量語言信息處理與云計算工程研究中心大數據搜索與挖掘實驗室( Big Data Search and Mining Lab.BDSM@BIT)發布。是基于層次HMM的分詞庫,將分詞、POS、NER等都納入到了一個層次HMM的框架之下聯合訓練得到。
主頁:http://ictclas.nlpir.org/github
項目地址:https://github.com/tsroten/pynlpir
使用示例:
#NLPIR-ICTCLAS #pip install pynlpir import pynlpirsentence = "不會講課的程序員不是一名好的算法工程師" pynlpir.open() tokens = [x[0] for x in pynlpir.segment(sentence)] print("NLPIR-TCTCLAS: " + " ".join(tokens)) pynlpir.close()#output #NLPIR-TCTCLAS: 不 會 講課 的 程序員 不 是 一 名 好 的 算法 工程?
4 LTP
哈工大出品,同THULAC一樣,LTP也是基于結構化感知器(Structured Perceptron, SP),以最大熵準則學習的分詞模型。
項目主頁:https://www.ltp-cloud.com/github
項目地址:https://github.com/HIT-SCIR/ltp
論文鏈接:http://jcip.cipsc.org.cn/CN/abstract/abstract1579.shtml
使用示例:使用前需下載分詞模型(http://ltp.ai/download.html)
5 HanLP
HanLP是隨《自然語言處理入門》配套開源的一系列NLP算法庫。除了經典的1.x版本在不斷迭代更新以外,今年還全新推出了2.0版本。1.x版本有有基于詞典的分詞工具和基于CRF的切詞模型。2.0版本開源了基于深度學習算法的分詞工具。
1.x版本
github項目地址:https://github.com/hankcs/pyhanlp
2.0版本
github地址:https://github.com/hankcs/HanLP/tree/doc-zh
使用示例:要求Python 3.6以上使用
#HanLP #v2.0 #pip install hanlp import hanlpsentence = "不會講課的程序員不是一名好的算法工程師" tokenizer = hanlp.load('PKU_NAME_MERGED_SIX_MONTHS_CONVSEG') tokens = tokenizer(sentence) print("hanlp 2.0: " + " ".join(tokens)) #output #hanlp 2.0: 不 會 講課 的 程序員 不 是 一 名 好 的 算法 工程?
6 Stanford CoreNLP
斯坦福推出的切詞工具,可以支持多種語言。算法核心是基于CRF模型。
github項目地址:https://github.com/Lynten/stanford-corenlp
論文鏈接:https://nlp.stanford.edu/pubs/sighan2005.pdf
使用示例:需要先從stanford官網下載中文切詞模型(https://stanfordnlp.github.io/CoreNLP/)
###stanford CoreNLP #pip install stanfordcorenlp from stanfordcorenlp import StanfordCoreNLPsentence = "不會講課的程序員不是一名好的算法工程師" with StanfordCoreNLP(r'stanford-chinese-corenlp-2018-10-05-models', lang='zh') as nlp:print("stanford: " + " ".join(nlp.word_tokenize(sentence)))?
總結
以上是生活随笔為你收集整理的史上最全的分词算法与工具介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 别再双塔了!谷歌提出DSI索引,检索效果
- 下一篇: 搜索引擎核心技术与算法 —— 词项词典与