跳至主要内容

Python 中的词干提取与词形还原

本教程从实践角度讲解如何使用 Python 的自然语言工具包(NLTK)实现词干提取与词形还原。
· 12分钟 阅读

使用 AI 探索

现代英语被认为是一种弱屈折语言。这意味着英语中有许多词是由另一个词派生而来;例如,屈折形式“normality”源自词根“norm”。所有屈折语言都包含具有共同词根的词,但屈折程度会因语言而异。  

处理文本时,有时需要应用规范化技术,将词语从派生形式还原到其词根。这有助于降低随机性,使语料库中的词更接近预定义标准,并通过减少计算机需要处理的特征数量来提高处理效率。 

自然语言处理(NLP)中两种最常用的文本规范化技术是词干提取(stemming)和词形还原(lemmatization)。 

研究人员多年来一直在研究这些技术;NLP 从业者通常会在多种任务中使用它们来为后续处理准备词、文本和文档。 

本教程将使用 Python 的自然语言工具包(NLTK)从实践角度讲解词干提取与词形还原。

请查看此 DataLab 工作簿以总览本教程中的全部代码。要编辑并运行代码,请先复制该工作簿,再运行和修改其中的代码。

什么是词干提取?

词干提取是一种将屈折词还原为其词干的技术。例如,“programming”、“programmer”和“programs”都可以还原为共同的词干“program”。换句话说,“program”可以视为上述三个屈折形式的同义词。 

执行这种文本处理技术常用于应对稀疏性和/或规范化词汇。它不仅有助于减少冗余(多数情况下词干与其屈折形式含义相同),还可以让 NLP 模型学到屈折词与其词干之间的联系,从而帮助模型理解它们在相似语境中的用法。 

词干提取算法通过收集屈折词中常见的前缀和后缀列表,并截去词的末尾或开头来实现。这有时会产生并非真实单词的词干——这一局限我们将在下文讨论。

词干提取的优点

  • 提升模型性能:词干提取可减少算法需要处理的唯一词数量,从而提升性能。此外,它还能使算法运行得更快、更高效。
  • 分组相似词:即使词形不同,含义相近的词也可以被归为一组。这在文档分类等任务中很有用,因为需要识别文档中的关键主题或话题。 
  • 更易分析与理解:由于词干提取通常会缩小词汇量规模,文本会更易于分析、比较和理解。这在诸如情感分析等任务中很有帮助,因为目标是判定文档的情感倾向。 

词干提取的缺点

  • 过度提取/假阳性:当算法把不相关的不同屈折词还原为相同词干时会发生这种情况;例如,Porter Stemmer 会把 "universal"、"university" 和 "universe" 提取为同一词干。尽管它们在词源学上有关联,但在现代语义中分属截然不同的领域。将它们视为同义词会降低搜索结果的相关性。 
  • 提取不足/假阴性:当算法将本应归为同一词干的屈折词还原为不同词干时会发生这种情况。例如,Porter Stemmer 不会将 “alumnus”、“alumnae” 和 “alumni” 还原为同一词干,尽管它们应被视为同义词。 
  • 语言挑战:目标语言的词法、拼写和字符编码越复杂,词干提取器的设计就越困难;例如,由于动词屈折更多,意大利语的词干提取器比英语更复杂。而由于名词变格更多,俄语的词干提取器更是复杂。

什么是词形还原?

词形还原是另一种将屈折词还原到其根词的技术。它指的是根据词的预期含义,算法化地识别屈折词“lemma”(词典形式)的过程。 

与词干提取不同,词形还原依赖于准确判定词在语境中的词性及其含义。这意味着它会考虑屈折词在句子中的位置,以及该句子周围更广的语境,如相邻句子甚至整篇文档。

词形还原通常指借助词汇表和词形分析来“正确地”处理单词,通常仅移除屈折词尾,并返回单词的基础或词典形式,即所谓的 lemma。

Standford NLP Group, Standford NLP Group

换言之,对文档进行词形还原意味着“按规范来做”:使用词汇表并进行词形分析,仅去除屈折尾缀,返回单词的基础或词典形式,即“lemma”。例如,您可以预期词形还原算法会将 “runs”、“running” 和 “ran” 都映射到同一个 lemma——“run”。  

词形还原的优点

  • 准确性:词形还原并不像词干提取算法那样简单地截断单词。它会基于单词的词性进行分析,将语境纳入考量来生成 lemma。此外,词形还原的输出会是真实的词典词。

词形还原的缺点 

  • 耗时:与词干提取相比,词形还原更慢、更耗时。这是因为它需要进行词形分析,并从词典中推导单词的含义。 

在 Python 中使用 NLTK 的词干提取与词形还原示例

现在您已了解词干提取与词形还原的概念。本节我们将上机实践,使用 Python 与名为 NLTK 的库演示这两种技术的示例。 

Python 的 NLTK 包

自然语言工具包(NLTK)是一个用于构建可处理自然语言程序的 Python 库。该库可执行多种操作,如分词、词干提取、分类、解析、标注、语义推理、情感分析等。

