Python - 知名 Jieba 中文斷詞工具教學

今天要介紹的這個算是很知名的中文斷詞工具,這個是大陸人發明的工具,並且將其開源在 GitHub 上,而且有積極維護中,非常不錯。但是可想而知它的這個工具對簡體中文分詞會比較準確,繁體中文雖然用這工具也還可以,但是有一些像是台灣用語就比較難斷得很好。

Jieba 安裝教學

conda 安裝

1
conda install -c conda-forge jieba

pip 安裝

1
pip install jieba

適合的 Python 版本 3 或 2 都支援,但是建議大家還是盡快使用 Python3,畢竟 Python2 已經官方公佈不支援了。

Jieba 原理介紹

Jieba 斷詞主要是结合:

  • 規則斷詞

    主要是透過詞典,在對句子進行斷詞的時候,將句子的每個字與詞典中的詞進行匹配,找到則斷詞,否則無法斷詞。

  • 統計斷詞

    主要是看如果相連的字在不同的文本中出現的次數越多,就推斷這相連的字很可能就是一個詞。因此就可以利用字與字相鄰出现的頻率來做統計。當高於某一個臨界值時,便可認為此字組是一個詞語。

透過以上兩種策略,再根據 Jieba 文檔的技術原理說明:

  • 使用前綴詞典進行詞圖掃描,生成所有成詞情況的有向無環圖(DAG, directed acyclic graph )

    例如:上 => 上海 => 上海市

    再透過,使用動態規劃查找最大概率路徑

  • 使用 HMM 模型(Hidden Markov Models)找出『未登錄詞』

    所謂 HMM 模型就是一種統計斷詞的方法,未登錄詞就是想成詞典中沒有這種詞,必須經過統計來得知新的詞語。

這邊就不在多講裡面技術的演算法是怎樣,有興趣的話可以根據關鍵字去查詢演算法。

Jieba 斷詞模式

最著名的功能就是提供斷詞模式,主要分為

  • 精確模式

    將句子最精確的切開,適合文本分析

  • 全模式

    把句子中所有的可以成詞的詞語都斷出来,速度非常快。

  • 搜索引擎模式

    在精確模式的基礎上,對長的詞語再次切分,提高召回率,適合用於搜索引擎分詞。

操作方式:

  • 透過 jieba.cut () 來進行斷詞,cut_all 參數為 True 的話為全模式,預設為 False,也就是精確模式
  • jibea.cut_for_search () 是搜索引擎模式
  • cut ()、cur_for_search () 返回的結構都是一个可迭代的 generator,因此使用 for 迴圈來取得每個斷詞。

簡體中文測試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import jieba

documents = ['我来自北京清华大学', '我喜欢写程式', '每天发技术文章']
# 精確模式
for sentence in documents:
seg_list = jieba.cut(sentence)
print('/'.join(seg_list))

print('---------------')

# 全模式
for sentence in documents:
seg_list = jieba.cut(sentence, cut_all=True)
print('/'.join(seg_list))

print('---------------')

# 搜索引擎模式
for sentence in documents:
seg_list = jieba.cut_for_search(sentence)
print('/'.join(seg_list))

輸出如下:

1
2
3
4
5
6
7
8
9
10
11
我/来自/北京/清华大学
我/喜欢/写/程式
每天/发/技术/文章
---------------
我/来自/北京/清华/清华大学/华大/大学
我/喜欢/写/程式
每天/发/技术/文章
---------------
我/来自/北京/清华/华大/大学/清华大学
我/喜欢/写/程式
每天/发/技术/文章

可以看出其實是滿準確的,尤其是精確模式,斷詞斷得很正確,而這輸出結果也可以看出精確模式、全模式、搜索引擎模式差別在哪。

繁體中文測試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import jieba

documents = ['我來自北京清華大學', '我喜歡寫程式', '每天發技術文章']
# 精確模式
for sentence in documents:
seg_list = jieba.cut(sentence)
print('/'.join(seg_list))

print('---------------')

# 全模式
for sentence in documents:
seg_list = jieba.cut(sentence, cut_all=True)
print('/'.join(seg_list))

print('---------------')

# 搜索引擎模式
for sentence in documents:
seg_list = jieba.cut_for_search(sentence)
print('/'.join(seg_list))

