中研院開源NLP套件「CKIPtagger」,繁中不結巴

Jim Ya
11 min readOct 10, 2019

--

雙十假日有時間來使用一下前陣子中研院詞庫小組開源的NLP套件「CKIPtagger」,根據該團隊說明,在中文處理部份,準確率達97%,比以往使用的結巴(Jieba)高七個百分點。趕緊來試用一下~

套件安裝

官方有提供3種安裝方法如下:

標準安裝:pip install -U ckiptagger[tf,gdown]
最小安裝:pip install -U ckiptagger
完整安裝:pip install -U ckiptagger[tfgpu,gdown]

差別就在於要不要裝「Tensorflow」與「gdown」這兩個套件,由於我本身的環境已經有安裝Tensorflow了,就不再安裝了,tfgpu則是GPU版本的Tensorflow。而 gdown 是與 Google Drive 之間的API,可以從 Google Drive上下載資料,因為CKIPtagger在處理文字所使用訓練好的模型是要再下載的,而研究團隊也把模型放在 Google Drive 上(但還是有提供其他載點啦…)。

先裝好CKIPtagger套件

pip install -U ckiptagger

如果你的環境是沒有裝Tensorflow的,記得也要裝

pip install tensorflow==1.13.1

在官方文件有寫到,Tensorflow ≥1.13.1或<2,Tensorflow-GPU也一樣,所以如果是使用Tensorflow 2.0 版的,因為 2.0 版的tf.variable_scope這個部份有改變,所以在使用CKIPtagger時會報錯,建議還是乖乖用 1.13.1 版。

接著從下面連結下載模型,看自已喜歡放哪都可以,只要 path 找得到就好,下載後是一個 .zip 的壓縮檔,裡面共5個資料夾,有3個資料夾存放的就是我們要使用的模型。

http://ckip.iis.sinica.edu.tw/data/ckiptagger/data.zip

先從CKIPtagger導入要用的模組,分別有「WS(斷詞)」、「POS(詞性標注)」及「NER(實體辨識)」三個功能。

from ckiptagger import WS, POS, NER

再來創建3個實例

ws = WS(<path>)
pos = POS(<path>)
ner = NER(<path>)

路徑依照個人剛剛所下載的模型檔存放位置填入,注意,路徑只需要指到第一層就好,不用指到特定檔案,比如我的是 D:\CKIPtagger_data\data,我為了要清礎區分各個資料夾的用途,所以多包了一層,一般解壓縮完應該就是在 .\data 裡面。

執行後若出現警告(Warning):The TensorFlow contrib module will not be included in TensorFlow 2.0.。別在意,還是可以正常使用的。

馬上用官方給的範例文本來試一下,先創建一個 list來放文本

sentence_list = ["傅達仁今將執行安樂死,卻突然爆出自己20年前遭緯來體育台封殺,    
他不懂自己哪裡得罪到電視台。",
"美國參議院針對今天總統布什所提名的勞工部長趙小蘭展開認可聽
證會,預料她將會很順利通過參議院支持,成為該國有史以來第一
位的華裔女性內閣成員。",
"",
"土地公有政策??還是土地婆有政策。.",
"… 你確定嗎… 不要再騙了……",
"最多容納59,000個人,或5.9萬人,再多就不行了.這是環評的結
論.",
"科長說:1,坪數對人數為1:3。2,可以再增加。"]

Step.1 斷詞(WS)

返回的是一個 list。還有二個參數可設定,分別是 recommend_dictionary及coerce_dictionary,使用方法都是帶入dict。

