NLP作业01:请利用HMM实现词性标注

目录

一、作业头

二、HMM模型介绍

 三、估计HMM的模型参数

 四、基于维特比算法进行解码

5、词性标注结果


一、作业头
 

这个作业属于那个课程自然语言处理
 这个作业要求在哪里http://t.csdn.cn/f0hUe
我在这个课程的目标是掌握分词以及标注的基本方法并且能熟练运用
这个作业在那个具体方面帮助我实现目标了解HMM模型和词性标注的原理
参考文献https://blog.csdn.net/slx_share/article/details/80237566
https://blog.csdn.net/m0_47150066/article/details/106116003
http://t.csdn.cn/f0hUe

二、HMM模型介绍

 隐马尔可夫模型(HMM)是统计模型,它用来描述一个含有隐含未知参数的马尔可夫过程,可以用五个元素来描述:
(1) 隐含状态 S:这些状态之间满足马尔可夫性质,是马尔可夫模型中实际所隐含的状态。这些状态通常无法通过直接观测而得到。
(2) 可观测状态 O:在模型中与隐含状态相关联,可通过直接观测而得到。
(3)初始状态概率矩阵 π:表示隐含状态在初始时刻t=1的概率矩阵,
(4) 隐含状态转移概率矩阵 A:描述了HMM模型中各个状态之间的转移概率。
(5)观测状态转移概率矩阵 B:令N代表隐含状态数目,M代表可观测状态数目。

 三、估计HMM的模型参数

HMM描述先由隐藏的马尔可夫链生成状态序列,各个状态序列生成一个观测,组合成最终的观测序列。故整个模型包含三个要素:
1.初始状态概率向量:生成第一个状态的概率
2.状态转移概率矩阵:当前状态转移到下一个状态的概率矩阵
3.观测生成概率矩阵:每个状态生成各个观测的概率

import numpy as np
from itertools import accumulate
class GenData:
    """
    根据隐马尔科夫模型生成相应的观测数据
    """
    def __init__(self, hmm, n_sample):
        self.hmm = hmm
        self.n_sample = n_sample

    def _locate(self, prob_arr):
        # 给定概率向量,返回状态
        seed = np.random.rand(1)
        for state, cdf in enumerate(accumulate(prob_arr)):
            if seed <= cdf:
                return state
        return

    def init_state(self):
        # 根据初始状态概率向量,生成初始状态
        return self._locate(self.hmm.S)

    def state_trans(self, current_state):
        # 转移状态
        return self._locate(self.hmm.A[current_state])

    def gen_obs(self, current_state):
        # 生成观测
        return self._locate(self.hmm.B[current_state])

    def gen_data(self):
        # 根据模型产生观测数据
        current_state = self.init_state()
        start_obs = self.gen_obs(current_state)
        state = [current_state]
        obs = [start_obs]
        n = 0
        while n < self.n_sample - 1:
            n += 1
            current_state = self.state_trans(current_state)
            state.append(current_state)
            obs.append(self.gen_obs(current_state))
        return state, obs

class HMM:
    def __init__(self, n_state, n_obs, S=None, A=None, B=None):
        self.n_state = n_state  # 状态的个数n
        self.n_obs = n_obs  # 观测的种类数m
        self.S = S  # 1*n, 初始状态概率向量
        self.A = A  # n*n, 状态转移概率矩阵
        self.B = B  # n*m, 观测生成概率矩阵

def _alpha(hmm, obs, t):
    # 计算时刻t各个状态的前向概率
    b = hmm.B[:, obs[0]]
    alpha = np.array([hmm.S * b])  # n*1
    for i in range(1, t + 1):
        alpha = (alpha @ hmm.A) * np.array([hmm.B[:, obs[i]]])
    return alpha[0]

def forward_prob(hmm, obs):
    # 前向算法计算最终生成观测序列的概率, 即各个状态下概率之和
    alpha = _alpha(hmm, obs, len(obs) - 1)
    return np.sum(alpha)

def _beta(hmm, obs, t):
    # 计算时刻t各个状态的后向概率
    beta = np.ones(hmm.n_state)
    for i in reversed(range(t + 1, len(obs))):
        beta = np.sum(hmm.A * hmm.B[:, obs[i]] * beta, axis=1)
    return beta

def backward_prob(hmm, obs):
    # 后向算法计算生成观测序列的概率
    beta = _beta(hmm, obs, 0)
    return np.sum(hmm.S * hmm.B[:, obs[0]] * beta)

def fb_prob(hmm, obs, t=None):
    # 将前向和后向合并
    if t is None:
        t = 0
    res = _alpha(hmm, obs, t) * _beta(hmm, obs, t)
    return res.sum()

def _gamma(hmm, obs, t):
    # 计算时刻t处于各个状态的概率
    alpha = _alpha(hmm, obs, t)
    beta = _beta(hmm, obs, t)
    prob = alpha * beta
    return prob / prob.sum()

def point_prob(hmm, obs, t, i):
    # 计算时刻t处于状态i的概率
    prob = _gamma(hmm, obs, t)
    return prob[i]

def _xi(hmm, obs, t):
    alpha = np.mat(_alpha(hmm, obs, t))
    beta_p = _beta(hmm, obs, t + 1)
    obs_prob = hmm.B[:, obs[t + 1]]
    obs_beta = np.mat(obs_prob * beta_p)
    alpha_obs_beta = np.asarray(alpha.T * obs_beta)
    xi = alpha_obs_beta * hmm.A
    return xi / xi.sum()

 四、基于维特比算法进行解码

def Viterbi_decode(A, B, Pi, O):
    """维特比解码,所有概率为对数概率。
    输入:
        A: N×N 的转移矩阵
        B: N×M 的输出矩阵
        Pi: list, 初始状态概率分布
        O: list, 观测序列
    返回:
        max_path: list, 最优路径。
    """
    T = O.shape[0]
    N = A.shape[0]   # 状态数
    p_nodes = Pi + B[:, O[0]]     # 记录每个节点的路径概率
    path_nodes = list()           # 记录每个节点的路径
    # 计初始化路径
    for node in xrange(N):
        path_nodes.append([node])
    # T 个时刻
    for step in xrange(1, T):
        for this_node in xrange(N):   # 计算每个节点的新概率
            p_news = list()
            for last_node in xrange(N):
                p_trans = A[last_node, this_node]  # 转移概率
                p_out = B[this_node, O[step]]       # 输出概率
                p_new = p_nodes[last_node] + p_trans + p_out
                p_news.append(p_new)
            p_nodes[this_node] = np.max(p_news)    # 更新节点路径概率
            last_index = np.argmax(p_news)         # 更新节点路径
            temp = path_nodes[last_index][:]
            temp.append(this_node)
            path_nodes[this_node] = temp
    max_index = np.argmax(p_nodes)
    max_path = path_nodes[max_index]        
    return max_path

max_path = Viterbi_decode(A, B, Pi, O)
print max_path

五、词性标注结果

test="那个球状闪电呈橘红色,拖着一条不太长的尾迹,在夜空中沿一条变换的曲线飘行着。"
words=list(jieba.cut(test))
words=[w for w in words if w.strip() and w not in punctuations]
labels=words2labels(words)
for i in range(len(words)):
    print("%s/%s " % (words[i], labels[i]), end="")

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值