自然語言處理之jieba分詞

奧辰發表於2020-08-18
 

在所有人類語言中,一句話、一段文字、一篇文章都是有一個個的片語成的。詞是包含獨立意義的最小文字單元,將長文字拆分成單個獨立的詞彙的過程叫做分詞。分詞之後,文字原本的語義將被拆分到在更加精細化的各個獨立詞彙中,詞彙的結構比長文字簡單,對於計算機而言,更容易理解和分析,所以,分詞往往是自然語言處理的第一步。

 

對於英文文字,句子中的詞彙可以通過空格很容易得進行劃分,但是在我們中文中則不然,沒有明顯的劃分標誌,所以需要通過專門的方法(演算法)進行分詞。在Python中,有多種庫實現了各種方法支援中文分詞,例如:jieba、hanlp、pkuseg等。在本篇中,先來說說jieba分詞。

 

1 四種模式分詞

 

(1)精確模式:試圖將句子最精確地切開,適合文字分析。精確分詞模式對應的方法是jieba.cut,該方法接受四個輸入引數: 需要分詞的字串;cut_all 引數用來控制是否採用全模式,值為False時表示採用精確分詞模式;HMM 引數用來控制是否使用 HMM 模型。

(2)全模式:把句子中所有的可以成詞的詞語都掃描出來,速度非常快,但是不能解決歧義。全模式同樣是呼叫jieba.cut方法實現,不過cut_all引數值設定為True。

(3)搜尋引擎模式:在精確模式的基礎上,對長詞再詞切分,提高召回率,適合用於搜尋引擎分詞。搜尋引擎模式對應的方法是jieba.cut_for_search。該方法接受兩個引數:需要分詞的字串;是否使用 HMM 模型。該方法適合用於搜尋引擎構建倒排索引的分詞,粒度比較細。

注意,待分詞的字串可以是 unicode 或 UTF-8 字串、GBK 字串。注意:不建議直接輸入 GBK 字串,可能無法預料地錯誤解碼成 UTF-8。 另外,jieba.cut 以及 jieba.cut_for_search 返回的結構都是一個可迭代的 generator,可以使用 for 迴圈來獲得分詞後得到的每一個詞語(unicode),或者用jieba.lcut 以及 jieba.lcut_for_search 直接返回 list。

在分詞文字過大時,可以使用jieba.enable_parallel()來開啟並行分詞模式,使用多進行進行分詞。

In [1]:
import jieba
In [4]:
strt = "據報導,因雷暴雨天氣,該地區川億線變壓器跌落式熔斷器引流線燒斷,造成電壓不穩" 
 
# 精確模式,預設hi精確模式,所以可以不指定cut_all=False
sl = jieba.cut(strt, cut_all=False, HMM=False)
print("精確模式分詞結果:", ",".join(sl))
print('\n')

# 全模式
sl = jieba.cut(strt, cut_all=True)
print("全模式分詞結果:", ",".join(sl) )
print('\n')
 
# 搜尋引擎模式
sl = jieba.cut_for_search(strt)
print("搜尋引擎模式分詞結果:", ",".join(sl))
 
精確模式分詞結果: 據,報導,,,因,雷暴雨,天氣,,,該,地區,川,億,線,變壓器,跌落,式,熔斷器,引流,線,燒,斷,,,造成,電壓,不,穩


全模式分詞結果: 據,報導,,,因,雷暴,雷暴雨,暴雨,雨天,天氣,,,該地,地區,川,億,線,變壓,變壓器,跌落,式,熔斷,熔斷器,引流,流線,燒,斷,,,造成,成電,電壓,不穩


搜尋引擎模式分詞結果: 據,報導,,,因,雷暴,暴雨,雷暴雨,天氣,,,該,地區,川,億線,變壓,變壓器,跌落,式,熔斷,熔斷器,引流,線,燒斷,,,造成,電壓,不,穩
 

在上面分詞結果中,可以看出,部分專有名詞並沒有被正確劃分,這時候可以試試將HMM引數設定為True,表示使用隱馬爾可夫模型發現新詞:

In [29]:
sl = jieba.cut(strt, cut_all=False, HMM=True)
print(",".join(sl))
 
據,報導,,,因,雷暴雨,天氣,,,該,地區,川,億線,變壓器,跌落,式,熔斷器,引流線,燒斷,,,造成,電壓,不穩
 

遺憾的是,就算使用了發現新詞功能,只是進一步劃分出了“燒斷”一詞,仍然沒有對“川億線”、“跌落式”、“引流線”等詞彙進行正確提取,這是隱馬爾可夫模型分詞原理決定的,只能發現在原始訓練詞庫彙總頻率稍高的新詞,而對沒有出現過的新詞無能為力。這時候,我們可以自定義詞典來新增新詞。

 