word_s = ws(sentence_list,
sentence_segmentation=True,
segment_delimiter_set={'?', '?', '!', '!', '。', ',',
',', ';', ':', '、'})
['傅達仁', '今', '將', '執行', '安樂死', ',', '卻', '突然', '爆出', '自己', '20', '年', '前', '遭', '緯來', '體育台', '封殺', ',', '他', '不', '懂', '自己', '哪裡', '得罪到', '電視台', '。'] ['美國', '參議院', '針對', '今天', '總統', '布什', '所', '提名', '的', '勞工部長', '趙小蘭', '展開', '認可', '聽證會', ',', '預料', '她', '將', '會', '很', '順利', '通過', '參議院', '支持', ',', '成為', '該', '國', '有史以來', '第一', '位', '的', '華裔', '女性', '內閣', '成員', '。'] [] ['土地公', '有', '政策', '?', '?', '還是', '土地', '婆', '有', '政策', '。', '.'] ['…', ' ', '你', '確定', '嗎', '…', ' ', '不要', '再', '騙', '了', '…', '…'] ['最多', '容納', '59', ',', '000', '個', '人', ',', '或', '5.9萬', '人', ',', '再', '多', '就', '不行', '了', '.', '這', '是', '環評', '的', '結論', '.'] ['科長', '說', ':', '1', ',', '坪數', '對', '人數', '為', '1', ':', '3', '。', '2', ',', '可以', '再', '增加', '。']

Step.2 詞性標注(POS)

一樣返回一個 list。記得這裡帶入的是經過斷詞後的 list,不是原始文本。

word_p = pos(word_s)['Nb', 'Nd', 'D', 'VC', 'Na', 'COMMACATEGORY', 'D', 'D', 'VJ', 'Nh', 'Neu', 'Nf', 'Ng', 'P', 'Nb', 'Na', 'VC', 'COMMACATEGORY', 'Nh', 'D', 'VK', 'Nh', 'Ncd', 'VJ', 'Nc', 'PERIODCATEGORY'] ['Nc', 'Nc', 'P', 'Nd', 'Na', 'Nb', 'D', 'VC', 'DE', 'Na', 'Nb', 'VC', 'VC', 'Na', 'COMMACATEGORY', 'VE', 'Nh', 'D', 'D', 'Dfa', 'VH', 'VC', 'Nc', 'VC', 'COMMACATEGORY', 'VG', 'Nes', 'Nc', 'D', 'Neu', 'Nf', 'DE', 'Na', 'Na', 'Na', 'Na', 'PERIODCATEGORY'] [] ['Nb', 'V_2', 'Na', 'QUESTIONCATEGORY', 'QUESTIONCATEGORY', 'Caa', 'Na', 'Na', 'V_2', 'Na', 'PERIODCATEGORY', 'PERIODCATEGORY'] ['ETCCATEGORY', 'WHITESPACE', 'Nh', 'VK', 'T', 'ETCCATEGORY', 'WHITESPACE', 'D', 'D', 'VC', 'Di', 'ETCCATEGORY', 'ETCCATEGORY'] ['VH', 'VJ', 'Neu', 'COMMACATEGORY', 'Neu', 'Nf', 'Na', 'COMMACATEGORY', 'Caa', 'Neu', 'Na', 'COMMACATEGORY', 'D', 'D', 'D', 'VH', 'T', 'PERIODCATEGORY', 'Nep', 'SHI', 'Na', 'DE', 'Na', 'PERIODCATEGORY'] ['Na', 'VE', 'COLONCATEGORY', 'Neu', 'COMMACATEGORY', 'Na', 'P', 'Na', 'VG', 'Neu', 'COLONCATEGORY', 'Neu', 'PERIODCATEGORY', 'Neu', 'COMMACATEGORY', 'D', 'D', 'VHC', 'PERIODCATEGORY']

Step.3 實體辨識(NER)

一樣返回一個 list。數字表示這個斷詞在文本中的位置,以(0, 3, ‘PERSON’, ‘傅達仁’)為例,從0的位置開始到3,如同 for i in range(0, 3),i 會迭代”0”、”1”、”2”。

