自然语言工具包 - 组合标注器



组合标注器

组合标注器或将标注器彼此链接是 NLTK 的重要功能之一。组合标注器背后的主要概念是,如果一个标注器不知道如何标注一个单词,它将被传递给链接的标注器。为了实现这一目的,SequentialBackoffTagger 为我们提供了回退标注功能。

回退标注

如前所述,回退标注是SequentialBackoffTagger 的重要功能之一,它允许我们以以下方式组合标注器:如果一个标注器不知道如何标注一个单词,则该单词将传递给下一个标注器,依此类推,直到没有剩余的回退标注器可供检查。

它是如何工作的?

实际上,SequentialBackoffTagger 的每个子类都可以接受一个“backoff”关键字参数。此关键字参数的值是SequentialBackoffTagger 的另一个实例。现在,每当初始化此SequentialBackoffTagger 类时,都会创建一个回退标注器内部列表(自身作为第一个元素)。此外,如果给定了一个回退标注器,则此回退标注器的内部列表将被追加。

在下面的示例中,我们采用DefaulTagger 作为上述 Python 代码中训练UnigramTagger 的回退标注器。

示例

在此示例中,我们使用DefaulTagger 作为回退标注器。每当UnigramTagger 无法标注一个单词时,回退标注器(在本例中为DefaulTagger)将用“NN”对其进行标注。

from nltk.tag import UnigramTagger
from nltk.tag import DefaultTagger
from nltk.corpus import treebank
train_sentences = treebank.tagged_sents()[:2500]
back_tagger = DefaultTagger('NN')
Uni_tagger = UnigramTagger(train_sentences, backoff = back_tagger)
test_sentences = treebank.tagged_sents()[1500:]
Uni_tagger.evaluate(test_sentences)

输出

0.9061975746536931

从以上输出可以看出,通过添加回退标注器,准确率提高了约 2%。

使用 pickle 保存标注器

正如我们所看到的,训练一个标注器非常麻烦,而且需要时间。为了节省时间,我们可以将训练好的标注器进行 pickle 处理,以便以后使用。在下面的示例中,我们将对我们已经训练好的名为“Uni_tagger”的标注器进行此操作。

示例

import pickle
f = open('Uni_tagger.pickle','wb')
pickle.dump(Uni_tagger, f)
f.close()
f = open('Uni_tagger.pickle','rb')
Uni_tagger = pickle.load(f)

NgramTagger 类

从前面单元中讨论的层次结构图中,UnigramTagger 继承自NgarmTagger 类,但NgarmTagger 类还有另外两个子类:

BigramTagger 子类

实际上,n 元组是 n 个项目的子序列,因此,顾名思义,BigramTagger 子类会查看两个项目。第一个项目是前一个已标注的单词,第二个项目是当前已标注的单词。

TrigramTagger 子类

BigramTagger 同理,TrigramTagger 子类会查看三个项目,即前两个已标注的单词和一个当前已标注的单词。

实际上,如果我们像使用 UnigramTagger 子类一样单独应用BigramTaggerTrigramTagger 子类,它们的性能都非常差。让我们在下面的示例中看看。

使用 BigramTagger 子类

from nltk.tag import BigramTagger
from nltk.corpus import treebank
train_sentences = treebank.tagged_sents()[:2500]
Bi_tagger = BigramTagger(train_sentences)
test_sentences = treebank.tagged_sents()[1500:]
Bi_tagger.evaluate(test_sentences)

输出

0.44669191071913594

使用 TrigramTagger 子类

from nltk.tag import TrigramTagger
from nltk.corpus import treebank
train_sentences = treebank.tagged_sents()[:2500]
Tri_tagger = TrigramTagger(train_sentences)
test_sentences = treebank.tagged_sents()[1500:]
Tri_tagger.evaluate(test_sentences)

输出

0.41949863394526193

您可以将之前使用的 UnigramTagger 的性能(准确率约为 89%)与 BigramTagger(准确率约为 44%)和 TrigramTagger(准确率约为 41%)进行比较。原因是 Bigram 和 Trigram 标注器无法从句子中的第一个单词(或单词)学习上下文。另一方面,UnigramTagger 类不关心之前的上下文,并猜测每个单词最常见的标签,因此能够获得较高的基准准确率。

组合 n 元标注器

从以上示例可以看出,当我们将 Bigram 和 Trigram 标注器与回退标注相结合时,它们可以做出贡献。在下面的示例中,我们将 Unigram、Bigram 和 Trigram 标注器与回退标注相结合。该概念与之前将 UnigramTagger 与回退标注器组合的代码相同。唯一的区别是我们使用 tagger_util.py 中提供的名为 backoff_tagger() 的函数进行回退操作,如下所示。

def backoff_tagger(train_sentences, tagger_classes, backoff=None):
   for cls in tagger_classes:
      backoff = cls(train_sentences, backoff=backoff)
   return backoff

示例

from tagger_util import backoff_tagger
from nltk.tag import UnigramTagger
from nltk.tag import BigramTagger
from nltk.tag import TrigramTagger
from nltk.tag import DefaultTagger
from nltk.corpus import treebank
train_sentences = treebank.tagged_sents()[:2500]
back_tagger = DefaultTagger('NN')
Combine_tagger = backoff_tagger(train_sentences,
[UnigramTagger, BigramTagger, TrigramTagger], backoff = back_tagger)
test_sentences = treebank.tagged_sents()[1500:]
Combine_tagger.evaluate(test_sentences)

输出

0.9234530029238365

从以上输出可以看出,准确率提高了约 3%。

广告