2 自定義詞典分詞

 

2.1 新增詞典

 

自定義詞典分詞是指在分詞前,使用者手動將部分詞彙新增到結巴分詞的詞庫中。通過這種方式可以進一步提取出預設詞庫中沒有的詞彙,提高分詞準確率。

在新增詞典通過jieba.load_userdict(file_name)方法實現,引數file_name是詞典檔名,詞典中一個詞佔一行,每一行分三部分,即詞語、詞頻(可省略)、詞性(可省略),用空格隔開,順序不可顛倒。

 

可見,“川億線”一次已被正確劃分。再來試試jieba.load_userdict(file_name)新增詞庫,這種方式的好處是可以一次性新增多個詞。我們先將詞彙寫入檔案,請讀者自行新建一個名為“dict.txt”的檔案,寫入一下內容:

 

跌落式 90 a 熔斷器 80 a 跌落式熔斷器 10 n 引流線 80 n

In [2]:
strt = "據報導,因雷暴雨天氣,該地區川億線變壓器跌落式熔斷器引流線燒斷,造成電壓不穩" 
In [10]:
jieba.load_userdict('dict.txt')
sl = jieba.cut(strt, cut_all=False)
print(",".join(sl))
 
據,報導,,,因,雷暴雨,天氣,,,該,地區,川,億線,變壓器,跌落式熔斷器,引流線,燒斷,,,造成,電壓,不,穩
 

從上面結果可以看出,新增自定義的詞庫後,原本沒有被正確劃分出來的詞,如“跌落式熔斷器”,“引流線”等都被正確劃分出來了。

 

2.2 調整詞典

 

新增自定義的詞典後,有時候我們還是需要對詞典進行微調。

  • jieba.suggest_freq()

在上述我們自定義的詞典中包含“跌落式”、“熔斷器”、“跌落式熔斷器”三個詞,但是分詞結果中按最長的“跌落式熔斷器”進行分詞,如果我們分別進行劃分,可以使用jieba.suggest_freq()方法調節單個詞語的詞頻,使其能(或不能)被正確劃分出來。

In [16]:
jieba.suggest_freq(('跌落式', '熔斷器'), tune=True)
Out[16]:
0
In [24]:
sl = jieba.cut(strt, cut_all=False, HMM=False)
print(",".join(sl))
 
據,報導,,,因,雷暴雨,天氣,,,該,地區,川,億,線,變壓器,跌落,式,熔斷器,引流線,燒,斷,,,造成,電壓,不穩
 

這時候,“跌落式”、“熔斷器”兩個詞就沒有在被完整劃分。

 

同時,對於居中的“不穩”一詞,在上述分詞結果中被分成了兩個部分,這裡也可以通過jieba.suggest_freq()調整,使其分為一個完整詞。

In [12]:
jieba.suggest_freq('不穩', tune=True)
Out[12]:
12
In [13]:
sl = jieba.cut(strt, cut_all=False)
print(",".join(sl))
 
據,報導,,,因,雷暴雨,天氣,,,該,地區,川,億線,變壓器,跌落式熔斷器,引流線,燒斷,,,造成,電壓,不穩
In [25]:
sl = jieba.cut(strt, cut_all=False)
print(",".join(sl))
 
據,報導,,,因,雷暴雨,天氣,,,該,地區,川,億線,變壓器,跌落,式,熔斷器,引流線,燒斷,,,造成,電壓,不穩
 
  • jieba.add_word(word, freq=None, tag=None)

jieba.add_word()用於向詞庫中新增一個詞,該方法有三個引數:word指需要新增的詞,freq是詞頻,tag是詞性,其中,詞頻和詞性可省略。例如,在上述分詞中沒有被正確劃分為一個詞“川億線”新增到詞庫中:

In [27]:
jieba.add_word('川億線')
sl = jieba.cut(strt, cut_all=False, HMM=False)
print(",".join(sl))
 
據,報導,,,因,雷暴雨,天氣,,,該,地區,川億線,變壓器,跌落,式,熔斷器,引流線,燒,斷,,,造成,電壓,不穩
 
  • del_word(word)

del_word(word)可以刪除詞庫中的一個詞。

In [28]:
jieba.del_word('川億線')
sl = jieba.cut(strt, cut_all=False, HMM=False)
print(",".join(sl))
 
據,報導,,,因,雷暴雨,天氣,,,該,地區,川,億,線,變壓器,跌落,式,熔斷器,引流線,燒,斷,,,造成,電壓,不穩
 

