中文斷詞
在中文自然語言處理NLP中,要對一堆文字詞語組成的文章進行分析, 分析前要先拆解文章,也就是斷詞,我們要分析的對象是詞語,而不是一個一個中文字,這跟英文完全不同,因為英文的斷詞就直接用標點符號、空白去區隔即可。
目前繁體中文斷詞系統有 中研院 CKIP 以及 jieba,在一些舊的文章中都提到 jieba 無法適當地處理繁體中文,而有替換繁體中文字典的改進作法,不過目前 jieba 已經到了 0.42 版,以下先了解官方的套件的功能,再看看需不需要修改繁體中文字典。
jieba 演算法
- 基於前綴詞典實現高效的詞圖掃描,生成句子中漢字所有可能成詞情況所構成的有向無環圖 (DAG)
- 採用了動態規劃查找最大概率路徑,找出基於詞頻的最大切分組合
- 對於未登錄詞,採用了基於漢字成詞能力的 HMM 模型,使用了 Viterbi 算法
安裝
可直接用 pip 安裝,或是將 jieba source code 的 jieba 目錄放在目前的工作目錄,或是 site-packages 目錄中
如果要使用 paddle 的分詞語詞性標注功能,必須安裝 paddlepaddle-tiny
pip3 install paddlepaddle-tiny==1.6.1
先直接下載 source code 試試看
wget https://github.com/fxsjy/jieba/archive/v0.42.1.tar.gz -O jieba-0.41.1.tgz
tar zxvf jieba-0.41.1.tgz
virtual environemnt
virtualenv --system-site-packages /root/venv-jieba
source /root/venv-jieba/bin/activate
## 如果不使用 paddlepaddle,這兩個套件也可以不安裝
pip3 install numpy==1.16.4
pip3 install paddlepaddle-tiny==1.6.1
# 把 soruce code 中的 jieba 目錄移動到工作目錄中
mv ~/temp/download/jieba-0.41.1/jieba ~/temp
斷詞
有四種斷詞模式
- 精確模式,試圖將句子最精確地切開,適合文本分析
- 完整模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;
- 搜索引擎模式,在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於搜索引擎分詞。
- paddle模式,利用PaddlePaddle深度學習框架,訓練序列標注(雙向GRU)網絡模型實現分詞。同時支持詞性標注。paddle模式使用需安裝 paddlepaddle-tiny
函式
- jieba.cut 方法接受四個輸入參數: 需要分詞的字符串;cutall 參數用來控制是否採用全模式;HMM 參數用來控制是否使用 HMM 模型;usepaddle 參數用來控制是否使用paddle模式下的分詞模式,paddle模式採用延遲加載方式,通過enable_paddle接口安裝paddlepaddle-tiny,並且import相關代碼
- jieba.cutforsearch 方法接受兩個參數:需要分詞的字符串;是否使用 HMM 模型。該方法適合用於搜索引擎構建倒排索引的分詞,粒度比較細
- 待分詞的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建議直接輸入 GBK 字符串,可能無法預料地錯誤解碼成 UTF-8
- jieba.cut 以及 jieba.cutforsearch 返回的結構都是一個可迭代的 generator,可以使用 for 循環來獲得分詞後得到的每一個詞語(unicode),或者用 jieba.lcut 以及 jieba.lcutforsearch 直接返回 list
- jieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定義分詞器,可用於同時使用不同詞典。jieba.dt 為默認分詞器,所有全局分詞相關函數都是該分詞器的映射。
# encoding=utf-8
import jieba
print()
print("完整模式:")
seg_list = jieba.cut("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list)) # 全模式
print()
print("精確模式:")
seg_list = jieba.cut("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精確模式
print()
print("預設是精確模式:")
seg_list = jieba.cut("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。") # 預設是精確模式
print(", ".join(seg_list))
print()
print("搜索引擎模式:")
seg_list = jieba.cut_for_search("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。") #
print(", ".join(seg_list))
print()
print("Paddle Mode:")
jieba.enable_paddle()# 啓動paddle模式。 0.40版之後開始支持,早期版本不支持
strs=["肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。","乒乓球拍賣完了","新竹清華大學"]
for str in strs:
seg_list = jieba.cut(str,use_paddle=True) # 使用paddle模式
print("Paddle Mode: " + '/'.join(list(seg_list)))
執行結果
完整模式:
Building prefix dict from the default dictionary ...
Loading model from cache /tmp/jieba.cache
Loading model cost 0.433 seconds.
Prefix dict has been built successfully.
Full Mode: 肺炎/ 疫情/ 的/ 挑/ 戰/ 日益/ 嚴/ 峻/ ,/ 新竹/ 清/ 華/ 大/ 學/ 自/ 農/ 曆/ 年/ 起/ 陸/ 續/ 已/ 採/ 取/ 了/ 量/ 測/ 體/ 溫/ 等/ 全面/ 的/ 防疫/ 措施/ 。
精確模式:
Default Mode: 肺炎/ 疫情/ 的/ 挑戰/ 日益/ 嚴峻/ ,/ 新竹/ 清華大學/ 自農/ 曆/ 年/ 起/ 陸續/ 已/ 採取/ 了/ 量/ 測體/ 溫/ 等/ 全面/ 的/ 防疫/ 措施/ 。
預設是精確模式:
肺炎, 疫情, 的, 挑戰, 日益, 嚴峻, ,, 新竹, 清華大學, 自農, 曆, 年, 起, 陸續, 已, 採取, 了, 量, 測體, 溫, 等, 全面, 的, 防疫, 措施, 。
搜索引擎模式:
肺炎, 疫情, 的, 挑戰, 日益, 嚴峻, ,, 新竹, 清華大學, 自農, 曆, 年, 起, 陸續, 已, 採取, 了, 量, 測體, 溫, 等, 全面, 的, 防疫, 措施, 。
Paddle Mode:
W0327 11:45:31.179752 21561 init.cc:157] AVX is available, Please re-compile on local machine
Paddle enabled successfully......
Paddle Mode: 肺炎/疫情/的/挑戰/日益/嚴/峻,新竹清華大學自農曆年起陸續/已/採取/了/量測體溫/等/全面/的/防疫/措施/。
Paddle Mode: 乒乓/球拍/賣/完/了
Paddle Mode: 新竹/清華大學
- jieba.lcut() lcut(),意思跟cut()是一樣的,只是返回的型態變成list
# encoding=utf-8
import jieba
print()
print("完整模式:")
seg_list = jieba.lcut("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。", cut_all=True)
print("Full Mode: ", seg_list) # 全模式
print()
print("精確模式:")
seg_list = jieba.lcut("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。", cut_all=False)
print("Default Mode: ", seg_list) # 精確模式
print()
print("預設是精確模式:")
seg_list = jieba.lcut("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。") # 預設是精確模式
print(seg_list)
print()
print("搜索引擎模式:")
seg_list = jieba.lcut_for_search("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。") #
print(seg_list)
print()
print("Paddle Mode:")
jieba.enable_paddle()# 啓動paddle模式。 0.40版之後開始支持,早期版本不支持
strs=["肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。","乒乓球拍賣完了","新竹清華大學"]
for str in strs:
seg_list = jieba.lcut(str,use_paddle=True) # 使用paddle模式
print("Paddle Mode: ", seg_list)
自訂詞典
雖然 jieba 有新詞識別能力,但是自行添加新詞可以保證更高的正確率
用法: jieba.loaduserdict(filename)
file_name 為文件類對象或自定義詞典的路徑
詞典格式和 dict.txt 一樣,一個詞佔一行;每一行分三部分:詞語、詞頻(可省略)、詞性(可省略),用空格隔開,順序不可顛倒。file_name 若為路徑或二進制方式打開的文件,則文件必須為 UTF-8 編碼。
詞頻省略時使用自動計算的方式處理,能保證分出該詞的詞頻。
使用 addword(word, freq=None, tag=None) 和 delword(word) 可在程序中動態修改詞典。
使用 suggest_freq(segment, tune=True) 可調節單個詞語的詞頻,使其能(或不能)被分出來。
注意:自動計算的詞頻在使用 HMM 新詞發現功能時可能無效。
在專案路徑下新增一個檔案叫做:userdict.txt
內容如下:
農曆年
量測
體溫
日益嚴峻
可在程式一開始,就載入自訂詞典
jieba.load_userdict('userdict.txt')
執行結果
精確模式:
Default Mode: ['肺炎', '疫情', '的', '挑戰', '日益嚴峻', ',', '新竹', '清華大學', '自', '農曆年', '起陸續', '已', '採取', '了', '量測', '體溫', '等', '全面', '的', '防疫', '措施', '。']
可動態調整詞語的頻率
jieba.suggest_freq(('陸續'), True)
print()
print("精確模式2:")
seg_list = jieba.lcut("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。", cut_all=False)
print("Default Mode: ", seg_list) # 精確模式
執行結果
精確模式2:
Default Mode: ['肺炎', '疫情', '的', '挑戰', '日益嚴峻', ',', '新竹', '清華大學', '自', '農曆年', '起', '陸續', '已', '採取', '了', '量測', '體溫', '等', '全面', '的', '防疫', '措施', '。']
也可以直接替換詞典
ithomeironman/day16NLP_Chinese/ 可下載一個繁體中文的字典 dict.txt.big
# encoding=utf-8
import jieba
jieba.set_dictionary('dict.txt.big')
# with open('stops.txt', 'r', encoding='utf8') as f:
# stops = f.read().split('\n')
print()
print("精確模式:")
seg_list = jieba.lcut("肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。", cut_all=False)
print("Default Mode: ", seg_list)
關鍵詞抽取
TF-IDF 方法
# jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())
# sentence 為待提取的文本
# topK 為返回幾個 TF/IDF 權重最大的關鍵詞,默認值為 20
# withWeight 為是否一並返回關鍵詞權重值,默認值為 False
# allowPOS 僅包括指定詞性的詞,默認值為空,即不篩選
# jieba.analyse.TFIDF(idf_path=None) 新建 TFIDF 實例,idf_path 為 IDF 頻率文件
TextRank 方法
jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v')) 直接使用,接口相同,注意默認過濾詞性。
jieba.analyse.TextRank() 新建自定義 TextRank 實例
# -*- coding: utf-8 -*-
import jieba
import jieba.analyse
jieba.set_dictionary('dict.txt.big')
print()
text = '肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。'
tags = jieba.analyse.extract_tags(text, topK=10)
print(tags)
# ['挑戰', '嚴峻', '清華大學', '農曆年', '陸續', '採取', '測體溫', '防疫', '新竹', '肺炎']
print()
print('textrank:')
for x, w in jieba.analyse.textrank(text, withWeight=True):
print('%s %s' % (x, w))
# textrank:
# 日益 1.0
# 全面 1.0
# 肺炎 0.6631715416020616
# 防疫 0.6631715416020616
# 疫情 0.6605033585768562
# 措施 0.6605033585768562
# 新竹 0.3607120276929184
# 了量 0.3607120276929184
詞性標注
ref: 彙整中文與英文的詞性標註代號:結巴斷詞器與FastTag / Identify the Part of Speech in Chinese and English
結巴預設會將標點符號標示為「x」,而不是「w」。而且英文會被標示為「eng」
# -*- coding: utf-8 -*-
import jieba
import jieba.posseg as pseg
text = '肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。'
seg_list = pseg.lcut(text)
# print("Default Mode: ", seg_list)
for word, flag in seg_list:
print("", word, " : ", flag)
執行結果
肺炎 : n
疫情 : n
的 : uj
挑戰 : vn
日益 : n
嚴峻 : a
, : x
新竹 : ns
清華大學 : nt
自 : p
農 : ng
曆 : zg
年 : q
起 : v
陸 : nr
續 : v
已 : d
採 : v
取 : v
了 : ul
量 : n
測 : v
體 : ng
溫 : v
等 : u
全面 : n
的 : uj
防疫 : vn
措施 : n
。 : x
word cloud
參考這篇文章中文自然語言處理基礎 以及資源
下載檔案:
cloud_mask7.png
sumsun.ttf
stops.txt
安裝其它套件
pip3 install collections
pip3 install wordcloud
pip3 install matplotlib
yum -y install python-imaging
import jieba
jieba.set_dictionary('dict.txt.big') # 如果是使用繁體文字,請記得去下載繁體字典來使用
with open('stops.txt', 'r', encoding='utf8') as f:
stops = f.read().split('\n')
text = "肺炎疫情的挑戰日益嚴峻,新竹清華大學自農曆年起陸續已採取了量測體溫等全面的防疫措施。"
from collections import Counter
from wordcloud import WordCloud
from matplotlib import pyplot as plt
stops.append('\n') ## 換行符號,加入停用字中,可以把它拿掉
stops.append('\n\n')
terms = [t for t in jieba.cut(text, cut_all=True) if t not in stops]
sorted(Counter(terms).items(), key=lambda x:x[1], reverse=True) ## 這個寫法很常出現在Counter中,他可以排序,list每個item出現的次數。
plt.clf()
wordcloud = WordCloud(font_path="simsun.ttf") ##做中文時務必加上字形檔
wordcloud.generate_from_frequencies(frequencies=Counter(terms))
plt.figure(figsize=(15,15))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
plt.savefig('cloud1.png')
from PIL import Image
import numpy as np
alice_mask = np.array(Image.open("cloud_mask7.png")) ## 請更改cloud_mask7.png路徑
wc = WordCloud(background_color="white", max_words=2000, mask=alice_mask, font_path="simsun.ttf")
wc.generate_from_frequencies(Counter(terms)) ## 請更改Counter(terms)
wc.to_file("cloud2.png") ##如果要存檔,可以使用
# plt.clf()
# plt.imshow(wc, interpolation='bilinear')
# plt.axis("off")
# plt.figure()
# plt.imshow(alice_mask, cmap=plt.cm.gray, interpolation='bilinear')
# plt.axis("off")
# plt.savefig('cloud2.png')
References
2017
沒有留言:
張貼留言