ZMonster's Blog 巧者劳而智者忧,无能者无所求,饱食而遨游,泛若不系之舟

词形还原工具对比

注: 本文仅讨论英文

词形还原(Lemmatization)

在英文当中,一些词会在不同的情况中有不同的形态:名词有单复数,动词有时态和语态,形容词有比较级。而在自然语言处理的一些场景中,这种形态的差异是没有意义的甚至有干扰作用,需要将不同形态的词转换为其 原型 ,这种处理就称为 "词形还原(Lemmatization/Lemmatisation)" ,得到的原型被称为 "词元(Lemma)" 。

举栗子:

  1. dogs -> dog, cats -> cat
  2. doing -> do, done -> do
  3. better -> good

为什么说词的不同形态有干扰作用呢?这么说其实不太严谨,要结合实际应用来说才是有意义的,不妨以文档相似计算这个应用来看一看。

假设我们有三个文档,内容分别为:

D1: I work at home
D2: I was working at home
D3: I was sleeping at home

从表达的内容上来看,我们会认为 D1 和 D2 是更接近的,而 D2 和 D3 表达的是两件事情。接下来计算一下 Document-Term 矩阵

  D1 D2 D3
I 1 1 1
was 0 1 1
work 1 0 0
working 0 1 0
sleeping 0 0 1
at 1 1 1
home 1 1 1

然后计算三个文档之间两两的相似性,为计算简单,这里定义文档相似性为: 两个文档在表中对应的列中 相同位的个数 ,那么

  D1 D2 D3
D1 7 4 4
D2 4 7 5
D3 4 5 7

结果是: D2 和 D3 最相似 —— 这和预期的结果是不一致的。

如果对文档中每个词都进行词形还原,那么结果会不一样。

注意,词形还原(lemmatization)与词干提取(stemming)在一些词的表现上很相似,但它们是两个东西。

词形还原的工具

Python: NLTK

NLTK 是著名的 Python 自然语言处理包,其中包含了非常多的自然语言处理相关方法和工具,其中也包含了词形还原的功能。

NLTK 中的词形还原功能依赖 WordNet 语料和一个名为 average_perceptron_tagger 的词性标注集,所以在使用其词形还原方法前需要先下载相关的数据:

import nltk

nltk.download("wordnet")
nltk.download("averaged_perceptron_tagger")

使用方法:

from nltk.stem import WordNetLemmatizer

lemmatizer = WordNetLemmatizer()
print lemmatizer.lemmatize('dogs')
print lemmatizer.lemmatize('working', pos='v')
dog
work

NLTK 里这个词形还原工具的一个问题是需要手动指定词性,比如上面例子中的 "working" 这个词,如果不加后面那个 pos 参数,输出的结果将会是 "working" 本身。

如果希望在实际应用中使用 NLTK 进行词形还原,一个完整的解决方案是:

  1. 输入一个完整的句子
  2. 用 NLTK 提供的工具对句子进行分词和词性标注
  3. 将得到的词性标注结果转换为 WordNet 的格式
  4. 使用 WordNetLemmatizer 对词进行词形还原

其中分词和词性标注又有数据依赖:

nltk.download("punkt")
nltk.download("maxnet_treebank_pos_tagger")

给一个小 demo

from nltk.corpus import wordnet
from nltk import word_tokenize, pos_tag
from nltk.stem import WordNetLemmatizer


def get_wordnet_pos(treebank_tag):
    if treebank_tag.startswith('J'):
        return wordnet.ADJ
    elif treebank_tag.startswith('V'):
        return wordnet.VERB
    elif treebank_tag.startswith('N'):
        return wordnet.NOUN
    elif treebank_tag.startswith('R'):
        return wordnet.ADV
    else:
        return None


def lemmatize_sentence(sentence):
    res = []
    lemmatizer = WordNetLemmatizer()
    for word, pos in pos_tag(word_tokenize(sentence)):
        wordnet_pos = get_wordnet_pos(pos) or wordnet.NOUN
        res.append(lemmatizer.lemmatize(word, pos=wordnet_pos))

    return res

Python: Pattern

Pattern 是一个 Python 的数据挖掘库,还实现了自然语言处理、机器学习以及数据可视化等一整套的功能,是一个非常强大的库, Github 上 4000 多个 star 呢。

Pattern 中的 en 模块是专门用于英文的自然语言处理的,实现了很多非常棒的方法,比如:

from pattern.en import quantify

print quantify(['goose', 'goose', 'duck', 'chicken', 'chicken', 'chicken'])
print quantify({'carrot': 100, 'parrot': 20})
print quantify('carrot', amount=1000)
several chickens, a pair of geese and a duck
dozens of carrots and a score of parrots
hundreds of carrots

再比如:

from pattern.en import comparative, superlative

print comparative('bad')
print superlative('bad')
worse
worst

嗯,跑题了……使用 Pattern 进行词形还原在形式上非常简单,如下所示:

from pattern.en import lemma

print lemma('working')
work

So easy!完全不用管什么词性标注之类的。

Python: TextBlob

TextBlob 是一个专注自然语言处理的 Python 库,集成了 NLTK 和 Pattern 的一些功能。其中的词形还原功能使用如下:

from textblob import TextBlob

blob = TextBlob('I was working at home')
print blob.words
for word in blob.words:
    print word.lemmatize()
['I', 'was', 'working', 'at', 'home']
I
wa
working
at
home

结果并不是很理想,同 NLTK 一样,可以传入词性来使词形提高还原结果。

Tree Tagger

Tree Tagger 是一个命令行词性标注工具,词形还原是它附带的功能。

安装:

  1. 下载词性标注器: Linux / Mac OS-X
  2. 下载词性标注 脚本
  3. 下载安装工具: install-tagger.sh
  4. 下载需要处理的语言的参数文件: 英语
  5. 将上述下载好的资源放到同一个目录中,其中的压缩包不需要解压,直接执行 install-tagger.sh

    chmod +x install-tagger.sh && ./install-tagger.sh
    

完成上述操作后将会在当前目录中生成 bin 和 cmd 两个目录,其中是一些可执行程序或脚本,不多介绍。想要进行词形还原,以英语为例,可以这么做:

echo 'I was working at home' | tree-tagger-english
I   PP  I
was VBD be
working VBG work
at  IN  at
home    NN  home