word_n = NER(word_s, word_p){(18, 22, 'DATE', '20年前'), (0, 3, 'PERSON', '傅達仁'), (23, 28, 'ORG', '緯來體育台')}
{(60, 62, 'NORP', '華裔'), (2, 5, 'ORG', '參議院'), (56, 58, 'ORDINAL', '第一'), (21, 24, 'PERSON', '趙小蘭'), (0, 2, 'GPE', '美國'), (17, 21, 'ORG', '勞工部長'), (42, 45, 'ORG', '參議院'), (11, 13, 'PERSON', '布什'), (7, 9, 'DATE', '今天')}
set()
{(0, 3, 'PERSON', '土地公')}
set()
{(14, 18, 'CARDINAL', '5.9萬'), (4, 6, 'CARDINAL', '59'), (7, 10, 'CARDINAL', '000')}
{(12, 13, 'CARDINAL', '1'), (14, 15, 'CARDINAL', '3'), (16, 17, 'CARDINAL', '2'), (4, 5, 'CARDINAL', '1')}

將WS→POS→NER三個步驟組合在一起來看

def combine_wandp(w_list, p_list):
assert len(w_list) == len(p_list)
for w, p in zip(w_list, p_list):
print ('{}({})'.format(w, p), end='\u3000')
for i, sentence in enumerate(sentence_list):
print ("'{}'".format(sentence))
combine_wandp(word_s[i], word_p[i])
print ()
for n in sorted(word_n[i]):
print (n)
print ('\n')

'傅達仁今將執行安樂死,卻突然爆出自己20年前遭緯來體育台封殺,他不懂自己哪裡得罪到電視台。'
傅達仁(Nb) 今(Nd) 將(D) 執行(VC) 安樂死(Na) ,(COMMACATEGORY) 卻(D) 突然(D) 爆出(VJ) 自己(Nh) 20(Neu) 年(Nf) 前(Ng) 遭(P) 緯來(Nb) 體育台(Na) 封殺(VC) ,(COMMACATEGORY) 他(Nh) 不(D) 懂(VK) 自己(Nh) 哪裡(Ncd) 得罪到(VJ) 電視台(Nc) 。(PERIODCATEGORY) 
(0, 3, 'PERSON', '傅達仁')
(18, 22, 'DATE', '20年前')
(23, 28, 'ORG', '緯來體育台')


'美國參議院針對今天總統布什所提名的勞工部長趙小蘭展開認可聽證會,預料她將會很順利通過參議院支持,成為該國有史以來第一位的華裔女性內閣成員。'
美國(Nc) 參議院(Nc) 針對(P) 今天(Nd) 總統(Na) 布什(Nb) 所(D) 提名(VC) 的(DE) 勞工部長(Na) 趙小蘭(Nb) 展開(VC) 認可(VC) 聽證會(Na) ,(COMMACATEGORY) 預料(VE) 她(Nh) 將(D) 會(D) 很(Dfa) 順利(VH) 通過(VC) 參議院(Nc) 支持(VC) ,(COMMACATEGORY) 成為(VG) 該(Nes) 國(Nc) 有史以來(D) 第一(Neu) 位(Nf) 的(DE) 華裔(Na) 女性(Na) 內閣(Na) 成員(Na) 。(PERIODCATEGORY) 
(0, 2, 'GPE', '美國')
(2, 5, 'ORG', '參議院')
(7, 9, 'DATE', '今天')
(11, 13, 'PERSON', '布什')
(17, 21, 'ORG', '勞工部長')
(21, 24, 'PERSON', '趙小蘭')
(42, 45, 'ORG', '參議院')
(56, 58, 'ORDINAL', '第一')
(60, 62, 'NORP', '華裔')

Hint:\u3000代表空白格,print ()的結尾默認為新的一行’\n’,透過end參數設定可以有不一樣的呈現。

以上就是今天小小的試用「CKIPtagger」,感覺斷得真的不錯,如果是處理繁中的資料,趕緊用上它吧!

Reference:

https://github.com/ckiplab/ckiptagger/wiki/Chinese-README

--

--

Responses (1)