維特比演算法之中文分詞
阿新 • • 發佈:2020-08-04
維特比原理
尋找上圖最短路徑
中文分詞
此專案需要的資料:
綜合類中文詞庫.xlsx: 包含了中文詞,當做詞典來用
以變數的方式提供了部分unigram概率word_prob
舉個例子: 給定詞典=[我們 學習 人工 智慧 人工智慧 未來 是], 另外我們給定unigram概率:p(我們)=0.25, p(學習)=0.15, p(人工)=0.05, p(智慧)=0.1, p(人工智慧)=0.2, p(未來)=0.1, p(是)=0
- 獲取中文字典
在變數word_prob沒有出現的的單詞但是出現在詞典裡的,統一把概率設定成為0.00001
import xlrd file_path = './data/綜合類中文詞庫.xlsx' workbook = xlrd.open_workbook(file_path) booksheet = workbook.sheet_by_index(0) col_values = booksheet.col_values(0) dic_words = {} max_len_word = 0 for word in col_values: dic_words[word] = 0.00001 len_word = len(word) if len_word > max_len_word: max_len_word = len_word word_prob = {"北京": 0.03, "的": 0.08, "天": 0.005, "氣": 0.005, "天氣": 0.06, "真": 0.04, "好": 0.05, "真好": 0.04, "啊": 0.01, "真好啊": 0.02, "今": 0.01, "今天": 0.07, "課程": 0.06, "內容": 0.06, "有": 0.05, "很": 0.03, "很有": 0.04, "意思": 0.06, "有意思": 0.005, "課": 0.01, "程": 0.005, "經常": 0.08, "意見": 0.08, "意": 0.01, "見": 0.005, "有意見": 0.02, "分歧": 0.04, "分": 0.02, "歧": 0.005} for key, value in word_prob.items(): dic_words[key] = value
- 根據詞典,輸入的句子和 word_prob來建立帶權重的有向圖(Directed Graph)
#從頭開始遍歷,找到字典中存在的所有候選詞 def create_graph(input_str): N = len(input_str) graph = {} for idx_end in range(1, N + 1): print('idx_end',idx_end) temp_list = [] max_split = min(idx_end, max_len_word) # 最大切分長度為idx_end,即這次迴圈的結果 for idx_start in range(idx_end - max_split, idx_end): # 就是 0 : idx_end word = input_str[idx_start:idx_end] # 根據起止索引得到單詞 print('idx_start',idx_start, word) if word in dic_words: print(idx_start,word) temp_list.append(idx_start) graph[idx_end] = temp_list print(graph) print('_______________') return graph
- 通過加權有向圖,實現維特比演算法
def word_segment_viterbi(input_str): graph = create_graph(input_str) N = len(input_str) m = [np.inf] * (N + 1) # 長度為 N+1 長度的陣列。初始化無窮大。 m[0] = 0 # 路徑值,第0個節點的值為0,後面計算節點1路徑權值時候,需要加上節點0的值。 last_index = [0] * (N + 1)# 儲存一路轉移的索引 for idx_end in range(1, N + 1): # 兩層for迴圈 idx_end 為 incoming_links,字典的鍵 for idx_start in graph[idx_end]: # idx_start 到 idx 組成了一個單詞。input_str[idx_start:idx_end] 在字典裡存在。 # 從字典找到這個單詞的概率。 # m[idx_start] 儲存了到這個單詞為止,最短路徑值。 log_prob = round(-1 * np.log(dic_words[input_str[idx_start:idx_end]])) + m[idx_start] if log_prob < m[idx_end]:# 這次迴圈裡,判斷到idx_end 位置的最短路徑值。 m[idx_end] = log_prob last_index[idx_end] = idx_start best_segment = [] i = N while True: best_segment.insert(0, input_str[last_index[i]:i]) i = last_index[i] if i == 0: break return best_segment