零基礎入門NLP之搭建中文分詞工具

2021-10-05 13:53:28 字數 4327 閱讀 5078

分詞就是中學學的斷句:

北,京,歡,迎,你

北京,歡,迎,你

北京,歡迎,你  等等如果沒有語料庫的話就是這樣的列舉。

分詞我們可以根據語料庫裡面的次來分,比如語料庫裡面有[北京,歡迎,你,歡,迎]則上面列出的就是我們可能的分詞結果用程式來是實現就是

需要的語料庫:

給出了每個詞出現的概率。相當於建立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條樣本,測...