NLP入門干貨:手把手教你3種中文規則分詞方法
在自然語言理解中,詞(token)是最小的能夠獨立活動的有意義的語言成分。將詞確定下來是理解自然語言的第一步,只有跨越了這一步,中文才能像英文那樣過渡到短語劃分、概念抽取以及主題分析,以至自然語言理解,最終達到智能計算的最高境界。因此,每個NLP工作者都應掌握分詞技術。
1. 分詞的概念和分類
“詞”這個概念一直是漢語言學界糾纏不清而又揮之不去的問題。“詞是什么”(詞的抽象定義)和“什么是詞”(詞的具體界定)這兩個基本問題迄今為止也未能有一個權威、明確的表述,當今更是沒有一份令大家公認的詞表。
問題的主要難點在于漢語結構與印歐體系語種差異甚大,對詞的構成邊界很難進行界定。比如在英語中,單詞本身就是“詞”的表達,一篇英文文章的格式就是“單詞”加分隔符(空格)。
而在漢語中,詞以字為基本單位,但是一篇文章的語義表達卻仍然是以詞來劃分。因此,需要針對中文漢字,將其按照一定的方式進行組織,分成不同的詞。
中文分詞是讓計算機自動識別出句子中的詞,然后在詞間加入邊界標記符。這個過程看似簡單,然而實踐起來要復雜得多,主要困難在于分詞歧義。
下面以NLP分詞的經典場景為例進行說明,短語“結婚的和尚未結婚的”,應該分詞為“結婚/的/和/尚未/結婚/的”,還是“結婚/的/和尚/未/結婚/的”呢?對于這個問題,機器很難處理。此外,像未登錄詞、分詞粒度粗細等都是影響分詞效果的重要因素。
自中文自動分詞被提出以來,歷經近30年的探索,先后出現了很多分詞方法,可主要歸納為規則分詞、統計分詞和混合分詞(規則+統計)這3個流派。最近這幾年又興起了以深度學習的方式進行分詞,比如BILSTM+CRF。
規則分詞是最早興起的方法,主要通過人工設立詞庫,按照一定方式進行匹配切分,其實現簡單高效,但對沒有錄入詞庫的新詞很難進行處理。
隨后統計機器學習技術興起,應用于分詞任務上就有了統計分詞方法。該方法能夠較好地應對新詞發現等特殊場景。然而在實踐中,單純的統計分詞也有其缺陷:太過依賴語料的質量。因此實踐中多是采用規則分詞和統計分詞這兩種方法的結合,即混合分詞。
2. 規則分詞
基于規則的分詞是一種機械分詞方法,需要不斷維護和更新詞典,在切分語句時,將語句的每個字符串與詞表中的每個詞進行逐一匹配,找到則切分,找不到則不予切分。
按照匹配劃分,主要有正向最大匹配、逆向最大匹配以及雙向最大匹配這3種切分方法。
1. 正向最大匹配
正向最大匹配(Maximum Match)通常簡稱為MM法,其執行過程如下所示。
- 從左向右取待切分漢語句的m個字符作為匹配字段,m為機器詞典中最長詞條的字符數。
- 查找機器詞典并進行匹配。若匹配成功,則將這個匹配字段作為一個詞切分出來。若匹配不成功,則將這個匹配字段的最后一個字去掉,剩下的字符串作為新的匹配字段,進行再次匹配,重復以上過程,直到切分出所有詞為止。
比如我們現在有個詞典,最長詞的長度為5,詞典中存在“南京市長”“長江大橋”和“大橋”3個詞。
現采用正向最大匹配對句子“南京市長江大橋”進行分詞,那么首先從句子中取出前5個字“南京市長江”,發現詞典中沒有該詞,于是縮小長度,取前4個字“南京市長”,詞典中存在該詞,于是該詞被確認切分。
再將剩下的“江大橋”按照同樣方式切分,得到“江”“大橋”,最終分為“南京市長”“江”“大橋”3個詞。顯然,這種結果不是我們所希望的。正向最大匹配法示例代碼如下。
- class MM(object):
- def __init__(self):
- self.window_size = 3
- def cut(self,text):
- result=[]
- index=0
- text_length = len(text)
- dic = ['研究','研究生','生命','起源']
- while text_length > index:
- for size in range(self.window_size+index,index,-1):#4,0,-1
- piece = text[index:size]
- if piece in dic:
- index = size-1
- break
- indexindex = index + 1
- result.append(piece)
- return result
分詞的結果如下所示,這個結果并不能讓人滿意。
- text = '研究生命的起源'
- tokenizer = MM()
- print(tokenizer.cut(text))
輸出結果如下所示。
- ['研究生', '命', '的', '起源']
2. 逆向最大匹配
逆向最大匹配簡稱為RMM法。RMM法的基本原理與MM法大致相同,不同的是分詞切分的方向與MM法相反。
逆向最大匹配法從被處理文檔的末端開始匹配掃描,每次取最末端的m個字符(m為詞典中最長詞數)作為匹配字段,若匹配失敗,則去掉匹配字段最前面的一個字,繼續匹配。相應地,它使用的分詞詞典是逆序詞典,其中的每個詞條都將按逆序方式存放。
在實際處理時,先將文檔進行倒排處理,生成逆序文檔。然后,根據逆序詞典,對逆序文檔用正向最大匹配法處理即可。
由于漢語中偏正結構較多,若從后向前匹配,可以適當提高精確度。所以,逆向最大匹配法比正向最大匹配法的誤差要小。
統計結果表明,單純使用正向最大匹配的錯誤率為1/169,單純使用逆向最大匹配的錯誤率為1/245。比如之前的“南京市長江大橋”,按照逆向最大匹配,最終得到“南京市”“長江大橋”的分詞結果。
當然,如此切分并不代表完全正確,可能有個叫“江大橋”的“南京市長”也說不定。逆向最大匹配法示例代碼如下。
- class RMM(object):
- def __init__(self):
- self.window_size = 3
- def cut(self, text):
- result = []
- index = len(text)
- dic = ['研究', '研究生', '生命', '命', '的', '起源']
- while index > 0:
- for size in range(index-self.window_size ,index):
- piece = text[size:index]
- if piece in dic:
- index = size + 1
- break
- indexindex = index - 1
- result.append(piece)
- result.reverse()
- return result
分詞的結果如下所示,這個結果就很準確了。
- text = '研究生命的起源'
- tokenizer = RMM()
- print(tokenizer.cut(text))
輸出結果如下所示。
- ['研究', '生命', '的', '起源']
3. 雙向最大匹配
雙向最大匹配法是將正向最大匹配法得到的分詞結果和逆向最大匹配法得到的結果進行比較,然后按照最大匹配原則,選取詞數切分最少的作為結果。
據Sun M.S.和Benjamin K.T.研究表明,對于中文中90.0%左右的句子,正向最大匹配和逆向最大匹配的切分結果完全重合且正確,只有大概9.0%的句子采用兩種切分方法得到的結果不一樣,但其中必有一個是正確的(歧義檢測成功),只有不到1.0%的句子,或者正向最大匹配和逆向最大匹配的切分結果雖重合卻都是錯的,或者正向最大匹配和逆向最大匹配的切分結果不同但兩個都不對(歧義檢測失敗)。這正是雙向最大匹配法在實用中文信息處理系統中得以廣泛使用的原因所在。
前面列舉的“南京市長江大橋”采用雙向最大匹配法進行切分,中間產生“南京市/ 江/ 大橋”和“南京市/ 長江大橋”兩種結果,最終選取詞數較少的“南京市/ 長江大橋”這一結果。
雙向最大匹配的規則如下所示。
(1) 如果正反向分詞結果詞數不同,則取分詞數量較少的那個結果(上例:“南京市/江/大橋”的分詞數量為3,而“南京市/長江大橋”的分詞數量為2,所以返回分詞數量為2的結果)。
(2) 如果分詞結果詞數相同,則:
- 分詞結果相同,就說明沒有歧義,可返回任意一個結果。
- 分詞結果不同,返回其中單字較少的那個。比如前文示例代碼中,正向最大匹配返回的結果為“['研究生', '命', '的', '起源']”,其中單字個數為2個;而逆向最大匹配返回的結果為“['研究', '生命', '的', '起源']”,其中單字個數為1。所以返回的是逆向最大匹配的結果。
參考代碼如下所示。
- #統計單字成詞的個數
- def count_singlechar(word_list):
- return sum(1 for word in word_list if len(word) == 1)
- def bidirectional_segment(text):
- mm = MM()
- rmm = RMM()
- f = mm.cut(text)
- b = rmm.cut(text)
- if (len(f) < len(b)):
- return f
- elif (len(f) > len(b)):
- return b
- else:
- if (count_singlechar(f) >= count_singlechar(b)):
- return b
- else:
- return f
最后我們驗證一下效果。
- print(bidirectional_segment('研究生命的起源'))
輸出結果為:
- ['研究', '生命', '的', '起源']
基于規則的分詞一般都較為簡單高效,但是詞典的維護面臨很龐大的工作量。在網絡發達的今天,網絡新詞層出不窮,很難通過詞典覆蓋所有詞。另外,詞典分詞也無法區分歧義以及無法召回新詞。
在實際項目中,我們是否會考慮使用規則分詞?
雖然使用規則分詞的分詞準確率看上去非常高,但是規則分詞有幾個特別大的問題:
- 不斷維護詞典是非常煩瑣的,新詞總是層出不窮,人工維護費時費力;
- 隨著詞典中條目數的增加,執行效率變得越來越低;
- 無法解決歧義問題。