【写在前边】
本人NLP小白,正在看序列标注相关问题,简单记录下学习的知识和博文,虽然大都是参考的别人的,但是字是我打的··哈哈哈
保护知识产权尊重参考博主:
参考链接:
HMM中的各种算法讲解
HMM词性标注参考原文
李航《统计学习方法》
speech and Language processing
HMM 生成式模型,利用联合概率建模,估算隐藏于观测序列背后的隐序列。
POS:单词:观测序列,词性:状态序列(隐序列)
HMM建模公式:
P ( O ) = ∑ Q P ( O , Q ) = ∑ Q P ( O ∣ Q ) P ( Q ) P(O) = \sum_{Q} P(O, Q) \\ = \sum_{Q} P(O| Q) P(Q) \\ P(O)=Q∑P(O,Q)=Q∑P(O∣Q)P(Q)
一阶马尔科夫假设:
前半部分需要计算隐序列的各种情况也就是发射概率矩阵,后半部分需要转移概率矩阵得到。
P ( O ∣ Q ) P ( Q ) = ∏ i = 1 n P ( o i ∣ q i ) × ∏ i = 1 n P ( q i ∣ q i − 1 ) P(O| Q) P(Q) = \prod_{i=1}^{n} P(o_{i}|q_{i}) \times \prod _{i=1} ^{n}P(q_{i} | q_{i-1}) P(O∣Q)P(Q)=i=1∏nP(oi∣qi)×i=1∏nP(qi∣qi−1)
HMM模型
λ = ( A , B , π ) \lambda = (A, B, \pi) λ=(A,B,π),也就是HMM中的A:转移概率矩阵,B:状态概率矩阵, π \pi π初始状态概率向量
HMM三个基本问题(图为李航老师《统计学习方法》)
用极大似然概率实现学习问题,也就是得到初始概率、转移矩阵和发射矩阵。
# 定义pi为字典,pi [p] = p的初始概率
pi = defaultdict(int)
# 学习初始概率,句子开头词性的频率
for sentence in train_data:
pi [sentence [0] [1] ] += 1
# 定义transition为字典。transition = { 词性p1: { 转移到的词性: 对应概率}}
transition = {}
for p in pos: # pos为所有的词性
transition[p] = defaultdict(int)
# 学习transition
states_transition = [(p1[1],p2[1]) for p1,p2 in zip(sent,sent[1:])]
#学习转移概率
for p1,p2 in states_transition:
transition [p1][p2] += 1
#定义 同transition。emission = {词性P:{word : 对应概率}}
emission = {}
for p in pos:
emission [ p] = defaultdict(int)
# 学习emission
for word,pos in sentence:
emission [pos][word] += 1
至此,HMM三个参数的学习问题结束。
看到了关于这个概率的优化,比如进行简单的平滑或者取对数,稍后会在写一篇学习博文。虽然都是参考的大佬的,但是!大佬写出来不就是为了让小白看的吗!!dei不dei!
在HMM和CRF中都会用到Viterbi解码,就是给定HMM模型或者CRF模型以及观测序列(词),找到最可能的状态序列(词性),也称为最优路径。
假设状态序列长度为N,观测序列长度为T,那么每个观测元素的状态都有N种可能,也就是 N T N^{T} NT,时间复杂度太高。
Viterbi算法是用动态规划的思想去计算HMM或者CRF中的最优路径问题。
假设HMM 在 t时刻的状态为 i,Viterbi变量定义为前 t-1时刻是使得t时刻状态为i概率最大的路径。
也就是
v ( i ) = m a x q 1 , q 2 . . . q N P ( q 1 , q 2 . . . q t − 1 , q t = i , o 1 , o 2 , . . . o t ∣ λ ) v(i) = max_{q_{1},q_{2}...q_{N}} P(q_{1},q_{2}...q_{t-1}, q_{t} = i, o_{1}, o_{2}, ...o_{t} \mid \lambda) v(i)=maxq1,q2...qNP(q1,q2...qt−1,qt=i,o1,o2,...ot∣λ)
输入:HMM模型 λ = ( A , B , π ) \lambda = (A, B, \pi) λ=(A,B,π), 观测序列 O = ( o 1 , o 2 , . . o t ) O = (o_{1}, o_{2},..o_{t}) O=(o1,o2,..ot)
输出:使得t时刻状态为i的最优路径 Q = ( q 1 , q 2 . . q t ) Q = (q_{1}, q_{2}..q_{t}) Q=(q1,q2..qt)
v ( i ) = m a x q 1 , q 2 . . . q N P ( q 1 , q 2 . . . q t − 1 , q t = i , o 1 , o 2 , . . . o t ∣ λ ) v(i) = max_{q_{1},q_{2}...q_{N}} P(q_{1},q_{2}...q_{t-1}, q_{t} = i, o_{1}, o_{2}, ...o_{t} \mid \lambda) v(i)=maxq1,q2...qNP(q1,q2...qt−1,qt=i,o1,o2,...ot∣λ)
#viterbi_matrix[t][i]表示时间t时最后一个状态为i的维特比变量
#viterbi_matrix = {t: {i: t时刻为i的概率}}
#等于前一个所有维特比变量乘以转移概率再乘以发射概率中的最大值
viterbi_matrix = defaultdict(dict)
#定义一个矩阵记录当t时刻为最后一个状态为i概率最大时,是由哪一个状态转移过来的
#backpointer_matrix[t][i]表示t时刻最后一个状态为i概率最大时的上一个状态。
#backpointer_amtrix = {t:{i: 转移到i的状态}}
backpointer_matrix = defaultdict(dict)
#初始化两个矩阵
#init Viterbi
for s in state:
# 如果句子中首词在emission矩阵中,那么就是初始概率乘以发射矩阵
if obs[0] in emission:
viterbi_matrix [0][s] = pi[s] * emission[s][obs[0]]
# 如果没有这个发射概率,就设置为0
else:
viterbi_matrix [0][s] = 0
#句首记号
backpointers_matrix[0][s] = ''
#计算对于t时刻i状态的维特比变量,以及记录它是由哪一个状态转移而来,states就是所有的状态
def get_max_arg(t,i):
max_prob, argmax_pre_state = 0,0
for j in states:
p = viterbi_matrix[t-1][j]*transition[j][i]*emission[i][obs[t]]
if p > max_prob:
max_prob = p
argmax_pre_state = j
return max_prob,argmax_pre_state
递推求得viterbi矩阵,每一步递推共N*N的计算量(N个状态转移到N个状态), 共O(T N^2)
for t in range(1,T):
for i in states:
max_prob,argmax_pre_state = get_max_arg(t,i)
viterbi_matrix[t][i] = max_prob
backpointers_matrix[t][i] = argmax_pre_state
max_prob_final_state, max_prob = None, 'UNK'
for j in states:
#可能出现这个条件永不满足的情况,即最后时刻所有状态的最大可能概率都是0
if viterbi_matrix[T-1][j] > max_prob:
max_prob_final_state = j
max_prob = viterbi_matrix[T-1][j]
回溯最优路径
best_path = [max_prob_final_state ]
for t in range(T-1, 0, -1):
try:
#backpointers_matrix[t][best_path[-1]] 表示t时刻最后一个状态最佳状态时(best_path[-1])概率最大时,是由哪一个状态转移而来。
previous_state = backpointers_matrix[t][best_path[-1]]
best_path.append(previous_state)
except:
best_path.append(None)
For Example:
path = viterbi(pi, transition, emisson, sentence)
#就得到了最优标注路径
《统计学习方法》中的例子
已知转移矩阵A,状态矩阵B,初始概率矩阵pi。观测序列为O={红,白,红},求解最优状态序列,也就是最优路径。