輸出如下:

1
2
3
4
5
6
7
8
9
10
11
我來/自/北京/清華大學
我/喜歡/寫/程式
每天/發技術/文章
---------------
我/來/自/北京/清/華/大/學
我/喜/歡/寫/程式
每天/發/技/術/文章
---------------
我來/自/北京/清華大學
我/喜歡/寫/程式
每天/發技術/文章

將簡體中文成繁體中文,可以看出精確模式下,第三句每天發技術文章斷不準確,但其他句斷的前面簡體中文一樣。

jieba.lcut () 示範

也可以使用 lcut (),意思跟 cut () 是一樣的,只是返回的型態變成 list,方便使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import jieba

documents = ['我来自北京清华大学', '我喜欢写程式', '每天发技术文章']
# 精確模式
for sentence in documents:
seg_list = jieba.lcut(sentence)
print(seg_list)

print('---------------')

# 全模式
for sentence in documents:
seg_list = jieba.lcut(sentence, cut_all=True)
print(seg_list)

print('---------------')

# 搜索引擎模式
for sentence in documents:
seg_list = jieba.lcut_for_search(sentence)
print(seg_list)

輸出如下:

1
2
3
4
5
6
7
8
9
10
11
['我', '来自', '北京', '清华大学']
['我', '喜欢', '写', '程式']
['每天', '发', '技术', '文章']
---------------
['我', '来自', '北京', '清华', '清华大学', '华大', '大学']
['我', '喜欢', '写', '程式']
['每天', '发', '技术', '文章']
---------------
['我', '来自', '北京', '清华', '华大', '大学', '清华大学']
['我', '喜欢', '写', '程式']
['每天', '发', '技术', '文章']

台灣新聞文檔測試

這是我去拿最近的新聞稿的文字:

1
蘇貞昌表示,春節期間中國武漢肺炎疫情急遽升高,他在年假第一天就到中央流行疫情指揮中心聽取簡報,並宣布提升到二級開設。年假期間,衛福部及相關機關幾乎都放棄休假,每天監控及因應各項疫情,並宣布防疫資源整備情形及最新防疫作為,隨時讓國人瞭解最新疫情發展。因為資訊透明,應變迅速,讓國人感受到「有政府,可放心」,感謝陳其邁副院長費心督導,對於年假期間各防疫機關人員的堅守崗位,也表示肯定與感謝。

用 Jieba 測試台灣用語的繁體中文效果如何:

1
2
3
4
5
import jieba

news = '蘇貞昌表示,春節期間中國武漢肺炎疫情急遽升高,他在年假第一天就到中央流行疫情指揮中心聽取簡報,並宣布提升到二級開設。年假期間,衛福部及相關機關幾乎都放棄休假,每天監控及因應各項疫情,並宣布防疫資源整備情形及最新防疫作為,隨時讓國人瞭解最新疫情發展。因為資訊透明,應變迅速,讓國人感受到「有政府,可放心」,感謝陳其邁副院長費心督導,對於年假期間各防疫機關人員的堅守崗位,也表示肯定與感謝。'
seg_list = jieba.lcut(news)
print(seg_list)

輸出如下:

1
['蘇貞昌', '表示', ',', '春節', '期間', '中國', '武漢', '肺炎', '疫情', '急遽', '升高', ',', '他', '在', '年', '假', '第一天', '就', '到', '中央', '流行', '疫情', '指揮', '中心', '聽取', '簡報', ',', '並', '宣布', '提升', '到', '二級', '開設', '。', '年', '假期', '間', ',', '衛福部', '及', '相關', '機關', '幾乎', '都', '放棄', '休假', ',', '每天', '監控及', '因應', '各項', '疫情', ',', '並', '宣布', '防疫', '資源', '整備', '情形', '及', '最新', '防疫', '作為', ',', '隨時', '讓', '國人', '瞭解', '最新', '疫情', '發展', '。', '因為', '資訊', '透明', ',', '應變', '迅速', ',', '讓', '國人', '感受', '到', '「', '有', '政府', ',', '可', '放心', '」', ',', '感謝', '陳', '其邁', '副', '院長', '費心督導', ',', '對', '於', '年', '假期', '間', '各', '防疫', '機關', '人員', '的', '堅守崗位', ',', '也', '表示', '肯定', '與', '感謝', '。']

