标注是典型的NLP流水线中继分词之后的第二个步骤
在NLP流程中,对工具的性能评估是NLP的一个中心主题,一个模块输出中的任何错误在下游模块都被无限放大了!
词性标注(part-of-speech tagging, POS tagging)
给文本中的词自动分配词性的过程称为词性标注、POS标注或标注。
词性标注是NLP中一个重要的、早期的序列分类任务:利用局部上下文语境中的词和标记,对序列中任意一点的分类决策。
词性标注器(part-of-speech tagger, POS tagger)
import nltk
text = nltk.word_tokenize("...")
nltk.pos_tag(text)
自动标记
默认标注器
为每个标识符分配同样的标记。找出最多的那个标记
>>> tags = [tag for (word,tag) in brown.tagged_words(categories='news')]
>>> nltk.FreqDist(tags).max()
u'NN'
>>> raw = '....'
>>> tokens = nltk.word_tokenize(raw)
>>> default_tagger = nltk.DefaultTagger('NN')
>>> default_tagger.tag(tokens)
>>> default_tagger.evaluate(brown_tagged_sents)
0.13089484257215028
正则表达式标注器
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')]
regexp_tagger = nltk.RegexpTagger(patterns)
regexp_tagger.tag(brown_sents[3])
regexp_tagger.evaluate(brown_tagged_sents)
0.20326391789486245
查询标记器
找出100个最频繁的词,存储它们最有可能的标记。然后利用这个信息作为查找标注器。
fd = nltk.FreqDist(bown.words(categories='news'))
cfd = nltk.ConditionalFreqDist(brown.tagged_words(categories='news'))
most_freq_words = fd.keys()[:100]
likely_tags = dict((word,cfd[word].max()) for word in most_freq_words)
baseline_tagger = nltk.UnigramTagger(model=likely_tags)
baseline_tagger.evaluate(brown_tagged_sents)
0.4557849513691344
对于不在前100个最频繁的词,分配默认标记‘NN’。也就是说先使用查找表,如果不在里面就使用默认标记器。——这个过程叫做回退,通过制定一个标注器作为另一个标注器的参数。
baseline_tagger = nltk.UnigramTagger(model=likely_tags,
backoff=nltk.DefaultTagger('NN'))
N-gram标注
一元标注器(Unigram Tagging)
简单的统计算法,对每个标识符分配最有可能的标记。类似查找标注器。
In [1]: import nltk
In [2]: from nltk.corpus import brown
In [3]: brown_tagged_sents = brown.tagged_sents(categories='news')
In [4]: brown_sents = brown.sents(categories='news')
In [5]: unigram_tagger = nltk.UnigramTagger(brown_tagged_sents)
In [6]: unigram_tagger.tag(brown_sents[2007])
Out[6]:
[(u'Various', u'JJ'),
(u'of', u'IN'),
(u'the', u'AT'),
(u'apartments', u'NNS'),
(u'are', u'BER'),
(u'of', u'IN'),
(u'the', u'AT'),
(u'terrace', u'NN'),
(u'type', u'NN'),
(u',', u','),
(u'being', u'BEG'),
(u'on', u'IN'),
(u'the', u'AT'),
(u'ground', u'NN'),
(u'floor', u'NN'),
(u'so', u'QL'),
(u'that', u'CS'),
(u'entrance', u'NN'),
(u'is', u'BEZ'),
(u'direct', u'JJ'),
(u'.', u'.')]
In [7]: unigram_tagger.evaluate(brown_tagged_sents)
Out[7]: 0.9349006503968017
分离训练和测试数据
In [11]: size = int(len(brown_tagged_sents) * 0.9)
In [12]: train_sents = brown_tagged_sents[:size]
In [13]: test_sents = brown_tagged_sents[size:]
In [14]: unigram_tagger = nltk.UnigramTagger(train_sents)
In [15]: unigram_tagger.evaluate(test_sents)
Out[15]: 0.8120203329014253
N-gram标注器
N-gram标注器可以定义较大数值的n,但是当n大于3时,常常会面临数据稀疏问题。即使使用大量的训练数据,看到的也只是上下文的一小部分。
unigram标注器的一般化,它的上下文是当前词和它前面n-1个标识符的词性标记,n-gram标注器将挑选在给定上下文中最有可能的标记
注意!:bigram标注器能够标注训练中它看到过的句子中的所有词,但对一个没见过的句子却不行。只要遇到一个新词,就无法给他分配标记。
>>> bigram_tagger.evaluate(test_sents)
0.10276088906608193
当n越大,上下文的奇异性就会增加,要标注的数据中包含训练数据中不存在的上下文的几率也增大。这被称为数据稀疏问题。
组合标注器
- 尝试使用bigram标注器标注标识符
- 如果bigram标注器无法找到标记,尝试unigram标注器
- 如果unigram标注器也无法找到标记,使用默认标注器
回退标注器
回退是一个组合模型的方法:当一个较专业的模型(如bigram标注器)不能为给定内容分配标记时,可以回退到一个较一般的模型(如unigram标注器)
t0 = nltk.DefaultTagger('NN')
t1 = nltk.UnigramTagger(train_sents, backoff=t0)
t2 = nltk.BigramTagger(train_sents, backoff=t1)
t2.evaluate(test_sents)
0.84491179108940495
基于转换的标注
Brill标注
猜想每个词的标记,然后返回和修复错误的。编制一个转换修正规则链表。
首先使用unigram标注器标注,然后运用规则修正错误。
例如规则:
(a)当前面的词是TO时,替换NN为VB;
(b)当下一个标记是NNS时,替换TO为IN.
以上规则根据以下形式的模板产生:“在上下文C中,替换T1为T12”
规则是语言学可以解释的。
参考《Natural Language Processing with Python》