三个标注器:默认标注器、正则表达式标注器和查询标注器。前面先进行简单介绍,后面再贴代码吧。
默认标注器
通过已经标注的文本获取其中出现最多的标签,然后待标注的文本都标注为这个标签。标注的结果什么样的都行。最好字典吧{单词:属性}
可以先自己尝试一下,获取已经标注过的句子brown.tagged_sents(categories='news')获取布朗语料库中已经标注的句子,分类是新闻类。
标注器对象:nltk.DefaultTagger(tag),调用该对象的tag( )方法对单词进行标注,所有的单词都会标注成tag。
正则表达式标注器
在一定单词知识的基础之上,识别单词,如以 ‘s 结尾的单词为名词所有格,以ed结尾的都看做过去式等等之类的。将所有正则表达式都组合成模式串,作为正则表达式标注器的参数。
patterns = [
(r'.*ing$',"VBG"),
(r'.*ed$',"VBD"),
(r'.*es$',"VBZ"),
(r'.*ould$',"MD"),
(r'.*\'s$',"NN"),
(r'.*s$',"NNS"),
(r'^-?[0-9]+(.[0-9]+)?$',"CD"),
(r'.*',"NN") #匹配所有单词,默认标注为NN
]
调用正则标注器 nltk.RegexpTagger(模式串),创建一个标注对象,调用该对象的tag()方法对单词进行标注,单词必须切分好。
查询标注器
通过已经标注的语料,使用频率分布得到出现频率高的单词,然后通过条件频率分布获得该单词最多的词性标注,构建这些单词和标签的字典作为参数新建查询标注器。构建查询标注器:nltk.UnigramTagger(model=likely_tags),此方法还有一个比较重要的参数:backoff值可以是前面两种标注器的对象,当查询标注器遇到没有在model字典的单词,如果backoff咩有值就标注为None,如果有值(模型),就调用这个标注器进行标注,能够提高很多正确率。
下面是这三个模型代码:
import nltk
from nltk.corpus import brown
brown_tagged_sents = brown.tagged_sents(categories='news')
brown_sents = brown.sents(categories='news')
print("布朗语料库中已经标注的句子",brown_tagged_sents)
print("没有标注的句子",brown_sents)
#******************************************************************************
#默认标注器
#获取文章中所有的标记
tags = [tag for (word,tag) in brown.tagged_words(categories='news')]
tag_fd = nltk.FreqDist(tags)
max_tag = tag_fd.max()
print("出现最多的标记:",max_tag)
#标注之前可以将大部分的单词都初始化为使用最多的标记
#简要标注一个文本,标注的形式就是字典
default_tagger = nltk.DefaultTagger(max_tag)
words = "hello world hello python".split()
word_tagged = default_tagger.tag(words)
print("默认标注器标注效果",word_tagged)
#标注的效果并不是很好,大部分都是错误的
#********************************************************************************
#正则表达式标注器
#正则表达式模式串
patterns = [
(r'.*ing$',"VBG"),
(r'.*ed$',"VBD"),
(r'.*es$',"VBZ"),
(r'.*ould$',"MD"),
(r'.*\'s$',"NN"),
(r'.*s$',"NNS"),
(r'^-?[0-9]+(.[0-9]+)?$',"CD"),
(r'.*',"NN")
]
#我自己来编造一个和正则表达式差不多的文本吧
words = "I heated Tom's oranges it is't interesting ".split()
#创建一个正则表达式的标注器
reg_tagger = nltk.RegexpTagger(patterns)
word_tagged = reg_tagger.tag(words)
print("正则表达式标注器标注效果",word_tagged)
#新的标注器比上一个好一些了,但是需要编写的正则表达式也变多了
#********************************************************************************
#查询标注器 --- 据说是能够正确标注一半左右
#找出100个最频繁的词,存储他们的标记,然后使用这个信息作为查找标注器的模型
#获取未标注单词的频率
fd = nltk.FreqDist(brown.words(categories='news'))
#获取已经标注的单词的频率分布
cfd = nltk.ConditionalFreqDist(brown.tagged_words(categories="news"))
most_freq_words = list(fd.keys())[:100]
#下面这句在最高频率单词的情况下,获取该单词使用最多的词性
likely_tags = dict((word,cfd[word].max()) for word in most_freq_words)
baseline_tagger = nltk.UnigramTagger(model=likely_tags)
print("正确率评估",baseline_tagger.evaluate(brown_tagged_sents))
#上面这个只能标注在100个高频词里面的词,其他的词会被分配一个None标签
#我们需要在它不能标注的时候使用正则表达式标注
print("联合标记",baseline_tagger.tag(words))
baseline_tagger = nltk.UnigramTagger(model=likely_tags,backoff=reg_tagger)
print("联合标记回退使用了正则标记:",baseline_tagger.tag(words))
print("正确率评估",baseline_tagger.evaluate(brown_tagged_sents))
运行结果
查询标注模型性能的评估
import nltk
from nltk.corpus import brown
def performance(cfd,wordlist):
lt = dict((word,cfd[word].max()) for word in wordlist)
basline_tagger = nltk.UnigramTagger(model=lt,backoff=nltk.DefaultTagger("NN"))
return basline_tagger.evaluate(brown.tagged_sents(categories="news"))
def display():
import pylab
word_by_freq = list(nltk.FreqDist(brown.words(categories="news")))
cfd = nltk.ConditionalFreqDist(brown.tagged_words(categories="news"))
sizes = [2 ** i for i in range(15)]
perfs = [performance(cfd,word_by_freq[:size]) for size in sizes]
pylab.plot(sizes,perfs,'-bo')
pylab.show()
display()
准确率随高频样本集的变化
随着高频率样本集的增多,标注的准确率也得到了很大提高,主要原因是,后面继续增加的词在文本中的频率越来越小。所以曲线增式变缓,由于前面的高频部分将一词多种词性这种情况标注为出现次数最多的频率,所以准确率是不可能等于1的。
网友评论