3. 基于词的分词(Word Tokenization):空格、标点规则,NLTK与spaCy实战

做 NLP 的第一步,往往就是怎么把一段话拆成一个个词。说白了,就是「这个词从哪里开始,到哪里结束」。最朴素的做法就是看空格和标点——英文天然空格分隔,中文就没这么幸运了。不过即使英文,也有不少坑。我这些年踩过不少,今天带你过一遍。

3.1 什么是词分词?

词分词(Word Tokenization)就是把文本切分成独立的词汇单元。对于英文,空格是天然的分隔符,但标点符号需要单独处理,比如句号、逗号、引号。比如:

"I'm a student."

理想的 列表是 ["I", "'m", "a", "student", "."]。你看,"'m" 和 "." 都独立出来了。

3.2 空格与标点规则:看是简单,陷阱不少

你以为按空格 split 就行?我曾经在项目里写过 text.split(),结果被测试同学骂惨了。因为标点怎么处理,直接影响下游任务。

  • 缩写:"U.S." 要不要拆成 "U" "." "S" "."?通常我们希望保留 "U.S." 作为整体。
  • 连字符:"state-of-the-art" 拆成三个词还是保留?看任务需求。
  • 表情与 Emoji:"😊" 算不算 ?现在很多模型专门保留。
  • 中文边界:中文没有空格,标点也只是部分参考。所以中英文混合时更麻烦。

嗯,这里要注意:简单规则永远有例外。所以成熟的工具库(NLTK、spaCy)内置了大量规则和异常列表,帮我们兜底。

3.3 NLTK 分词实战

NLTK 的 word_tokenize 背后用的是 Punkt 句子分割器 + 正则分词器。我习惯先下载资源:

import nltk
nltk.download('punkt_tab')
nltk.download('punkt')

from nltk.tokenize import word_tokenize

text = "I'm from the U.S., and I love Python!"
tokens = word_tokenize(text)
print(tokens)
# 输出:["I","'m","from","the","U.S.",",","and","I","love","Python","!"]

你看,"U.S." 被完整保留了,逗号和感叹号单独切出。如果你需要中文支持,还要额外下载 punkt_tab 的对应语言包。

个人小技巧:我在做多语言语料预处理时,NLTK 的 word_tokenize 配合 sent_tokenize 一起用,先分句再分词,准确率高不少。

3.4 spaCy 分词实战

spaCy 的分词更现代,基于神经网络规则 + 统计模型。它直接把分词结果封装在 Doc 对象里,每个 带了很多属性。

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("I'm from the U.S., and I love Python!")

for  in doc:
    print(.text, .pos_, .dep_)
# 输出示例:
# I PRON nsubj
# 'm AUX aux
# from ADP prep
# the DET det
# U.S. PROPN pobj
# , PUNCT punct
# and CCONJ cc
# ...

spaCy 的分词结果和 NLTK 类似,但多了词性、依存关系等信息。而且它处理特殊符号、连字符、URL 等更鲁棒。

经验之谈:我做过一个日志分析项目,里面全是 JSON 片段和特殊符号。spaCy 的自定义分词器(通过修改 Tokenizer 的规则)比 NLTK 灵活得多,后来我全切换到 spaCy 了。

3.5 NLTK vs spaCy 对比

特性 NLTK spaCy
底层原理 规则 + 统计(Punkt) 统计模型 + 神经网络
输出格式 字符串列表 Doc 对象( 迭代器)
多语言支持 需下载对应 punkt 包 官方提供 60+ 语言模型
速度 快(纯 Python 实现) 中(Cython 优化,但模型加载慢)
灵活性 可自定义正则规则 可重写 Tokenizer 规则或使用 Pipe
适用场景 教学、快速原型、简单任务 生产环境、Pipeline 集成

你看,没有的好坏。我自己的习惯是:写讲义或小实验用 NLTK,跑线上服务用 spaCy。当然,如果你要处理中文,spaCy 的中文模型(zh_core_web_sm)比我以前自己写的分词器稳定多了。

3.6 避坑指南

我曾经踩过一个坑:在预处理社交媒体文本时,直接用了 word_tokenize,结果把 "@username" 拆成了 "@" 和 "username",模型把 "@" 当成噪声过滤了,导致用户识别全出错。后来我加了自定义规则,用 TweetTokenizer(NLTK 自带的)就好了很多。所以,在正式使用前,一定要抽样检查你的 质量。

还有一个常见的误区:认为分词结果越细越好。其实不一定。比如命名实体 "New York",词分词拆成两个 ,但下游命名实体识别需要合并。所以,分词粒度要和你的任务对齐。

词分词流程与工具选择 原始文本 空格 & 标点规则 考虑缩写、连字符、Emoji、中文边界 NLTK word_tokenize Punkt + 正则规则 spaCy Tokenizer 统计模型 + 属性标注 词列表 Tokens 中文无空格,需额外处理 建议用 jieba / spaCy 中文模型

总结一下:词分词是文本处理的起点。空格和标点规则看似简单,实际充满细节。NLTK 和 spaCy 各有侧重,选哪个取决于你的项目阶段和需求。如果你想进一步了解不同语言的分词特性,后面我会专门讲中文分词。今天就到这里,不妨打开你的 Python,试一下上面两个例子,看看你的数据里藏着哪些坑。