3 詞性標註

 

jieba分詞中,通過jieba.posseg提供詞性標註支援。jieba.posseg.cut()方法返回一個生成器,jieba.posseg.lcut()放回一個列表。

In [30]:
import jieba.posseg
In [39]:
sl = jieba.posseg.cut(strt)
for x in sl:
    print('分詞: ', x.word, '     詞性: ', x.flag)
 
分詞:  據      詞性:  p
分詞:  報導      詞性:  v
分詞:  ,      詞性:  x
分詞:  因      詞性:  p
分詞:  雷暴雨      詞性:  nr
分詞:  天氣      詞性:  n
分詞:  ,      詞性:  x
分詞:  該      詞性:  r
分詞:  地區      詞性:  n
分詞:  川      詞性:  j
分詞:  億線      詞性:  m
分詞:  變壓器      詞性:  n
分詞:  跌落      詞性:  v
分詞:  式      詞性:  k
分詞:  熔斷器      詞性:  n
分詞:  引流線      詞性:  n
分詞:  燒斷      詞性:  v
分詞:  ,      詞性:  x
分詞:  造成      詞性:  v
分詞:  電壓      詞性:  n
分詞:  不穩      詞性:  a
 

結巴分詞中,各種詞性標註含義如下所示:

 

 

4 關鍵詞提取

 

4.1 基於 TF-IDF 演算法的關鍵詞抽取

 

TF-IDF演算法可以分為TF和IDF兩個部分來理解,TF指的是Term Frequency,意思詞頻。

 

 

IDF指的是inverse document frequency,即逆文件頻率。

 

 

從計算公式上可以看出,TF衡量的是某個詞在當前文件的頻率,IDF衡量的是包含某個詞的文件佔比,當佔比越少時,IDF越大。TF-IDF演算法的基本思想:某一個詞在本篇文章中詞頻越高(TF越大),而在其他文章中出現次數少(IDF小),則越有可能是本篇文章的關鍵詞。將TF與IDF相結合,進行關鍵詞篩選,於是有了一個新的量TF-IDF:

 

 

jieba分詞中實現了TF-IDF演算法,進行關鍵詞選取,可以通過呼叫jieba.analyse.extract_tags()使用這一功能,在extract_tags()有4個引數:

  • sentence 為待提取的文字
  • topK 為返回幾個 TF/IDF 權重最大的關鍵詞,預設值為 20
  • withWeight 為是否一併返回關鍵詞權重值,預設值為 False
  • allowPOS 僅包括指定詞性的詞,預設值為空,即不篩選
In [40]:
import jieba.analyse as analyse
In [41]:
txt = "自然語言處理是電腦科學領域與人工智慧領域中的一個重要方向。它研究能實現人與計算機之間用自然語言進行有效通訊的各種理論和方法。自然語言處理是一門融語言學、電腦科學、數學於一體的科學。因此,這一領域的研究將涉及自然語言,即人們日常使用的語言,所以它與語言學的研究有著密切的聯絡,但又有重要的區別。自然語言處理並不是一般地研究自然語言,而在於研製能有效地實現自然語言通訊的計算機系統,特別是其中的軟體系統。因而它是電腦科學的一部分。"
In [47]:
analyse.extract_tags(txt, topK=5, withWeight=True, allowPOS=())
Out[47]:
[('自然語言', 1.1237629576061539),
 ('電腦科學', 0.4503481350267692),
 ('語言學', 0.27566262244215384),
 ('研究', 0.2660770221507693),
 ('領域', 0.24979825580353845)]
 

4.2 基於 TextRank 演算法的關鍵詞抽取

 

TextRank演算法在原理上比DF-ITF演算法複雜許多,本文不在展開介紹。在jieba分詞庫中使用TextRank演算法通過呼叫jieba.analyse.textrank方法實現。該方法引數與上述使用TF-IDF演算法時呼叫的analyse.extract_tags的引數一致。

In [44]:
txt = "自然語言處理是電腦科學領域與人工智慧領域中的一個重要方向。它研究能實現人與計算機之間用自然語言進行有效通訊的各種理論和方法。自然語言處理是一門融語言學、電腦科學、數學於一體的科學。因此,這一領域的研究將涉及自然語言,即人們日常使用的語言,所以它與語言學的研究有著密切的聯絡,但又有重要的區別。自然語言處理並不是一般地研究自然語言,而在於研製能有效地實現自然語言通訊的計算機系統,特別是其中的軟體系統。因而它是電腦科學的一部分。"
In [46]:
analyse.textrank(txt, topK=5, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'))
Out[46]:
['研究', '領域', '電腦科學', '實現', '處理']

相關文章