0、问题描述:
实验三:基于HMM的词性标注
任务:将语料分为测试集、训练集(一般比例1:4),训练集上:统计初始概率,发射概率,转移概率,估算所需的参数。
实验四:
任务1:利用Viterbi算法,实现基于HMM的词性标注。
任务2:编写评价程序,计算词性标注的准确率。
该程序的实现过程如下:
1. 使用nltk库中提供的布朗语料库加载数据集。
2. 将语料库分为训练集和测试集,比例为4:1。
3. 定义train_hmm函数,根据训练集,统计初始概率、发射概率和转移概率,并返回这些参数。
4. 定义viterbi函数,实现Viterbi算法,对输入的句子进行词性标注,返回预测的词性序列。
5. 定义pos_tagging函数,对测试集进行词性标注,并计算准确率。
6. 调用train_hmm函数,训练HMM模型,并调用pos_tagging函数对测试集进行词性标注,最终输出准确率。
需要注意的是,由于HMM是一种生成模型,其参数的估计需要大量的数据和计算资源,
因此在实际应用中,需要对数据进行一定的预处理和降维处理,以提高模型的效率和准确率。
此外,由于HMM模型对文本的分词和词性标注是基于概率的,因此在一些特殊的语境下,可能会出现较大的错误率。
另外:训练集和数据集的理解:
假设题库有100道题目:
训练集:平时老师拿来给你练习的题目。一般比较多,假设老师拿出80道题目给你们练习
测试集:考试的时候,老师想测试你们学得咋样,所以拿出剩下的20道题给你们期末考试。
所以为啥一般训练题更多,因为训练是大量的,测试只是想看看有没有效果,所以测试一般就是少量的
1、nltk(Natural Language Toolkit)是一个用于自然语言处理的Python库,
提供了各种文本处理功能,例如分词、词性标注、命名实体识别、语义分析等等。
在本任务中,我们使用了nltk库提供的`布朗语料库作为训练和测试集`。
如果已经有了数据集,替换即可
2、sklearn.model_selection中的train_test_split函数可以将
数据集按照一定比例(或指定数量)分为训练集和测试集,方便进行模型训练和测试。
3、collections中的defaultdict是一个类,提供了默认值的字典功能,可以使得在访问不存在的键时返回一个默认值
而不是引发KeyError异常,这在处理大规模数据时非常方便。
在本任务中,我们使用defaultdict来统计标签的频率和计算概率。
import nltk
from sklearn.model_selection import train_test_split
from collections import defaultdict
# 1、加载数据集
corpus = nltk.corpus.brown.tagged_sents(tagset='universal')
# 2、将语料分为测试集和训练集
train_corpus, test_corpus = train_test_split(corpus, test_size=0.2, random_state=42)
# 3、根据训练集:统计初始概率、发射概率、转移概率
def train_hmm(tagged_corpus):
# 统计标签频率
tag_freq = defaultdict(int)
for sentence in tagged_corpus:
for word, tag in sentence:
tag_freq[tag] += 1
# 计算初始概率
total_sentences = len(tagged_corpus)
start_prob = {tag: tag_freq[tag]/total_sentences for tag in tag_freq}
# 统计发射概率和转移概率
emit_prob = defaultdict(lambda: defaultdict(int))
trans_prob = defaultdict(lambda: defaultdict(int))
for sentence in tagged_corpus:
for i in range(len(sentence)):
word, tag = sentence[i]
emit_prob[tag][word] += 1
if i == 0:
trans_prob['start'][tag] += 1
else:
prev_tag = sentence[i-1][1]
trans_prob[prev_tag][tag] += 1
# 将频率转换为概率
tag_list = list(tag_freq.keys())
emit_prob = {tag: {word: count/tag_freq[tag] for word, count in emit_prob[tag].items()} for tag in tag_list}
trans_prob = {tag1: {tag2: count/tag_freq[tag1] for tag2, count in trans_prob[tag1].items()} for tag1 in tag_list}
return start_prob, emit_prob, trans_prob
# 4、使用Viterbi算法对测试集进行词性标注
def viterbi(sentence, start_prob, emit_prob, trans_prob):
# 初始化
v = [{}]
path = {}
for tag in start_prob:
v[0][tag] = start_prob[tag] * emit_prob[tag].get(sentence[0], 0)
path[tag] = [tag]
# 递推
for t in range(1, len(sentence)):
v.append({})
newpath = {}
for tag in start_prob:
prob, best_tag = max((v[t-1][prev_tag] * trans_prob[prev_tag].get(tag, 0) * emit_prob[tag].get(sentence[t], 0), prev_tag) for prev_tag in start_prob)
v[t][tag] = prob
newpath[tag] = path[best_tag] + [tag]
path = newpath
# 终止
prob, best_tag = max((v[len(sentence)-1][tag], tag) for tag in start_prob)
return path[best_tag]
# 5、对测试集进行词性标注
def pos_tagging(test_corpus, start_prob, emit_prob, trans_prob):
total = 0
correct = 0
for sentence in test_corpus:
words, tags = zip(*sentence)
pred_tags = viterbi(words, start_prob, emit_prob, trans_prob)
for j in range(len(tags)):
total += 1
if tags[j] == pred_tags[j]:
correct += 1
accuracy = correct / total
return accuracy
# 6、训练HMM模型、#对测试集进行词性标注,并计算准确率并输出
start_prob, emit_prob, trans_prob = train_hmm(train_corpus)
accuracy = pos_tagging(test_corpus, start_prob, emit_prob, trans_prob)
print('Accuracy: {:.2%}'.format(accuracy))