上一次实验我们介绍了隐马尔可夫模型的基本原理,以及根据具体案例来分析其应用,详细介绍了隐马尔科夫模型所包含的三个重要概率分布矩阵,初始化概率矩阵,状态转移概率矩阵,发射概率矩阵。以及其如何计算的方法。我们在上次实验的代码里,将训练好的三个矩阵用python的pumkle的模块进行了存储,这次实验我们着重介绍,隐马尔可夫模型在分词中的应用。
二、用隐马尔可夫模型解决问题内容:已知语料库中有且仅有如下3个句子:
商品 和 服务
商品 和服 物美价廉
服务 和 货币
应用隐马尔可夫模型和维特比算法给出‘商品和货币’这句话的分词结果。
我们在此处应用在上次博客中提到的方法,根据输入隐马尔科夫模型的分好词的句子。
首先我们运行上次博客介绍的代码。分别输入“商品 和 服务”,“商品 和服 物美价廉”,“服务 和 货币”。
该代码主要思路如下:
- 对词性标注。
- 然后计算列标准初始状态概率向量,状态转移概率矩阵,发射概率矩阵。
- 保存结果文件。
然后读者可复制以下的代码,然后再该代码的同目录下新建文件夹,命名为mat_pickle,然后运行该代码,该代码可以自动读取上个代码保留的文件,然后读取存储的模型,进而对新的句子进行分词。
该代码思路如下:
- 读取上次代码的模型。
- 根据状态概率矩阵,应用维特比算法,对问题求出最优解。
- 对求出的最大值,反向回溯,求得最优的序列。
- 对求出的序列,进行解码,从而实现对句子的分词。
维特比算法是一种采用了动态规划的算法,他在隐马尔可夫模型中的应用十分广泛,它的大致思想就是对每一个状态记录当前状态和之后状态的所有情况,选取最优解。它是一种对于枚举方法的改进(即计算出所有情况,然后选取最优解),在问题规模很大时维特比算法相比于暴力枚举将展现其强大的优势。
四、python代码实现代码的维特比算法借鉴了该代码,原文链接:https://blog.csdn.net/youfefi/article/details/74276546
import numpy as np
import pickle
def solve_tag(answer,sentence):
result = []
hidden_state = ["B", "M", "E", "S"]
for i in range(len(answer)):
if(hidden_state[int(answer[i])]=='E' or hidden_state[int(answer[i])]=='S'):
result.append(sentence[i])
result.append(" ")
else:
result.append(sentence[i])
return result
def compute(obs, states, start_p, trans_p, emit_p):
# max_p(3*2)每一列存储第一列不同隐状态的最大概率
max_p = np.zeros((len(obs), len(states)))
#print("max_p",max_p)
# path(2*3)每一行存储上max_p对应列的路径
path = np.zeros((len(states), len(obs)))
#print("path",path)
# 初始化
for i in range(len(states)):
max_p[0][i] = start_p[i] * emit_p[i][obs[0]]
path[i][0] = i
#print("max_p", max_p)
#print("path", path)
for t in range(1, len(obs)):
newpath = np.zeros((len(states), len(obs)))
for y in range(len(states)):
prob = -1
for y0 in range(len(states)):
nprob = max_p[t-1][y0] * trans_p[y0][y] * emit_p[y][obs[t]]
if nprob > prob:
prob = nprob
state = y0
# 记录路径
max_p[t][y] = prob
for m in range(t):
newpath[y][m] = path[state][m]
newpath[y][t] = y
path = newpath
#print("path",path)
max_prob = -1
path_state = 0
# 返回最大概率的路径
for y in range(len(states)):
if max_p[len(obs)-1][y] > max_prob:
max_prob = max_p[len(obs)-1][y]
path_state = y
#print("path_state",path_state)
return path[path_state]
if __name__ == "__main__":
BMES = []
#emit_mat = pd.Dataframe(index=['B','M','E','S'])
#new_sentence = input("请输入你要分词的句子:")
#new_sentence = "商品和货币"
print("正在读取本地模型矩阵...")
with open(r'mat_pickle/init_mat.pkl', "rb") as f0:
init_mat = np.array(list(pickle.load(f0).values()))
with open(r'mat_pickle/trans_mat.pkl', "rb") as f1:
init_trans_mat = np.array(list(pickle.load(f1).values()))
with open(r'mat_pickle/emit_mat.pkl', "rb") as f2:
init_emit_mat = pickle.load(f2)
catalog = list(init_emit_mat)
trans_mat = []
emit_mat = []
hidden_state = ["B", "M", "E", "S"]
for item in init_trans_mat:
trans_mat.append(np.array(list(item.values())))
for i in hidden_state:
emit_mat.append(np.array(list(init_emit_mat.loc[i])))
emit_mat = np.array(emit_mat).reshape(4,-1)
#print(init_mat)
#print(trans_mat)
#print(emit_mat)
print("读取模型矩阵成功!")
print("目前模型的汉字库",catalog)
while(1):
new_sentence = input("请输入你要分词的句子(如:商品和货币)输入0结束分词功能:")
if (new_sentence == '0'):
print("输入结束!")
break
state_s = [0,1,2,3]
original = [catalog.index(i) for i in new_sentence]
#print(original)
#print(hidden_state)
result = compute(original, state_s, init_mat, trans_mat, emit_mat)
#print(result)
answer = solve_tag(result,new_sentence)
#print(answer)
print("分词的结果为:")
for item in answer:
print(item,end='')
print("n")



