TM04 Chinese word segmentation
1. 中文斷詞基礎流程
1.1 載入資料
with open("data/blessing.txt", encoding="utf8") as fin:
text = fin.read()
print(type(fin))
print(type(text))
print("Number of characters: %d" % len(text))
print(text[:int(len(text)/10)])
1.2 斷句
在斷句前先清除每個段落前的首行縮排(兩個全形空白)。並且取代換行符號(要注意並不是每次都先取代換行符號,比方說你可以比較一下這章節所用的文本和PTT文章在斷句上的差異)。
text_refined = text.replace(" ", "").replace("\n", "")
sentences = text_refined.split("。")
print("Number of sentences: %d" % len(sentences))
print(sentences[:20])
1.3 斷詞
# !pip install jieba
import jieba
print(sentences[0])
print(jieba.cut(sentences[0]))
print(list(jieba.cut(sentences[0])))
2. 繁簡斷詞效果比較
jieba套件在繁簡兩種字體的斷詞效果差不少,必須要謹慎考量。但若要用結巴,比較好的方法應該是先轉為簡體,然後用簡體斷詞的位置來斷開繁體句子。
2.1 繁簡互換
繁轉簡,問題不大,但簡又轉回繁,幾乎跟原本的繁體字意義都不同了。
# !pip install hanziconv
from hanziconv import HanziConv
sim = HanziConv.toSimplified('臺北市長柯文哲')
print(sim)
tra = HanziConv.toTraditional(sim)
print(tra)
台北市长柯文哲
颱北市長柯文哲
2.2 繁簡斷詞結果比較
tra = '台北市長柯文哲'
sim = HanziConv.toSimplified(tra)
print(" / ".join(jieba.cut(tra)))
print(" / ".join(jieba.cut(sim)))
台北市 / 長 / 柯文 / 哲 # 直接斷繁體
台北 / 市长 / 柯文 / 哲 # 直接斷簡體
sim = HanziConv.toSimplified(sentences[0])
sim_cutted = list(jieba.cut(sim))
sim_cutted_to_tra = [HanziConv.toTraditional(term) for term in sim_cutted]
print(" / ".join(sim_cutted_to_tra))
print(" / ".join(jieba.cut(sim)))
舊曆 / 的 / 年底 / 畢竟 / 最像 / 年底 / , / 村鎮 / 上 / 不必 / 說 / , / 就 / 在 / 天空 / 中 / 也 / 顯齣 / 將 / 到 / 新年 / 的 / 氣象 / 來
旧历 / 的 / 年底 / 毕竟 / 最像 / 年底 / , / 村镇 / 上 / 不必 / 说 / , / 就 / 在 / 天空 / 中 / 也 / 显出 / 将 / 到 / 新年 / 的 / 气象 / 来
2.3 用簡體斷詞位置切割繁體字詞
方法一(推薦)
def restore(text, toks):
results = []
offset = 0
for tok in toks:
results.append(text[offset:offset + len(tok)])
offset += len(tok)
return results
print(restore(tra, list(jieba.cut(HanziConv.toSimplified(tra)))))
['國民黨', '高雄市', '長', '候選人', '韓國', '瑜', '今天', '晚上', '開', '直播', ',', '針對', '最近', '引起爭議', '的', '一些', '事', '說明', ',', '強調', '他', '並', '沒有', '消費', '愛心', '菜販', '陳樹菊', ',', '全力支持', '「', '館長', '」', '做', '公益', ',', '所謂', '高雄', '三鳳宮', '靈籤', '可能', '是', '「', '新型', '態', '選舉', '詐騙', '」', ';', '至於', '為', '強調', '招商引資', '說出', '「', '陪', '睡', '」', '遭批', ',', '他', '以後', '不會', '說', '這麼', '有', '張力', '的', '用語', ',', '畢竟', '是', '市長', '候選人', '。']
方法一(測試二)
# Segmenting traditional Chinese directly
sample = "國民黨高雄市長候選人韓國瑜今天晚上開直播,針對最近引起爭議的一些事說明,強調他並沒有消費愛心菜販陳樹菊,全力支持「館長」做公益,所謂高雄三鳳宮靈籤可能是「新型態選舉詐騙」;至於為強調招商引資說出「陪睡」遭批,他以後不會說這麼有張力的用語,畢竟是市長候選人。"
print(" / ".join(jieba.cut(sample)))
# Segmenting traditional Chinese by cutted simplified index
print("/ ".join(restore(sample, list(jieba.cut(HanziConv.toSimplified(sample))))))
國民黨 / 高雄市 / 長 / 候選人 / 韓國瑜 / 今天 / 晚上 / 開 / 直播 / , / 針對 / 最近 / 引起 / 爭議 / 的 / 一些 / 事 / 說明 / , / 強調 / 他 / 並沒有 / 消費 / 愛心 / 菜販 / 陳樹菊 / , / 全力支持 / 「 / 館長 / 」 / 做 / 公益 / , / 所謂 / 高雄 / 三鳳 / 宮靈 / 籤 / 可能 / 是 / 「 / 新型 / 態選舉 / 詐騙 / 」 / ; / 至於 / 為 / 強調 / 招商 / 引資 / 說 / 出 / 「 / 陪 / 睡 / 」 / 遭批 / , / 他 / 以後不會 / 說 / 這麼 / 有 / 張力 / 的 / 用語 / , / 畢 / 竟是 / 市長 / 候選人 / 。
國民黨/ 高雄市/ 長/ 候選人/ 韓國/ 瑜/ 今天/ 晚上/ 開/ 直播/ ,/ 針對/ 最近/ 引起爭議/ 的/ 一些/ 事/ 說明/ ,/ 強調/ 他/ 並/ 沒有/ 消費/ 愛心/ 菜販/ 陳樹菊/ ,/ 全力支持/ 「/ 館長/ 」/ 做/ 公益/ ,/ 所謂/ 高雄/ 三鳳宮/ 靈籤/ 可能/ 是/ 「/ 新型/ 態/ 選舉/ 詐騙/ 」/ ;/ 至於/ 為/ 強調/ 招商引資/ 說出/ 「/ 陪/ 睡/ 」/ 遭批/ ,/ 他/ 以後/ 不會/ 說/ 這麼/ 有/ 張力/ 的/ 用語/ ,/ 畢竟/ 是/ 市長/ 候選人/ 。
方法二
import numpy
print(sentences[0])
def tra_cutted_by_sim(tra):
sim_cutted = jieba.cut(HanziConv.toSimplified(tra))
term_len = [len(w) for w in list(sim_cutted)]
term_index = [0] + list(numpy.cumsum(term_len)[:-1])
term_dict = {k:v for k, v, in zip(term_index, term_len)}
tra_cutted = [tra[k:k+v] for k, v in term_dict.items()]
return tra_cutted
print(tra_cutted_by_sim(sentences[0]))
sim_cutted = jieba.cut(HanziConv.toSimplified(sentences[0]))
sim_cutted_to_tra = [HanziConv.toTraditional(term) for term in sim_cutted]
print(sim_cutted_to_tra)
舊曆的年底畢竟最像年底,村鎮上不必說,就在天空中也顯出將到新年的氣象來
['舊曆', '的', '年底', '畢竟', '最像', '年底', ',', '村鎮', '上', '不必', '說', ',', '就', '在', '天空', '中', '也', '顯出', '將', '到', '新年', '的', '氣象', '來']
['舊曆', '的', '年底', '畢竟', '最像', '年底', ',', '村鎮', '上', '不必', '說', ',', '就', '在', '天空', '中', '也', '顯齣', '將', '到', '新年', '的', '氣象', '來']
(Option) 斷詞效果的測試工具
tra_cutted = list(jieba.cut(sample))
cut_by_sim = tra_cutted_by_sim(sample)
i, j = 0, 0
print("Cutted tra directly:", tra_cutted)
print("Cutted by sim:", cut_by_sim)
print(len(tra_cutted), len(cut_by_sim))
while i < len(tra_cutted) and j < len(cut_by_sim):
if tra_cutted[i] == cut_by_sim[j]:
print("[%d]%s\t[%d]%s" % (i, tra_cutted[i], j, cut_by_sim[j]))
i += 1
j += 1
else:
ei, ej = 1, 1
listi, listj = [], []
listi.append(tra_cutted[i])
listj.append(cut_by_sim[j])
while "".join(listi) != "".join(listj):
if(len("".join(listi)) < len("".join(listj))):
listi.append(tra_cutted[i+ei])
ei += 1
else:
listj.append(cut_by_sim[j+ej])
ej += 1
print("\t[%d_%d]%s\t[%d_%d]%s" % (i, ei, listi, j, ej, listj))
i += ei
j += ej
3. POS、停用詞、標點符號去除
jieba使用者自定義詞典
jieba.load_userdict("data/userdict.txt")
print(" / ".join(jieba.cut(sentences[0])))
舊曆 / 的 / 年底 / 畢竟 / 最 / 像 / 年底 / , / 村鎮 / 上 / 不必 / 說 / , / 就 / 在 / 天空 / 中 / 也 / 顯出 / 將到 / 新年 / 的 / 氣象 / 來
標記POS
import jieba.posseg
print(list((jieba.posseg.cut(sample))))
去除標點符號並計算詞頻
Removing punctuation marks with the information of unicode category: https://en.wikipedia.org/wiki/Unicode_character_property
# Just an testing
import unicodedata
chars = "『「,。、標點符號"
print([unicodedata.category(ch) for ch in chars])
['Ps', 'Ps', 'Po', 'Po', 'Po', 'Lo', 'Lo', 'Lo', 'Lo']
import unicodedata
from collections import Counter
word_counts = Counter()
for sentence in sentences:
for word in jieba.cut(sentence):
if len(word) > 1 or not unicodedata.category(word).startswith('P'):
word_counts[word] += 1
print(word_counts.most_common(20))
[('的', 345), ('了', 197), ('她', 155), ('我', 88), ('是', 86), ('也', 77), ('說', 65), ('在', 61), ('來', 57), ('就', 56), ('裏', 54), ('有', 46), ('麽', 45), ('不', 37), ('祥林嫂', 35), ('又', 34), ('一個', 33), ('你', 33)
, ('去', 32), ('他', 28)]
刪去停用詞
Manipulate a Chinese stopword list.
What is "U+FEFF"? The Unicode character U+FEFF is the byte order mark, or BOM, and is used to tell the difference between big- and little-endian UTF-16 encoding. If you decode the web page using the right codec, Python will remove it for you. Examples:
Sol. 1. Add stop words temporarily
stopwords = ["的", "了", "是", "也", "在"]
Sol. 2. Add stop words from files
with open("data/stopwords_zh-tw.txt", encoding="utf-8") as fin:
stopwords = fin.read().split("\n")[1:]
print(stopwords)
print(len(stopwords))
Remove stop words
import unicodedata
from collections import Counter
word_counts = Counter()
for sentence in sentences:
for word in jieba.cut(sentence):
if word in stopwords:
continue
if len(word) > 1 or not unicodedata.category(word).startswith('P'):
word_counts[word] += 1
print(word_counts.most_common(20))
print(len(word_counts))
Using CKIPTagger
CKIPTagger在沒有GPU資源時非常的慢,所以即使有CPU,跑起來也不快。
他是用bi-lstm,所以要先安裝tensorflow,版本大於1.14,小於2,那就是1.15
Last updated