零基础入门NLP之搭建中文分词工具
1 文本分析的基本過程
分詞就是中學學的斷句:
舉個例子來說明:假設我們輸入一句話:北京歡迎你。則有可能的斷句為:
北,京,歡,迎,你
北京,歡,迎,你
北京,歡迎,你? 等等如果沒有語料庫的話就是這樣的枚舉。
分詞我們可以根據語料庫里面的次來分,比如語料庫里面有[北京,歡迎,你,歡,迎]則上面列出的就是我們可能的分詞結果用程序來是實現就是
需要的語料庫:
https://github.com/liangqi/chinese-frequency-word-list
給出了每個詞出現的概率。相當于建立unigram模型代碼如下
from collections import Counter import random import numpy as np with open('./data/現代漢語常用詞表.txt') as f:lines = f.readlines() sum = 0 word_prob = Counter() for line in lines:columns = line.strip().split()# 重復詞頻率直接相加,(相同詞多次出現是因為發音不同,即語義也不同,這里不做區分)word_prob[columns[0]] += int(columns[-1])sum += int(columns[-1]) # 頻率轉為概率 for word in word_prob:word_prob[word] /= sum這段代碼的功能是讀取語料庫中的單詞,并統計每個詞出現的概率,我們可以去測試一下。
print([{word: word_prob[word]} for word in random.sample(word_prob.keys(), 2)]) print("詞典大小:%d" % len(word_prob)) print(np.sum(list(word_prob.values())))成功讀取語料庫并計算出每個詞的概率后,我們可以開始基于每個詞的概率開始進行分割。代碼如下
def sentence_break(str):"""求該句話在當前詞典下的全切分。思路:狀態轉移,設M[i]是從句子開始到第i個字所組成句的全切分,word是以字i結尾的可在詞典中找到的詞,則M[i] = M[i-len(word)] + wordstr: 字符串,傳入的句子"""# 存儲狀態memory = [[] for _ in range(len(str))]for i in range(0, len(str)):for j in range(0, i+1):# 從開始到當前cursor視為一個詞if j == 0:if str[j:i+1] in word_prob:memory[i].append([str[j:i+1]])continue# 確定依賴的之前狀態存在且(達成轉移條件:詞存在)if memory[j-1] and str[j:i+1] in word_prob:# 狀態轉移過程for state in memory[j-1]:memory[i].append(state + [str[j:i+1]])return memory[-1]測試
print(sentence_break("北京歡迎你"))計算每一句話出現的概率,并返回最大概率的一句話?
## TODO 編寫word_segment_naive函數來實現對輸入字符串的分詞 import mathdef word_segment_naive(input_str):"""1. 對于輸入字符串做分詞,并返回所有可行的分詞之后的結果。2. 針對于每一個返回結果,計算句子的概率3. 返回概率最高的最作為最后結果input_str: 輸入字符串 輸入格式:“今天天氣好”best_segment: 最好的分詞結果 輸出格式:["今天","天氣","好"]"""# TODO: 第一步: 計算所有可能的分詞結果,要保證每個分完的詞存在于詞典里,這個結果有可能會非常多。 segments = sentence_break(input_str) # 存儲所有分詞的結果。如果次字符串不可能被完全切分,則返回空列表(list)# 格式為:segments = [["今天",“天氣”,“好”],["今天",“天“,”氣”,“好”],["今“,”天",“天氣”,“好”],...]# TODO: 第二步:循環所有的分詞結果,并計算出概率最高的分詞結果,并返回best_segment = list()best_score = 0for seg in segments:# TODO ...if seg:score = 0for word in seg:# 防止下溢,取logscore += math.log(word_prob[word])if best_score == 0:best_segment = segbest_score = scoreelse:if score > best_score:best_segment = segbest_score = scorereturn best_segment因為上面的分詞和計算概率分開進行,我們可以通過建立一個有向無環圖來實現同時進行(維特比算法)
## TODO 編寫word_segment_viterbi函數來實現對輸入字符串的分詞 import mathdef word_segment_viterbi(input_str):"""1. 基于輸入字符串,詞典,以及給定的unigram概率來創建DAG(有向圖)。2. 編寫維特比算法來尋找最優的PATH3. 返回分詞結果input_str: 輸入字符串 輸入格式:“今天天氣好”best_segment: 最好的分詞結果 輸出格式:["今天","天氣","好"]"""# TODO: 第一步:根據詞典,輸入的句子,以及給定的unigram概率來創建帶權重的有向圖(Directed Graph)# 有向圖的每一條邊是一個單詞的概率(只要存在于詞典里的都可以作為一個合法的單詞),這些概率在 word_prob,如果不在word_prob里的單詞但在# 詞典里存在的,統一用概率值1e-100。# 圖是為了直觀起見,邊表示字或詞及其概率,節點存儲狀態,圖有沒有其實無所謂,從本質上講其實就是個狀態轉移算法# 每個節點的狀態包含-log(P)和當前最優切分memory = [[0, []] for _ in range(len(input_str)+1)]# TODO: 第二步: 利用維特比算法來找出最好的PATH, 這個PATH是P(sentence)最大或者 -log P(sentence)最小的PATH。# TODO: 第三步: 根據最好的PATH, 返回最好的切分for i in range(1, len(input_str)+1):for j in range(i):# 這里偷個懶,默認沒有形成詞的單字可以在詞典中找到(如果不成立事實上會返回完整句子,因為-log(1e-100)必然小于該值加某個非負數word = input_str[j:i]prob = word_prob[word] if word in word_prob else 1e-100score = memory[j][0] - math.log(prob)# 狀態更新if memory[i][0] == 0:memory[i][0] = scorememory[i][1] = memory[j][1] + [word]else:if score < memory[i][0]:memory[i][0] = scorememory[i][1] = memory[j][1] + [word]return memory[-1][1]?
目前比較流行的中文分詞工具
jieba:做最好的 Python 中文分詞組件https://github.com/fxsjy/jieba
清華大學THULAC:一個高效的中文詞法分析工具包
中科院計算所NLPIR
哈工大LTP
FoolNLTK可能不是最快的開源中文分詞,但很可能是最準的開源中文分詞
參考資料:https://zhuanlan.zhihu.com/p/95599399
總結
以上是生活随笔為你收集整理的零基础入门NLP之搭建中文分词工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python正则表达式元字符用法_正则表
- 下一篇: Linux下gcc入门