使用nltk进行自定义标记

29 投票
4 回答
18129 浏览
提问于 2025-04-16 17:10

我正在尝试创建一种类似英语的小语言,用来指定任务。基本的想法是把一个句子拆分成动词和名词短语,让这些动词可以应用到这些名词上。我在使用nltk这个工具,但结果并没有达到我的期望,比如:

>>> nltk.pos_tag(nltk.word_tokenize("select the files and copy to harddrive'"))
[('select', 'NN'), ('the', 'DT'), ('files', 'NNS'), ('and', 'CC'), ('copy', 'VB'), ('to', 'TO'), ("harddrive'", 'NNP')]
>>> nltk.pos_tag(nltk.word_tokenize("move the files to harddrive'"))
[('move', 'NN'), ('the', 'DT'), ('files', 'NNS'), ('to', 'TO'), ("harddrive'", 'NNP')]
>>> nltk.pos_tag(nltk.word_tokenize("copy the files to harddrive'"))
[('copy', 'NN'), ('the', 'DT'), ('files', 'NNS'), ('to', 'TO'), ("harddrive'", 'NNP')]

在每个例子中,它都没有识别出第一个词(选择、移动和复制)是动词。我知道我可以创建自定义的标记器和语法来解决这个问题,但同时我又不想重新发明轮子,因为很多东西对我来说太复杂了。我特别希望能找到一种解决方案,可以处理非英语的语言。

所以,我的问题是: 有没有更好的标记器适合这种语法? 有没有办法让现有的标记器更倾向于使用动词形式,而不是名词形式? 有没有办法训练一个标记器? 有没有更好的方法呢?

4 个回答

8

看看Jacob的回答。

在后来的版本中(至少是nltk 3.2),nltk.tag._POS_TAGGER这个东西不存在了。默认的标注工具通常会下载到nltk_data/taggers/这个文件夹里,比如:

>>> import nltk
>>> nltk.download('maxent_treebank_pos_tagger') 

使用方法如下。

>>> import nltk.tag, nltk.data
>>> tagger_path = '/path/to/nltk_data/taggers/maxent_treebank_pos_tagger/english.pickle'
>>> default_tagger = nltk.data.load(tagger_path)
>>> model = {'select': 'VB'}
>>> tagger = nltk.tag.UnigramTagger(model=model, backoff=default_tagger)

另外可以参考:如何在Python中使用NLTK进行词性标注

22

雅各布的回答非常准确。不过,我想进一步说明一下,你可能需要的不仅仅是单个词。

举个例子,看看这三句话:

select the files
use the select function on the sockets
the select was good

在这里,"select"这个词分别作为动词、形容词和名词使用。单个词的标记器无法处理这种情况。即使是两个词的标记器也不行,因为有两个用法前面都跟着同一个词(也就是“the”)。你需要一个三个词的标记器才能正确处理这种情况。

import nltk.tag, nltk.data
from nltk import word_tokenize
default_tagger = nltk.data.load(nltk.tag._POS_TAGGER)

def evaluate(tagger, sentences):
    good,total = 0,0.
    for sentence,func in sentences:
        tags = tagger.tag(nltk.word_tokenize(sentence))
        print tags
        good += func(tags)
        total += 1
    print 'Accuracy:',good/total

sentences = [
    ('select the files', lambda tags: ('select', 'VB') in tags),
    ('use the select function on the sockets', lambda tags: ('select', 'JJ') in tags and ('use', 'VB') in tags),
    ('the select was good', lambda tags: ('select', 'NN') in tags),
]

train_sents = [
    [('select', 'VB'), ('the', 'DT'), ('files', 'NNS')],
    [('use', 'VB'), ('the', 'DT'), ('select', 'JJ'), ('function', 'NN'), ('on', 'IN'), ('the', 'DT'), ('sockets', 'NNS')],
    [('the', 'DT'), ('select', 'NN'), ('files', 'NNS')],
]

tagger = nltk.TrigramTagger(train_sents, backoff=default_tagger)
evaluate(tagger, sentences)
#model = tagger._context_to_tag

需要注意的是,你可以使用NLTK的NgramTagger来训练一个标记器,使用任意数量的n-grams,但通常在使用三个词的标记器后,性能提升就不大了。

32

一种解决方案是手动创建一个叫做UnigramTagger的工具,它可以在没有找到合适标签时退回到NLTK的标签器。大概是这样的:

>>> import nltk.tag, nltk.data
>>> default_tagger = nltk.data.load(nltk.tag._POS_TAGGER)
>>> model = {'select': 'VB'}
>>> tagger = nltk.tag.UnigramTagger(model=model, backoff=default_tagger)

这样你就能得到

>>> tagger.tag(['select', 'the', 'files'])
[('select', 'VB'), ('the', 'DT'), ('files', 'NNS')]

这个方法同样适用于非英语的语言,只要你有一个合适的默认标签器。你可以使用来自nltk-trainertrain_tagger.py来训练自己的标签器,并且需要一个合适的语料库。

撰写回答