作业头
这个作业属于哪个课程 | 自然语言处理 |
---|---|
这个作业要求在哪里 | 利用HMM实现词性标注作业要求 |
我在这个课程的目标 | 实现词性标注 |
这个作业在哪个具体方面帮助我实现目标 | 代码实现 |
参考文献 | 1.隐马尔科夫模型 2.基于HMM的词性标注 3.基于HMM+Viterbi算法的词性标注 Python |
文章目录
- 作业内容
- HMM模型介绍✨
- 维特比算法介绍
- 代码展示
作业内容
1.利用“1998人民日报词性标注语料库”进行模型的训练。
2.根据数据估计HMM的模型参数:全部的词性集合Q,全部的词集合V ,初始概率向量PI ,词性到词性的转移矩阵A ,词性到词的转移矩阵B 。 可以采用频率估计概率的方法计算模型参数,但需要进一步采用拉普拉斯平滑处理。
3.在模型预测阶段基于维特比算法进行解码,并给出测试文本:“那个球状闪电呈橘红色,拖着一条不太长的尾迹,在夜空中沿一条变换的曲线飘行着。”的词性标注结果。
HMM模型介绍✨
HMM,即隐马尔可夫模型(Hidden Markov Model),是一种基于状态转移的概率模型,它可以用来描述一个系统在不同状态之间转移的过程,其中状态是不可观测的,只能通过观测到的结果来推测。
HMM模型由两个部分组成:状态序列和观测序列。状态序列是一个随机的序列,描述了系统在不同状态之间的转移过程;观测序列是由状态序列生成的观测数据,描述了在不同状态下系统产生的观测结果。
HMM模型的基本假设是,系统的状态转移过程和观测结果都是由概率分布描述的。具体来说,HMM模型可以用一个三元组(S, O, A, B, π)来表示:
S:状态集合,包含所有可能的状态。
O:观测集合,包含所有可能的观测结果。
A:状态转移概率矩阵,描述了系统在不同状态之间转移的概率。
B:观测概率矩阵,描述了在不同状态下系统产生观测结果的概率。
π:初始状态概率向量,描述了系统在初始时各个状态的出现概率。
HMM模型的主要应用领域是语音识别、自然语言处理、图像识别、生物信息学等领域。在语音识别中,HMM模型被广泛应用,主要用于识别语音信号中的语音单元,如音素、音节等。在自然语言处理中,HMM模型可以用来对文本进行词性标注、命名实体识别等任务。在图像识别中,HMM模型可以用来对图像序列进行分析和识别。在生物信息学中,HMM模型可以用来对DNA序列进行分析和比对。
HMM模型的优点是可以很好地处理序列数据,能够捕捉到不同状态之间的转移过程,并且可以通过观测序列来推测系统的状态。
HMM三个基本问题:
估计问题:给定一个隐马尔科夫模型M=(A,B),如何有效计算某个观测序列O出现的概率,即计算P(O|M) (A表示转移概率,B表示发射概率) 。
解码问题:给定一个观测序列O和一个HMM模型M,寻找最好的隐序列Q以便最好的解释观测值
学习问题:依据给定的观测序列O以及HMM模型中的状态集合,学习最佳HMM参数模型A和B。
维特比算法介绍
维特比算法(Viterbi Algorithm)是一种基于动态规划的解决序列标注问题的算法。它可以用于许多应用,如语音识别、自然语言处理、生物信息学等领域。
维特比算法主要用于在隐藏马尔可夫模型(Hidden Markov Model,HMM)中寻找最有可能的状态序列。HMM是一种概率模型,它由一个隐含的马尔可夫链和一个生成观测的随机过程组成。它被广泛应用于语音识别、手写字符识别、自然语言处理、生物信息学等领域。
在HMM中,我们无法观察到隐含的状态序列,但是可以得到与状态相关的观测序列。维特比算法通过计算所有可能的状态序列的概率来寻找最有可能的状态序列,从而实现对观测序列的标注。
维特比算法的具体步骤如下:
初始化:设 t=1 时刻的各状态的最大概率为对应状态的初始状态概率。
递归计算:对于每个 t>1 的时刻和每个可能的状态 i,计算从时刻 1 到 t-1 的所有状态序列中,以状态 i 作为时刻 t-1 的状态,到时刻 t 的状态序列的最大概率以及相应的路径。
终止:最后一个时刻 T 时的各状态的最大概率即为整个序列的概率,并得到最可能的状态序列。 最终我们得到的就是最有可能的状态序列。
代码展示
存在一点问题。。。
import os
import numpy as np
# 读取语料库数据
def read_data(filename):
with open(filename, 'r', encoding='utf-8') as f:
data = f.read()
return data
# 预处理,将每行数据转化为词性标注对
def pre_process(data):
sentences = data.split('\n')[:-1]
result = []
for sentence in sentences:
words = sentence.split(' ')[:-1]
for word in words:
w, tag = word.rsplit('/', 1)
result.append((w, tag))
return result
# 计算模型参数,并进行拉普拉斯平滑处理
def train(train_data):
# 获取所有不同的词和词性
words = set([item[0] for item in train_data])
tags = set([item[1] for item in train_data])
# 统计初始概率向量
pi = {}
for _, tag in train_data:
if tag not in pi:
pi[tag] = 0
pi[tag] += 1
for tag in tags:
pi[tag] = (pi.get(tag, 0) + 1) / (len(train_data) + len(tags))
# 统计词性到词性的转移矩阵
A = {}
for i in range(len(train_data)-1):
cur_tag, next_tag = train_data[i][1], train_data[i+1][1]
if cur_tag not in A:
A[cur_tag] = {}
if next_tag not in A[cur_tag]:
A[cur_tag][next_tag] = 0
A[cur_tag][next_tag] += 1
for cur_tag in tags:
if cur_tag not in A:
A[cur_tag] = {}
for next_tag in tags:
A[cur_tag][next_tag] = (A[cur_tag].get(next_tag, 0) + 1) / (sum(A[cur_tag].values()) + len(tags))
# 统计词性到词的转移矩阵
B = {}
for word, tag in train_data:
if tag not in B:
B[tag] = {}
if word not in B[tag]:
B[tag][word] = 0
B[tag][word] += 1
for tag in tags:
if tag not in B:
B[tag] = {}
for word in words:
B[tag][word] = (B[tag].get(word, 0) + 1) / (sum(B[tag].values()) + len(words))
return list(tags), list(words), pi, A, B
# 预测阶段,使用维特比算法解码
def predict(test_data, tags, words, pi, A, B):
# 构建词性下标索引
tag2id = {tag: i for i, tag in enumerate(tags)}
result = []
for i in range(len(test_data)):
# 计算词在各个词性下的条件概率
word = test_data[i]
p_words = np.zeros(len(tags))
for j in range(len(tags)):
p_words[j] = np.log(B[tags[j]].get(word, 1e-10))
# 第一个词计算初始概率
if i == 0:
p = np.log([pi[tag] for tag in tags]) + p_words
max_idx = np.argmax(p)
result.append(tags[max_idx])
else:
# 计算上一个词的各个词性概率
prev_word = test_data[i-1]
p_prev = np.zeros(len(tags))
for j in range(len(tags)):
p_prev[j] = np.log(A[tags[j]].get(prev_tag, 1e-10)) + np.log(B[tags[j]].get(prev_word, 1e-10))
# 当前词基于上一个词的各个词性概率计算
p = p_prev + p_words
max_idx = np.argmax(p)
result.append(tags[max_idx])
prev_tag = result[-1]
return result
# 加载数据并进行预处理
data = read_data('1998人民日报语料库.txt')
train_data = pre_process(data[:10000]) # 仅使用前10000个标注对进行训练
test_data = '那个球状闪电呈橘红色,拖着一条不太长的尾迹,在夜空中沿一条变换的曲线飘行着。'.split()
# 训练模型
tags, words, pi, A, B = train(train_data)
# 预测结果
result = predict(test_data, tags, words, pi, A, B)
# 输出结果
print(' '.join([f"{test_data[i]}/{result[i]}" for i in range(len(test_data))]))