一元标注
基于统计为单词分配最有可能的标记,注意不要将训练数据和测试数据混合在一起。会导致标注未知文本的时候得分低。
import nltk
from nltk.corpus import brown
#有单词和标记的数据
tagged_sents = brown.tagged_sents(categories="news")
#标注器,样本集使用tagged_sents
tagger = nltk.UnigramTagger(tagged_sents)
print("在训练集上评估准确性:",tagger.evaluate(tagged_sents))
#将语料分为训练集和测试集
size = len(tagged_sents) #总单词数
train_set = tagged_sents[:int(size*0.9)]
test_set = tagged_sents[int(size*0.9):]
tagger2 = nltk.UnigramTagger(train_set)
print("在测试集上评估准确性:",tagger2.evaluate(test_set))
输出:
在训练集上评估准确性: 0.9349006503968017
在测试集上评估准确性: 0.8121200039868434
虽然说效果还行,但是已经是下降了很多。相比于查询标注器区别就在于,标注的规则是模型使用训练语句得到的,而查询标注是给定了单词和对应的标注信息。
一般的N-gram的标注
在给定的上下文中选择最可能的标记,如wind在上下文the wind或者to wind中一般的unigram则因为名词词性比较常见就将to wind标注为名词。而现在是需要根据上下文确定当前单词的词性,如是n_gram标注则,标注第n个单词的词性,需要参考前面n-1个单词的词性才能做出标注。所以理所当然标注出来的结果也是单个的单词,并没有像n元语法一样组合多个单词为一个整体
import nltk
from nltk.corpus import brown
#获取二元标注的训练数据
corpus_sents = brown.tagged_sents(categories="news")
#句子总长度
size = len(corpus_sents)
#训练数据
train_sents = corpus_sents[:int(size*0.9)]
#测试数据
test_sents = corpus_sents[int(size*0.9):]
#二元标注模型
bigram_tagger = nltk.BigramTagger(train_sents)
#标注结果
data = bigram_tagger.tag(brown.sents(categories="news")[4203])
print(data)
#标注准确度测试
print(bigram_tagger.evaluate(test_sents))
标注的结果如果是出现在训练样本集里面的组合标注效果比较好,对于样本中没有出现的单词以及没有出现过的组合,模型的标注效果比较差,只能标注为None。相应的这个标注器也有backoff参数。
当n越大,每次确定当前单词的时候需要考虑的单词就越多,不存在上下文单词的概率也就越大,这个被称为数据稀疏问题。这就需要在结果精度和覆盖范围权衡。
组合标注器
解决办法有使用更加精确的算法,但是很多时候覆盖范围广的算法取得更好的效果。标注器在标注的时候对于训练集里面没有的单词或者单词组合(因为需要参考标注)调用backoff参数的模型处理,如果没有则标注为None。联合就是在这个参数上逐层将标注任务分给其他模型处理。
import nltk
from nltk.corpus import brown
#获取二元标注的训练数据
corpus_sents = brown.tagged_sents(categories="news")
#句子总长度
size = len(corpus_sents)
#训练数据
train_sents = corpus_sents[:int(size*0.9)]
#测试数据
test_sents = corpus_sents[int(size*0.9):]
#二元标注模型
bigram_tagger = nltk.BigramTagger(train_sents)
#标注准确度测试
print("二元标注模型的准确率:",bigram_tagger.evaluate(test_sents))
#构造一个联合的标注器,当二元找不到一个好的标注的时候选用1元标注器,
#1元标注器也找不到则选用默认的标注器
t0 = nltk.DefaultTagger("NN")
t1 = nltk.UnigramTagger(train_sents,backoff=t0)
t2 = nltk.BigramTagger(train_sents,backoff=t1)
print("联合标注模型的准确率:",t2.evaluate(test_sents))
输出结果:
二元标注模型的准确率: 0.10206319146815508
联合标注模型的准确率: 0.8452108043456593
标注生词
标注生词的任务,还是只能交给默认标注器和正则表达式标注器。
存储标注器
训练好的标注器,没有必要每次程序启动都进行训练,我们可以将它保存到一个文件中。
将模型保存到文件中:
- 导入pickle模块的dump
- 打开文件为写入二进制模式‘wb’
- dump(模型,文件,返回值)
- 关闭文件
加载文件中的模型:
- 导入pickle模块的load
- 打开文件为读取二进制模式‘rb’
- 模型 = load(文件流)
- 关闭文件
###代码接上面的一个代码##
###代码接上面的一个代码##
###代码接上面的一个代码##
#将t2模型保存到文件中
from pickle import dump
out = open("t2.txt",'wb')
dump(t2,out,-1)
out.close()
#加载模型
from pickle import load
input = open("t2.txt",'rb')
tagger3 = load(input)
print("t3标注器的正确率",tagger3.evaluate(test_sents))
基于转换的标注
n-gram一个潜在的问题是模型的大小不好确定,在回退时使用其他模型的话,可能会是一个很大的稀疏矩阵。第二个问题是使用上下文的特征为词标记忽略和词本身是有意义的。
Brill一种归纳标注方法,模型只有n-gram的很小一部分。brill标注是一种基于转换的学习,一般的方法是猜测每科次的标记然后返回或者是修复标记,Brill标注器在有监督的情况下将标记转换为一个更好的。猜测之后使用训练数据来评估这个标记,它不计数观察结果,只是编制一个转换修正规则链表。先将标记全部初始化为最频繁出现的标记。例如,初始化所有单词的词性为名词,然按照修改正规则表,如果前面一个单词的词性是TO则修改单词的词性为动词。
如对于下面句子的标注:
The president said he will ask Congress to increase grants to sates for vocationl rehabiliation.(总统表示:他将要求国会增加拨款给各州用于职业康复)

第二行使用unigram对句子进行标注将increase标注为了名词,第二个to标注为了不定式的to。rule1,和relu2是转换标注的修正规则,根据规则1由于increase前面是to,所以修正increase为动词,第二个to根据规则2后面跟的是名词复数,所以to修正为介词to。output是输出,黄金准则的到的也是这个结果。
在训练阶段会为转换模型创建很多规则,每一条规则都根据它修正的标记减去被它改错的标记进行打分。
相对于n-gram标注,Brill转换学到的规则是语言学可以理解的。
网友评论