發現以下幾點事情:

  1. 武漢肺炎、年假、陳其邁,這些詞沒斷好,推斷可能大陸用語沒有這樣的用法,此外台灣的人名也一定會是新詞沒辦法被斷好。
  2. 標點符號會自動被斷出來

還好,Jieba 提供自定義詞典的功能,用來避免以上的情況。

Jieba 自定義詞典

  • 如果 Jieba 內建詞庫沒有你要的詞,可以建立自定義的詞典

    讓 Jieba 根據你自定義的詞典來斷出你想要的詞。雖然可能會覺得這樣不就很 Hard Code 嗎?沒錯就是這樣,因為大部分的情況下雖說是依賴 Jieba 斷詞的能力,但在特殊的名詞或者台灣用語可以採用該方法

  • 用法:jieba.load_userdict (file_path)

    file_path 為自定義詞典的檔案路徑

  • 自定義詞典格式

    一個词占一行,每一行分三部分:詞語、詞頻(可省略)、磁性(可省略),用空格隔開,顺序不可錯誤。file 必須為 UTF-8 編碼。

    詞頻省略的話 Jieba 內建會自動計算,保證可以分出你自定義的詞頻。簡單來說,你可以想成,你自定義的詞語一定是優先度最大,儘管跟其他有衝突,你定義的 Jieba 一定會優先斷出來,保證正確性。

根據以上的新聞稿,我們將武漢肺炎年假陳其邁,加入自定義詞典試試看有沒有辦法成功斷詞出來。

在專案路徑下新增一個檔案叫做:userdict.txt

內容如下:

1
2
3
武漢肺炎
年假
陳其邁

程式碼如下:

1
2
3
4
5
6
7
import jieba

jieba.load_userdict('./userdict.txt')

news = '蘇貞昌表示,春節期間中國武漢肺炎疫情急遽升高,他在年假第一天就到中央流行疫情指揮中心聽取簡報,並宣布提升到二級開設。年假期間,衛福部及相關機關幾乎都放棄休假,每天監控及因應各項疫情,並宣布防疫資源整備情形及最新防疫作為,隨時讓國人瞭解最新疫情發展。因為資訊透明,應變迅速,讓國人感受到「有政府,可放心」,感謝陳其邁副院長費心督導,對於年假期間各防疫機關人員的堅守崗位,也表示肯定與感謝。'
seg_list = jieba.lcut(news)
print(seg_list)

輸出結果:

1
['蘇貞昌', '表示', ',', '春節', '期間', '中國', '武漢肺炎', '疫情', '急遽', '升高', ',', '他', '在', '年假', '第一天', '就', '到', '中央', '流行', '疫情', '指揮', '中心', '聽取', '簡報', ',', '並', '宣布', '提升', '到', '二級', '開設', '。', '年', '假期', '間', ',', '衛福部', '及', '相關', '機關', '幾乎', '都', '放棄', '休假', ',', '每天', '監控及', '因應', '各項', '疫情', ',', '並', '宣布', '防疫', '資源', '整備', '情形', '及', '最新', '防疫', '作為', ',', '隨時', '讓', '國人', '瞭解', '最新', '疫情', '發展', '。', '因為', '資訊', '透明', ',', '應變', '迅速', ',', '讓', '國人', '感受', '到', '「', '有', '政府', ',', '可', '放心', '」', ',', '感謝', '陳其邁', '副', '院長', '費心督導', ',', '對', '於', '年', '假期', '間', '各', '防疫', '機關', '人員', '的', '堅守崗位', ',', '也', '表示', '肯定', '與', '感謝', '。']

可以看出,年假、武漢肺炎、陳其邁被成功斷出來,但你可能會發現年假期間沒有被斷好,所以可以考慮在把年假期間加入自定義詞典裡面。

基本上,我自己覺得在還沒有自定義詞典的話,大部分的詞語也算斷得不錯,再加入自定義詞典差不多也夠了。

說實在如果只是要做特定領域的機器人,像是現在很夯的 Line Bot、Messenger Bot,且沒有很精確的要求,其實很多機器人它可能只是用到句子斷詞的功能,藉此找到該句子的一些關鍵字,而根據關鍵字,機器人再回答相對應的句子。所以,有時候你會發現你如果只打一個關鍵字,機器人還是回你同樣的話,通常就是這樣設計的。

