自然语言处理听起来像是一整套复杂系统,但很多能力都可以从几个基础动作开始:把文本切成词,归一化词形,识别词性,再把短语结构抽出来。NLTK 是 Python 生态里非常适合练这些基本功的工具包,尤其适合用小段文本快速验证概念。
这篇文章围绕 NLTK 的典型知识点展开:tokenizing、stemming、tagging、chunking,以及简单文本分析。你可以把它当成一份面向工程实践的复习笔记,而不是只会背概念的选择题答案。
从文本到 token:NLP 管道的第一刀
大多数 NLP 工作的入口都是分词。机器不直接理解一整段自然语言文本,它通常先拿到一个 token 序列:单词、标点、数字,或者更细的子词单元。
在 NLTK 里,常见动作包括:
- 句子切分:把段落拆成句子。
- 单词分词:把句子拆成词和标点。
- 过滤停用词:去掉
the、is、and这类高频但信息量较低的词。 - 频率统计:观察文本中最常出现的词。
分词看似简单,但它会影响后面的所有步骤。比如 can't 要不要拆成 ca 和 n't?标点是否保留?大小写是否统一?这些选择都会改变统计结果。
词干提取不是词形还原
NLTK 常被用来演示 stemming,也就是词干提取。它会把 running、runs、runner 这类词压缩到更短的形式,方便做粗粒度匹配。
一个经典例子是 Porter Stemmer:
running可能变成runstudies可能变成studihappily可能变成happili
注意,stemming 追求的是规则化和压缩,不保证结果是真实单词。如果你的业务需要合法词形,比如把 was 还原成 be,那通常应该考虑 lemmatization,也就是词形还原。
可以这样判断选择:
- 搜索、粗略匹配、简单分类:stemming 往往够用。
- 面向用户展示、语言学分析、语义更敏感的任务:lemmatization 更稳。
- 对准确率要求高的生产系统:不要只靠词干,最好结合上下文模型或领域词典。
词性标注和分块:让文本有一点结构
分词之后,NLTK 可以进一步做词性标注,也就是给每个 token 标上名词、动词、形容词等标签。例如:
Python可能是专有名词。builds可能是动词。useful可能是形容词。
词性标注之后,就可以做 chunking。chunking 不是完整句法解析,它更像是轻量级短语抽取。比如用规则找出名词短语:
the quick brown foxa small NLP pipelinePython developers
这类能力在关键词抽取、实体候选发现、文本摘要预处理里很常见。它不一定完美,但非常适合快速搭建可解释的文本分析原型。
可以直接运行的 NLTK 小项目
下面这个示例展示一个最小 NLP 流水线:分句、分词、停用词过滤、词干提取、词性标注、名词短语分块。
运行前先安装依赖:
python -m pip install nltk
保存为 nltk_pipeline_demo.py 后运行:
import nltk
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer
from nltk.tokenize import sent_tokenize, word_tokenize
# 首次运行需要下载资源;如果你在受限环境中运行,可提前在联网机器下载到 NLTK_DATA。
nltk.download("punkt")
nltk.download("averaged_perceptron_tagger")
nltk.download("stopwords")
text = """
Python developers use NLTK to explore natural language processing.
They tokenize text, remove common words, tag parts of speech, and extract useful phrases.
"""
sentences = sent_tokenize(text)
stemmer = PorterStemmer()
stop_words = set(stopwords.words("english"))
print("Sentences:")
for sentence in sentences:
print("-", sentence)
words = word_tokenize(text)
normalized_words = [word.lower() for word in words if word.isalpha()]
content_words = [word for word in normalized_words if word not in stop_words]
stemmed_words = [stemmer.stem(word) for word in content_words]
tagged_words = nltk.pos_tag(content_words)
noun_phrase_grammar = "NP: {<JJ.*>*<NN.*>+}"
chunk_parser = nltk.RegexpParser(noun_phrase_grammar)
chunk_tree = chunk_parser.parse(tagged_words)
print("\nContent words:")
print(content_words)
print("\nStemmed words:")
print(stemmed_words)
print("\nPOS tags:")
print(tagged_words)
print("\nNoun phrase chunks:")
for subtree in chunk_tree.subtrees(filter=lambda tree: tree.label() == "NP"):
phrase = " ".join(word for word, tag in subtree.leaves())
print("-", phrase)
你可以替换 text 变量来分析自己的文本。如果处理中文,这段代码不能直接套用,因为中文通常需要专门分词器;NLTK 更适合作为英语 NLP 基础练习工具。
练习时别忽略这些边界
NLTK 的优势是清晰、教学友好、可解释。你能看到每一步如何改变文本,这对学习 NLP 特别重要。但在生产环境中,它也有边界:
- 规则和传统统计方法对噪声文本较敏感。
- 词干提取可能产生不可读的词形。
- 简单 chunk 规则很难覆盖复杂句法。
- 领域文本需要额外词典、规则或模型适配。
如果你正在学习 NLP,可以从 NLTK 的这些基础任务开始:先跑通分词和标注,再观察输出错误,最后尝试修改规则。真正的理解往往不是来自记住 API 名称,而是来自看见文本在每一步被怎样切开、压缩和重组。