分詞就是中學學的斷句:
北,京,歡,迎,你
北京,歡,迎,你
北京,歡迎,你 等等如果沒有語料庫的話就是這樣的列舉。
分詞我們可以根據語料庫裡面的次來分,比如語料庫裡面有[北京,歡迎,你,歡,迎]則上面列出的就是我們可能的分詞結果用程式來是實現就是
需要的語料庫:
給出了每個詞出現的概率。相當於建立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([ 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)] + word
str: 字串,傳入的句子
"""# 儲存狀態
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:
continue
# 確定依賴的之前狀態存在且(達成轉移條件:詞存在)
if memory[j-1] and str[j:i+1] in word_prob:
# 狀態轉移過程
for state in memory[j-1]:
return memory[-1]
測試
print(sentence_break("北京歡迎你"))
計算每一句話出現的概率,並返回最大概率的一句話
## todo 編寫word_segment_*****函式來實現對輸入字串的分詞
import math
def word_segment_*****(input_str):
"""1. 對於輸入字串做分詞,並返回所有可行的分詞之後的結果。
2. 針對於每乙個返回結果,計算句子的概率
3. 返回概率最高的最作為最後結果
input_str: 輸入字串 輸入格式:「今天天氣好」
best_segment: 最好的分詞結果 輸出格式:["今天","天氣","好"]
"""# todo: 第一步: 計算所有可能的分詞結果,要保證每個分完的詞存在於詞典裡,這個結果有可能會非常多。
segments = sentence_break(input_str) # 儲存所有分詞的結果。如果次字串不可能被完全切分,則返回空列表(list)
# 格式為:segments = [["今天",「天氣」,「好」],["今天",「天「,」氣」,「好」],["今「,」天",「天氣」,「好」],...]
# todo: 第二步:迴圈所有的分詞結果,並計算出概率最高的分詞結果,並返回
best_segment = list()
best_score = 0
for seg in segments:
# todo ...
if seg:
score = 0
for word in seg:
# 防止下溢,取log
score += math.log(word_prob[word])
if best_score == 0:
best_segment = seg
best_score = score
else:
if score > best_score:
best_segment = seg
best_score = score
return best_segment
因為上面的分詞和計算概率分開進行,我們可以通過建立乙個有向無環圖來實現同時進行(維特比演算法)
## todo 編寫word_segment_viterbi函式來實現對輸入字串的分詞
import math
def word_segment_viterbi(input_str):
"""1. 基於輸入字串,詞典,以及給定的unigram概率來建立dag(有向圖)。
2. 編寫維特比演算法來尋找最優的path
3. 返回分詞結果
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-100
score = memory[j][0] - math.log(prob)
# 狀態更新
if memory[i][0] == 0:
memory[i][0] = score
memory[i][1] = memory[j][1] + [word]
else:
if score < memory[i][0]:
memory[i][0] = score
memory[i][1] = memory[j][1] + [word]
return memory[-1][1]
目前比較流行的中文分詞工具
jieba:做最好的 python 中文分詞元件
清華大學thulac:乙個高效的中文詞法分析工具包
中科院計算所nlpir
哈工大ltp
foolnltk可能不是最快的開源中文分詞,但很可能是最準的開源中文分詞
參考資料:
零基礎入門NLP 新聞文字分類
1 transformer transformer是一種新的編碼器 解碼器架構,它僅使用注意力機制而不是rnn來編碼每個位置,並關聯有關其自身的輸入和輸出的兩個遠端單詞,然後可以並行化處理,因而加快訓練。2 attention 模仿人類視覺注意力機制,學習出乙個對影象特徵的權重分布,再把這個權重分布...
Python零基礎入門之函式
函式的命名空間和作用域 函式的三類命名空間 內建 全域性 區域性 兩大作用域 全域性 內建和全域性命名空間都屬於全域性作用域 區域性 區域性命名空間屬於區域性作用域 什麼是作用域鏈?就是由外而內的命名空間中的中的變數的生存週期都是就近原則 全域性作用域 大區域性作用域 小區域性作用域 函式的兩大引數...
Task1 零基礎入門NLP之新聞文字分類
天池對應比賽 賽題以自然語言處理為背景,要求選手對新聞文字進行分類,這是乙個典型的字元識別問題,通過這道賽題可以引導我們走入自然語言處理的世界,帶我們接觸nlp的預處理 模型構建和模型訓練等知識點。對賽題進行初步的認識和了解 賽題資料由以下幾個部分構成 訓練集20w條樣本,測試集a包括5w條樣本,測...