词级别分词:空格分词与标点分词
讲完了字节对编码这些花哨的东西,我们回过头来聊聊最朴素的方案。说实话,你真正上手做 NLP 项目时,很可能第一个遇到的就是词级别分词——而且大多数场景下,它就是最简单的那个答案。
1. 空格分词:最直接的切法
把文本按空格劈开,每个连续的非空字符块就是一个 。这在英语等用空格隔开单词的语言里特别自然。我当年刚学 NLP 时觉得这有啥好讲的,不就是 split(" ") 吗?
核心思路:用空白字符(空格、制表符、换行)做分隔符,保留连续字符序列作为 。
# 一个最简单的空格分词器
def whitespace_tokenize(text):
return text.split()
text = "Hello world! This is a test."
tokens = whitespace_tokenize(text)
print(tokens)
# 输出: ['Hello', 'world!', 'This', 'is', 'a', 'test.']
你注意看结果里的 "world!" 和 "test."——标点还粘在单词上。这既是问题,也是后面要聊的标点分词要解决的事情。
空格分词的优缺点
| 优点 | 缺点 |
|---|---|
| 实现极简单,零逻辑 | 标点粘连,词汇表膨胀 |
| 推理速度飞快 | 无法处理连字符、缩写(如 "don't") |
| 无训练成本 | 对中文等无空格语言完全失效 |
| 可解释性高,每个 就是一个原词 | 多语言场景下空格规则不统一 |
我曾经在一个英文新闻分类项目里直接用了空格分词,跑完精度还行。但一看字典大小,接近 50 万——因为那些粘连的标点("Hello?"、"hello?"、"hello!")全成了不同词。嗯,那时候我才意识到小细节能搞出多大麻烦。
2. 标点分词:让标点独立出来
空格分词的问题在于标点被当作单词的一部分。标点分词就是在空格分词的基础上,把标点符号单独切出来。
一个小技巧:大多数标点分词器会维护一个标点集合,用正则或遍历的方式把每个 再拆一遍。
import re
def punctuation_tokenize(text):
# 先用空格分词
tokens = text.split()
result = []
punct_set = set("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
for in tokens:
# 如果 全由标点组成,直接加进去
if all(ch in punct_set for ch in ):
result.append()
continue
# 否则按标点拆分
current = []
for ch in :
if ch in punct_set:
if current:
result.append(''.join(current))
current = []
result.append(ch)
else:
current.append(ch)
if current:
result.append(''.join(current))
return result
text = "Hello world! This is a test."
print(punctuation_tokenize(text))
# 输出: ['Hello', 'world', '!', 'This', 'is', 'a', 'test', '.']
注意:不是所有标点都要切。例如 '('与')'在中英文混排里最好保持原样;URL 里的 '/' 和 '.' 如果切开就破坏了结构。我曾经踩过一个坑:把 IP 地址 "192.168.1.1" 切成了 7 个 ,导致下游模型学不到 IP 的整体含义。
3. 常见标点分词规则
标点分词没有唯一标准,但工业界有一组常规做法。我整理了一张表,可以帮你快速对照:
| 标点类别 | 典型处理 | 示例 |
|---|---|---|
句尾标点 (.!?) | 独立成 | "Hello." → ['Hello', '.'] |
| 逗号、分号、冒号 | 独立成 | "a, b" → ['a', ',', 'b'] |
| 引号、括号 | 左右分别处理 | '"hi"' → ['"', 'hi', '"'] |
| 连字符 | 通常保留为词的一部分 | "high-level" → 不切,保留原样 |
| 撇号(缩写) | 按语言习惯处理 | "don't" → 可切为 ['do', "n't"] 或保留 |
| 数字中的标点 | 保持数字完整性 | "3.14" → 不切,"1,000" 中的逗号可保留或切 |
说白了,规则是死的,场景是活的。你在做英文电商评论分析时,就把 "!!!" 这种连续感叹号单独切出来,模型才能学到感叹号的情绪强度。但做技术文档分词时,把 "C++" 里的加号切开就闹笑话了。
4. 词级别分词的局限性
到这里你可能已经感觉到了:词级别分词对固定语言、固定领域能跑得很顺,但一旦跨语言或者遇到 OOV(未登录词),它就自动崩了。我参与过一个多语言舆情系统,英文部分用空格+标点分词跑得好好的,换成德语复合词 "Donaudampfschifffahrtsgesellschaftskapitän"(多瑙河汽船公司船长)——一个词五十多个字符,字典里根本不可能收录,直接被 OOV 吞掉。
说白了,词级别分词的优缺点很清楚:简单、快、可解释;但词汇表大、OOV 多、对形态丰富的语言不友好。你在原型期、资源受限的设备上、或者目标语言词汇规模可控时,它依然是上佳选择。
本章知识点结构
实用避坑指南:我曾经在做一个英文简历解析系统时,直接用空格+标点分词。结果简历里大量 "2019-Present"、"iOS/Android"、"C++" 等格式,全被拆得七零八落。后来不得不加了一个“保护规则”——正则匹配常见复合词和数字范围,跳过标点拆分。教训就是:没有任何分词器能通用,你必须为你的数据做定制化“打补丁”。
好了,词级别分词的核心就这些。它简单到让你觉得没什么技术含量,但正是这种“低技术门槛”让它成为无数生产系统的第一选择。记住了,当你问“该用哪种分词器”时,先试试空格+标点——如果够用,就别折腾别的。保持简单,保持可维护,这才是工程师的直觉。