python词性标注_Python词性标注HMM+viterbi实现

本文介绍了Python中使用隐马尔科夫模型(HMM)进行词性标注的任务,详细讲解了HMM的理论背景,包括概率图模型、贝叶斯网络和马尔科夫模型。接着,通过代码展示了如何训练HMM参数、实现维特比算法进行隐序列解码。最后,给出了一个完整的词性标注例子。
摘要由CSDN通过智能技术生成

大纲:1. 任务描述

2.一些理论

3. 隐马尔科夫模型

4. HMM应用之——隐序列解码(词性标注)

5. 附录

6. 完整代码

1. 任务描述

标注英文句子中每个单词的词性,属于HMM任务中的预测过问题(已知参数和x,求z)。

场景:给定一个英文句子X=’I like …’其中每个单词由

equation?tex=x 表示,

equation?tex=z 表示那个单词对应的词性。

目标:给定一个句子

equation?tex=X%3Dx_1%2Cx_2%2Cx_3%2Cx_4%2Cx_5++ .可以得出每个单词对应的词性

2.一些理论

(1)概率图模型

利用图来表示与模型有关的变量的联合概率分布

(2) 贝叶斯网络

将随机变量作为结点,若两个随机变量相关或者不独立,则将二者连接一条边;若给定若干随机变量,则形成一个有向图,即构成一个网络。

如果该网络是有向无环图,则这个网络称为贝叶斯网络。

(3)马尔科夫模型

若贝叶斯网络退化成线性链,则得到马尔科夫模型。

已知 n个有序随机变量,根据贝叶斯定理,其联合分布可写成条件分布的连乘。

equation?tex=p%28x_1%2Cx_2%2C...%2Cx_n%29%3D%5Cprod_%7Bi%3D1%7D%5E%7Bn%7D+p%28x_i%7Cx_%7Bi-1%7D%2C...%2Cx_1%29%5C%5C

马尔科夫模型是指,假设序列中的任一随机变量只与它的前 1个变量有关,与更早的变量条件独立:

equation?tex=p%28x_i%7Cx_%7Bi-1%7D%2C...%2Cx_1%29%3Dp%28x_i%7Cx_i-1%29%5C%5C

在此假设下,其联合分布可简化为:

equation?tex=p%28x_1%2Cx_2%2C...%2Cx_n%29%3Dp%28x_1%29+%5Cprod_%7Bi%3D2%7D%5E%7Bn%7D+p%28x_i%7Cx_%7Bi-1%7D%29%5C%5C

为了表达当前变量与更早的变量之间的关系,可引入高阶马尔科夫性,指当前随机变量与它的前 m 个变量有关:

equation?tex=p%28x_1%2Cx_2%2C...%2Cx_n%29%3D%5Cprod_%7Bi%3D1%7D%5E%7Bn%7D+p%28x_i%7Cx_%7Bi-1%7D%2C...%2Cx_%7Bi-m%7D%29%5C%5C

3. 隐马尔科夫模型

对于离散型随机变量,隐状态:

equation?tex=Z%3Dz_1%2Cz_2%2C...%2Cz_n

可观测变量:

equation?tex=X%3Dx_1%2Cx_2%2C...%2Cx_n

X 已知,求解最大概率的 Z,即是最大化

equation?tex=+p%28Z%7CX%29

equation?tex=p%28Z%7CX%29%3Dp%28X%7CZ%29p%28Z%29%3Dp%28x_1%2Cx_2%2C...%2Cx_n%7Cz_1%2Cz_2%2C...%2Cz_n%29+p%28z_1%2Cz_2%2C...%2Cz_n%29%5C%5C

根据马尔科夫假设,任一随机变量只与它的前 1个变量有关

equation?tex=p%28x_1%2Cx_2%2C...%2Cx_n%7Cz_1%2Cz_2%2C...%2Cz_n%29%3D%5Cprod_%7Bi%3D1%7D%5E%7Bn%7D+p%28x_i%7Cz_i%29+%5C%5C