最新版本为 NLTK 3.9.x,需要 Python 3.10 或更高版本(最高到 3.14),但您无需担心,因为它已预装在DataLab 工作簿中——只需导入 nltk 即可使用。

Python 词干提取示例

最受欢迎的词干提取算法之一是“Porter stemmer”。Porter stemmer 最早由 Martin Porter 在 1980 年的一篇论文《An algorithm for suffix stripping》中提出,现已成为英语词干提取中最常见的算法之一。

我们来看看它如何工作:

import nltk
from nltk.stem import PorterStemmer
nltk.download("punkt_tab")  # Use punkt_tab in NLTK 3.9+; punkt is deprecated
​
# Initialize Python porter stemmer
ps = PorterStemmer()
​
# Example inflections to reduce
example_words = ["program","programming","programer","programs","programmed"]
​
# Perform stemming
print("{0:20}{1:20}".format("--Word--","--Stem--"))
for word in example_words:
   print ("{0:20}{1:20}".format(word, ps.stem(word)))

"""
--Word--            --Stem--            
program             program             
programming         program             
programer           program             
programs            program             
programmed          program

"""

这是一个非常简单的示例;正如上文“词干提取”部分所述,我们对 Porter stemmer 的这些结果是预期之中的。 

我们来试一个更棘手的示例:

import string
from nltk.tokenize import word_tokenize

example_sentence = "Python programmers often tend like programming in python because it's like english. We call people who program in python pythonistas."

# Remove punctuation
example_sentence_no_punct = example_sentence.translate(str.maketrans("", "", string.punctuation))

# Create tokens
word_tokens = word_tokenize(example_sentence_no_punct)

# Perform stemming
print("{0:20}{1:20}".format("--Word--","--Stem--"))
for word in word_tokens:
    print ("{0:20}{1:20}".format(word, ps.stem(word)))

"""
--Word--            --Stem--            
Python              python              
programmers         programm            
often               often               
tend                tend                
like                like                
programming         program             
in                  in                  
python              python              
because             becaus              
its                 it                  
like                like                
english             english             
We                  we                  
call                call                
people              peopl               
who                 who                 
program             program             
in                  in                  
python              python              
pythonistas         pythonista
"""

您可以看到部分输出词并不在英语词典中(如 “becaus”、“people” 和 “programm.”)。另一个需要注意的是未考虑语境。例如,“programmers” 是复数名词,却被还原为 “program”,该词既可以是名词也可以是动词——换句话说,词根存在歧义。 

Python 词形还原示例

上下文敏感的词形还原器的动机在于提升对未见词和歧义词的处理表现。在本次词形还原示例中,我们将使用一个常见的词形还原器——WordNet lemmatizer。 

WordNet 是一个大型、免费、公开可用的英语词汇数据库,旨在建立词与词之间的结构化语义关系。

我们来实际看看:

from nltk.stem import WordNetLemmatizer
nltk.download("wordnet")
nltk.download("omw-1.4")
​
# Initialize wordnet lemmatizer
wnl = WordNetLemmatizer()
​
# Example inflections to reduce
example_words = ["program","programming","programer","programs","programmed"]
​
# Perform lemmatization
print("{0:20}{1:20}".format("--Word--","--Lemma--"))

for word in example_words:
   # pos="v" tells the lemmatizer to treat words as verbs
   # Other options: "n" (noun), "a" (adjective), "r" (adverb)
   print("{0:20}{1:20}".format(word, wnl.lemmatize(word, pos="v")))

"""
--Word--            --Lemma--           
program             program             
programming         program             
programer           programer           
programs            program             
programmed          program
"""   

如果输入词在 WordNet 中找不到,我们的还原器会保持其不变。这意味着必须提供语境,即在 wordnet_lemmatizer.lemmatize 中为词性参数 pos 赋值。

请注意,“programmer” 未被我们的还原器还原为 “program”:这是因为我们告知还原器只处理动词。 

让我们给还原器传入更复杂的内容,看看它的表现如何……

example_sentence = "Python programmers often tend like programming in python because it's like english. We call people who program in python pythonistas."
​
# Remove punctuation
example_sentence_no_punct = example_sentence.translate(str.maketrans("", "", string.punctuation))
​
word_tokens = word_tokenize(example_sentence_no_punct)
​
# Perform lemmatization
print("{0:20}{1:20}".format("--Word--","--Lemma--"))
for word in word_tokens:
   print ("{0:20}{1:20}".format(word, wnl.lemmatize(word, pos="v")))
"""
--Word--            --Lemma--           
Python              Python              
programmers         programmers         
often               often               
tend                tend                
like                like                
programming         program             
in                  in                  
python              python              
because             because             
its                 its                 
like                like                
english             english             
We                  We                  
call                call                
people              people              
who                 who                 
program             program             
in                  in                  
python              python              
pythonistas         pythonistas 
"""

词形还原算法返回的所有词都是英语词典中的词——除了 “pythonistas”,它更像是一个用来指代 Python 程序员的非正式术语。 

