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