equation?tex=p%28z_1%2Cz_2%2C...%2Cz_n%29%3D+p%28z_1%29+%5Cprod_%7Bi%3D2%7D%5E%7Bn%7D+p%28z_i%7Cz_%7Bi-1%7D%29%5C%5C

因此:

equation?tex=p%28Z%7CX%29%3D%5Cprod_%7Bi%3D1%7D%5E%7Bn%7D+p%28x_i%7Cz_i%29+p%28z_1%29+%5Cprod_%7Bi%3D2%7D%5E%7Bn%7D+p%28z_i%7Cz_%7Bi-1%7D%29%5C%5C

模型简化为3个部分:

发射概率矩阵:

equation?tex=B%3D%5Cprod_%7Bi%3D1%7D%5E%7Bn%7D+p%28x_i%7Cz_i%29

初始概率:

equation?tex=p%28z_1%29

转移概率矩阵:

equation?tex=+A%3D%5Cprod_%7Bi%3D2%7D%5E%7Bn%7D+p%28z_i%7Cz_%7Bi-1%7D%29

因此首先需计算HMM模型参数

equation?tex=%5Cpi%2CA%2CB ,再利用已知的x加上参数,求y(HMM的预测问题)

equation?tex=dp%5Bi%5D%5Bj%5D%3D%5Cmax%5Cleft%5C%7B%5Cbegin%7Bmatrix%7D++dp%5B0%5D%5Bi-1%5D%2B%5Clog+p%28adv%7Cadj%29%2B+%5Clog+p%28x_i%7Cadv%29%5C%5C++dp%5B1%5D%5Bi-1%5D%2B%5Clog+p%28adv%7Cadv%29%2B+%5Clog+p%28x_i%7Cadv%29%5C%5C++dp%5B2%5D%5Bi-1%5D%2B%5Clog+p%28adv%7Cend%29%2B+%5Clog+p%28x_i%7Cadv%29%5C%5C++dp%5B3%5D%5Bi-1%5D%2B%5Clog+p%28adv%7Csubj%29%2B+%5Clog+p%28x_i%7Cadv%29++%5Cend%7Bmatrix%7D%5Cright.%5C%5C+

简化:

对应到代码中:

# 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参数训练

equation?tex=A%2CB%2C%5Cpi

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)频数 --> 概率对数

取对数,防止下溢,乘法运算转换成更简单的加法运算

equation?tex=%5Csum_%7Bi%3D1%7D%5E%7Bn%7D%7B%5Cln+p%28x_i%7Cz_i%29%7D%2B%5Cln+p%28z_1%29+%2B+%5Csum_%7Bi%3D2%7D%5E%7Bn%7D%7B%5Cln+p%28z_i%7Cz_%7Bi-1%7D%29%7D%5C%5C

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)

初始概率

equation?tex=%5Cpi

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)

补充:马尔科夫网络、马尔科夫模型、马尔科夫过程、贝叶斯网络的区别将随机变量作为结点,若两个随机变量相关或者不独立,则将二者连接一条边;若给定若干随机变量,则形成一个有向图,即构成一个网络。

如果该网络是有向无环图,则这个网络称为贝叶斯网络。

如果这个图退化成线性链的方式,则得到马尔科夫模型;因为每个结点都是随机变量,将其看成各个时刻(或空间)的相关变化,以随机过程的视角,则可以看成是马尔科夫过程。

若上述网络是无向的,则是无向图模型,又称马尔科夫随机场。

如果在给定某些条件的前提下,研究这个马尔科夫随机场,则得到条件随机场。

如果使用条件随机场解决标注问题,并且进一步将条件随机场中的网络拓扑变成线性的,则得到线性链条件随机场

总结:

先利用训练集训练模型参数

equation?tex=A%2CB%2C%5Cpi ,此时已知x和模型参数,利用维特比算法,求z

参考:Python词性标注HMM+viterbi实现_ArYe-CSDN博客_hmm_viterbi python3​blog.csdn.net025b4e5ff10b71f9aebb50e3896a4201.png

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值