而除了自定義詞典,也可以在程式碼中動態加入自定義的詞,來幫助斷詞正確性提高:

  • jieba.add_word (‘武漢肺炎’)

  • jieba.suggest_freq (‘武漢肺炎’, tune=True)

    tune 的參數代表該詞語要不要被成功斷出來。

Jieba 透過 TF-IDF 找出句子關鍵字

此外,Jieba 也有提供根據 TF-IDF 算法來找出句子的關鍵字。

程式碼如下:

1
2
3
4
5
6
import jieba.analyse
news = '蘇貞昌表示,春節期間中國武漢肺炎疫情急遽升高,他在年假第一天就到中央流行疫情指揮中心聽取簡報,並宣布提升到二級開設。年假期間,衛福部及相關機關幾乎都放棄休假,每天監控及因應各項疫情,並宣布防疫資源整備情形及最新防疫作為,隨時讓國人瞭解最新疫情發展。因為資訊透明,應變迅速,讓國人感受到「有政府,可放心」,感謝陳其邁副院長費心督導,對於年假期間各防疫機關人員的堅守崗位,也表示肯定與感謝。'
tags = jieba.analyse.extract_tags(news, topK=5, withWeight=True)

for tag in tags:
print('word:', tag[0], 'tf-idf:', tag[1])

jieba.analyse.extract_tags 主要有以下的參數:

  • sentence 為句子
  • topK 代表返回 TF-IDF 權重最大的關鍵字,默認為 20
  • withWeight 代表是否返回關鍵字權重值,默認為 False
  • allowPOS 代表指定詞性,默認為空,也就是不篩選

輸出如下:

1
2
3
4
5
word: 疫情 tf-idf: 0.4687853402985507
word: 防疫 tf-idf: 0.4334283486630435
word: 機關 tf-idf: 0.34651500008405794
word: 國人 tf-idf: 0.34651500008405794
word: 感謝 tf-idf: 0.34651500008405794

可以看到疫情的最高,實際上句子確實也是疫情出現最多次~符合關鍵字。

此外,除了 TF-IDF 算法,還有提供基於 TextRank 算法的關鍵字抽取,這邊就不多介紹,有興趣的話可以看文檔。

Jieba 詞性標註功能

透過 jiba.posseg.cut () 可以將句子中的每個斷詞進行詞性標註。

程式碼:

1
2
3
words = jieba.posseg.cut('我喜欢写程式')
for word, flag in words:
print(f'{word} {flag}')

輸出如下:

1
2
3
4
我 r
喜欢 v
写 v
程式 n

至於旁邊英文字母代表甚麼詞性,需要參考官方提供的說明:

標籤 含意 標籤 含意 標籤 含意 標籤 含意
n 普通名词 f 方位名词 s 处所名词 t 时间
nr 人名 ns 地名 nt 机构名 nw 作品名
nz 其他专名 v 普通动词 vd 动副词 vn 名动词
a 形容词 ad 副形词 an 名形词 d 副词
m 数量词 q 量词 r 代词 p 介词
c 连词 u 助词 xc 其他虚词 w 标点符号
PER 人名 LOC 地名 ORG 机构名 TIME 时间

總結

總的來說,身為一個開源工具提供這麼多功能還是很棒的,儘管對於繁體中文或台灣用語不是那麼好,不過我認為解決方法就是:

  • https://github.com/ldkrsi/jieba-zh_TW

    這個是有人將 Jieba 內建的詞典改成台灣繁體版本,因此可以考慮用這個 jieba 工具,缺點就是沒辦法跟著 Jieba 工具持續更新

  • 用自定義詞典來解決台灣繁體用語

  • 使用台灣中研院開源的 CkipTagger,這個我還沒用過,聽說繁體中文斷詞很不錯,改天試試看,在發文紀錄 XD

此外,官方文檔也有提供多種程式語言的版本,非常的不錯。

Jieba 斷詞的速度也滿快的,當然如果參考其他語言的實作可能會更快。總得來說,如果是做簡單的機器人且沒有那麼在乎準確性,使用 Jieba 是還不錯的選擇,此外也可以用在文檔分析上,幫助斷詞以便之後進行更進一步的操作。