词干提取 vs. 词形还原

您已经了解了两种技术的实现,但它们究竟如何比较? 

词干提取和词形还原都是旨在将屈折词还原为共同基础词根的文本处理技术。尽管总体目标相关,但两者并不相同。 

两者的主要区别在于它们达成将屈折词还原为共同词根这一目标的方式不同。 

词干提取算法通过截去词的结尾或开头来尝试找到各种屈折形式的共同词根。截取依据是屈折词中常见的前缀和后缀列表。这种不加区分的截断有时能得到有意义的词干,但有时则不然。 

相反,词形还原算法通过进行词形分析来尝试从屈折词中找到共同的基础词根。为了准确还原屈折形式,必须维护一部详尽的词典,以便算法检索并将屈折词映射回其 lemma。

两者的差异还在于:词干提取更常把派生相关的词折叠到一起,而词形还原通常只折叠同一 lemma 的不同屈折形式。

Stanford NLP, IR Book

词干提取算法采用粗糙的启发式方法,通常意味着速度快、效率高,但并非总是准确。相较之下,词形还原算法为了准确性牺牲了速度与效率,因此能得到更有意义的基础词根。 

例如,某个词干提取算法可能会把 “saw” 还原成 “s.”。而词形还原算法会先根据使用语境判断 “saw” 是名词(锯子)还是动词(see 的过去式),再决定返回的 lemma——若为名词则返回 “saw”,若为动词则返回 “see”。

这些要点您现在可能已很清楚,那么百万美元问题来了——“我应该在文本预处理中使用词干提取还是词形还原?” 

就像大多数与软件相关的选择一样:视情况而定。 

您更在意速度与效率吗?如果是,选择词干提取。 

语境对您的应用重要吗?如果回答“是”,请使用词形还原。 

采用哪种技术完全取决于您所做的应用以及项目目标。您可以对两种技术都进行实验并比较结果,看看哪种方法更契合项目目标。 

本教程未深入讨论词形还原算法的构建原因在于已有多种库(如 SpaCy、NLTK 等)可用于不同语言。然而,如果您需要为一种尚无现成工具的语言(例如 Akan)自行构建词形还原器,就需要对该目标语言有充分的知识与理解。相较之下,在这类场景中构建词干提取算法要容易得多。

 词干提取词形还原
速度快慢
准确性较低较高
输出可能不是真实单词始终为真实单词
是否使用语境?否是
最佳适用场景搜索引擎、垃圾邮件过滤聊天机器人、情感分析、问答

结论

总而言之,词干提取与词形还原是 NLP 中用于文本处理的技术。它们都旨在将屈折形式还原为共同的基础词根,但实现方式各不相同。 

词形还原则比词干提取准确得多。它总是返回词典中的真实单词,并将语境纳入考虑。在大多数现代 NLP 流水线中,当准确性重要时,更倾向于使用词形还原。不过,如果您在大规模场景下工作或需要快速构建原型,词干提取可能已足够且速度显著更快。

Kurtis Pykes

作者

Kurtis Pykes

FAQs

使用 NLTK 的词干提取器和词形还原器是否需要额外下载?

对于 Porter Stemmer,除了安装 NLTK 本身外无需额外下载。对于 WordNet Lemmatizer,首次使用前需要运行一次 nltk.download("wordnet") 和 nltk.download("omw-1.4")。进行分词时,在 NLTK 3.9+ 中还需要 nltk.download("punkt_tab")。

为什么我的词形还原器返回的词与输入相同、没有变化?

通常有两种可能:要么该词没有在 WordNet 词典中找到,要么您没有指定正确的 pos(词性)参数。例如,wnl.lemmatize("running") 默认返回 "running",因为它假定为名词。传入 pos="v" 将其视为动词即可得到 "run"。

对搜索引擎而言,词干提取还是词形还原更好?

一般来说,搜索引擎更偏好词干提取,因为在这种规模下速度比绝对准确更重要。当用户搜索 "running shoes" 时,您希望快速返回含 "run"、"runner"、"runs" 的结果。偶尔产生非词典词并不会对体验造成太大影响。对于多数搜索用例,词形还原显得过于复杂。

这些技术能用于英语以外的语言吗?

可以,但难度不一。NLTK 对其他语言的原生支持有限。若要更广泛的多语言支持,spaCy 或 Stanza 等库更合适——尤其是 spaCy,在德语、法语、西班牙语、葡萄牙语等语言的词形还原上支持强大。为低资源语言(如 Akan)自行构建词干提取器或词形还原器则需要对该语言的词法有深厚了解。

词形还原一定比词干提取带来更好的 NLP 模型效果吗?

不一定,取决于任务和数据集。对于需要精确词义的任务——如问答、情感分析或聊天机器人——词形还原通常表现更好。对于文档分类或关键词提取等较简单任务,词干提取往往以更低的处理成本获得相当的效果。最稳妥的做法是两者都测试,并在您的特定数据上比较结果。

主题