大纲:1. 任务描述
2.一些理论
3. 隐马尔科夫模型
4. HMM应用之——隐序列解码(词性标注)
5. 附录
6. 完整代码
1. 任务描述
标注英文句子中每个单词的词性,属于HMM任务中的预测过问题(已知参数和x,求z)。
场景:给定一个英文句子X=’I like …’其中每个单词由
表示,
表示那个单词对应的词性。
目标:给定一个句子
.可以得出每个单词对应的词性
2.一些理论
(1)概率图模型
利用图来表示与模型有关的变量的联合概率分布
(2) 贝叶斯网络
将随机变量作为结点,若两个随机变量相关或者不独立,则将二者连接一条边;若给定若干随机变量,则形成一个有向图,即构成一个网络。
如果该网络是有向无环图,则这个网络称为贝叶斯网络。
(3)马尔科夫模型
若贝叶斯网络退化成线性链,则得到马尔科夫模型。
已知 n个有序随机变量,根据贝叶斯定理,其联合分布可写成条件分布的连乘。
马尔科夫模型是指,假设序列中的任一随机变量只与它的前 1个变量有关,与更早的变量条件独立:
在此假设下,其联合分布可简化为:
为了表达当前变量与更早的变量之间的关系,可引入高阶马尔科夫性,指当前随机变量与它的前 m 个变量有关:
3. 隐马尔科夫模型
对于离散型随机变量,隐状态:
可观测变量:
X 已知,求解最大概率的 Z,即是最大化
根据马尔科夫假设,任一随机变量只与它的前 1个变量有关
因此:
模型简化为3个部分:
发射概率矩阵:
初始概率:
转移概率矩阵:
因此首先需计算HMM模型参数
,再利用已知的x加上参数,求y(HMM的预测问题)
简化:
对应到代码中:
# trans_p是转移概率矩阵A emit_p是发射概率矩阵B
dp[i,j]=max(dp[i -1][k]+ trans_p[k][j]+ emit_p[j][obs[i]] for k in range(T))
dp[i][j], path[i][j] = max(
(dp[i - 1][k] + trans_p[k][j] + emit_p[j][obs[i]], k)
for k in range(T))
----------------------------
不懂就看下面简单版
dp=[4,5,6]
T=3
a=max(dp[k] for k in range(T))
print(a) #6
dp=[4,5,6]
T=3
a,b=max((dp[k],k) for k in range(T))
print(a,b) #6 2
----------------------
4. HMM应用之——隐序列解码(词性标注)
import numpy as np, pandas as pd
(1)数据预处理
START = 'start' # 句始tag
END = 'end' # 句末tag
NOUN = 'subj' # 名词
ADV = 'adv' # 副词
ADJ = 'adj' # 形容词
corpus = np.array([
('我', NOUN), ('很', ADV), ('菜', ADJ), ('。', END),
('我', NOUN), ('好', ADV), ('菜', ADJ), ('。', END),
('我', NOUN), ('很', ADV), ('好', ADJ), ('。', END),
('他', NOUN), ('很', ADV), ('菜', ADJ), ('。', END),
('他', NOUN), ('好', ADV), ('菜', ADJ), ('。', END),
('他', NOUN), ('很', ADV), ('好', ADJ), ('。', END),
('菜', NOUN), ('很', ADV), ('好', ADJ), ('。', END),
('我', NOUN), ('菜', ADJ), ('。', END),
('我', NOUN), ('好', ADJ), ('。', END),
('他', NOUN), ('菜', ADJ), ('。', END),
('他', NOUN), ('好', ADJ), ('。', END),
('菜', NOUN), ('好', ADJ), ('。', END),
('我', NOUN), ('好', ADV), ('好', ADJ), ('。', END),
('他', NOUN), ('好', ADV), ('好', ADJ), ('。', END),
], dtype=str)
words = sorted(set(corpus[:, 0])) # 单词
tags = sorted(set(corpus[:, 1])) # 词性
W = len(words) # 词汇量
T = len(tags) # 词性数
word2id = {words[i]: i for i in range(W)} # 单词-id词典
tag2id = {tags[i]: i for i in range(T)} # 词性-id词典
id2tag = {i: tags[i] for i in range(T)} # id-词性词典
(2)HMM参数训练
SMOOTHNESS = 1e-8 # 平滑参数
emit_p = np.zeros((T, W)) + SMOOTHNESS # 发射概率 B
start_p = np.zeros(T) + SMOOTHNESS # π
trans_p = np.zeros((T, T)) + SMOOTHNESS # 转移概率 A
prev_tag = START # 前一个tag
for word, tag in corpus: # 每一个单词 每一个词性
wid, tid = word2id[word], tag2id[tag] # 单词转id,词性转id
emit_p[tid][wid] += 1 # # 无论如何都要更新B 词性到单词
if prev_tag == START: # 句首
start_p[tid] += 1 # 更新π
else: # 不是句首,更新A 词性到词性
trans_p[tag2id[prev_tag]][tid] += 1
# 如果句子结束了 prev_tag = START 否则prev_tag=tag
prev_tag = START if tag == END else tag
(3)频数 --> 概率对数
取对数,防止下溢,乘法运算转换成更简单的加法运算
start_p = np.log(start_p / sum(start_p))
for i in range(T):
emit_p[i] = np.log(emit_p[i] / sum(emit_p[i]))
trans_p[i] = np.log(trans_p[i] / sum(trans_p[i]))
发射矩阵B
pd.DataFrame(emit_p, tags, words)
初始概率
pd.DataFrame(start_p.reshape(1, T),['START'], tags)
状态转移矩阵A
pd.DataFrame(trans_p, tags, tags)
(4)维特比算法
sentence =list('菜好好。')
obs = [word2id[w] for w in sentence] # 观测序列
le = len(obs) # 序列长度 4
# 动态规划矩阵
dp = np.array([[-1e99] * T] * le) # 节点最大概率
path = np.zeros((le, T), dtype=int) # 节点转移记录
for j in range(T):
dp[0][j] = start_p[j] + emit_p[j][obs[0]]
path[0][j] = -1
for i in range(1, le):
for j in range(T):
dp[i][j], path[i][j] = max(
(dp[i - 1][k] + trans_p[k][j] + emit_p[j][obs[i]], k)
for k in range(T))
节点最大概率矩阵
pd.DataFrame(dp.T, tags, sentence)
节点转移记录矩阵
pd.DataFrame(path.T, tags, sentence)
隐态序列标号
states = [np.argmax(dp[le - 1])] # [2]
# 从后到前的循环来依次求出每个单词的词性
for i in range(le - 2, -1, -1):
states.insert(0, path[i + 1][states[0]]) # path[3][3] path[2][0] path[1][1]
注意states会动态变化
(5)标注结果打印
for word, tid inzip(sentence, states):
print(word, id2tag[tid])
菜 subj
好 adv
好 adj
。 end
5. 附录
语料地址:
GitHub地址:
6. 完整代码:
import numpy as np
"""配置"""
SMOOTHNESS = 1e-8
START = 'start' # 句始tag
END = 'end' # 句末tag
NOUN = 'subj' # 名词
ADV = 'adv' # 副词
ADJ = 'adj' # 形容词
"""数据预处理"""
corpus = np.array([
('我', NOUN), ('很', ADV), ('菜', ADJ), ('。', END),
('我', NOUN), ('好', ADV), ('菜', ADJ), ('。', END),
('我', NOUN), ('很', ADV), ('好', ADJ), ('。', END),
('他', NOUN), ('很', ADV), ('菜', ADJ), ('。', END),
('他', NOUN), ('好', ADV), ('菜', ADJ), ('。', END),
('他', NOUN), ('很', ADV), ('好', ADJ), ('。', END),
('菜', NOUN), ('很', ADV), ('好', ADJ), ('。', END),
('我', NOUN), ('菜', ADJ), ('。', END),
('我', NOUN), ('好', ADJ), ('。', END),
('他', NOUN), ('菜', ADJ), ('。', END),
('他', NOUN), ('好', ADJ), ('。', END),
('菜', NOUN), ('好', ADJ), ('。', END),
('我', NOUN), ('好', ADV), ('好', ADJ), ('。', END),
('他', NOUN), ('好', ADV), ('好', ADJ), ('。', END),
], dtype=str)
words = sorted(set(corpus[:, 0]))
tags = sorted(set(corpus[:, 1]))
W = len(words) # 词汇量
T = len(tags) # 词性种类数
word2id = {words[i]: i for i in range(W)}
tag2id = {tags[i]: i for i in range(T)}
id2tag = {i: tags[i] for i in range(T)}
"""HMM训练"""
emit_p = np.zeros((T, W)) + SMOOTHNESS # emission_probability
start_p = np.zeros(T) + SMOOTHNESS # start_probability
trans_p = np.zeros((T, T)) + SMOOTHNESS # transition_probability
prev_tag = START # 前一个tag
for word, tag in corpus:
wid, tid = word2id[word], tag2id[tag]
emit_p[tid][wid] += 1
if prev_tag == START:
start_p[tid] += 1
else:
trans_p[tag2id[prev_tag]][tid] += 1
prev_tag = START if tag == END else tag # 句尾判断
# 频数 --> 概率对数
start_p = np.log(start_p / sum(start_p))
for i in range(T):
emit_p[i] = np.log(emit_p[i] / sum(emit_p[i]))
trans_p[i] = np.log(trans_p[i] / sum(trans_p[i]))
def viterbi(sentence):
"""维特比算法"""
obs = [word2id[w] for w in sentence.strip()] # 观测序列
le = len(obs) # 序列长度
# 动态规划矩阵
dp = np.zeros((le, T)) # 记录节点最大概率对数
path = np.zeros((le, T), dtype=int) # 记录上个转移节点
for j in range(T):
dp[0][j] = start_p[j] + emit_p[j][obs[0]]
for i in range(1, le):
for j in range(T):
dp[i][j], path[i][j] = max(
(dp[i - 1][k] + trans_p[k][j] + emit_p[j][obs[i]], k)
for k in range(T))
# 隐序列
states = [np.argmax(dp[le - 1])]
# 从后到前的循环来依次求出每个单词的词性
for i in range(le - 2, -1, -1):
states.insert(0, path[i + 1][states[0]])
# 打印
for word, tid in zip(sentence, states):
print(word, id2tag[tid])
"""测试"""
x = '菜好好。'
viterbi(x)
补充:马尔科夫网络、马尔科夫模型、马尔科夫过程、贝叶斯网络的区别将随机变量作为结点,若两个随机变量相关或者不独立,则将二者连接一条边;若给定若干随机变量,则形成一个有向图,即构成一个网络。
如果该网络是有向无环图,则这个网络称为贝叶斯网络。
如果这个图退化成线性链的方式,则得到马尔科夫模型;因为每个结点都是随机变量,将其看成各个时刻(或空间)的相关变化,以随机过程的视角,则可以看成是马尔科夫过程。
若上述网络是无向的,则是无向图模型,又称马尔科夫随机场。
如果在给定某些条件的前提下,研究这个马尔科夫随机场,则得到条件随机场。
如果使用条件随机场解决标注问题,并且进一步将条件随机场中的网络拓扑变成线性的,则得到线性链条件随机场
总结:
先利用训练集训练模型参数
,此时已知x和模型参数,利用维特比算法,求z
参考:Python词性标注HMM+viterbi实现_ArYe-CSDN博客_hmm_viterbi python3blog.csdn.net