共计 2192 个字符,预计需要花费 6 分钟才能阅读完成。
1. 核心概念:理解 NLP 中的 Token
在自然语言处理中,Token 是模型处理文本的最小单位。与字符(单个字母 / 符号)或完整单词不同,Token 通过分词算法(Tokenization)将文本拆分为语义或语法上有意义的片段。例如句子 ”unhappiness” 可能被拆分为 [“un”, “happiness”] 两个 subword tokens。

关键特性:
- 词汇表映射:每个 Token 对应预定义词汇表(vocabulary)中的一个 ID,词汇表大小(vocabulary size)直接影响模型参数量
- 长度影响:输入序列的 Token 数量决定计算量(attention 运算复杂度与序列长度平方成正比)
- 语言差异:中文需要专门的分词处理,而拉丁语系更依赖空格分割
2. 主流分词技术对比
2.1 WordPiece(BERT 采用)
- 优点:
- 通过统计频率合并子词,有效平衡词典大小与 OOV(out-of-vocabulary)问题
- 对常见前缀 / 后缀处理优秀(如 ”##ing” 表示后缀)
- 缺点:
- 需要预训练词典,难以动态扩展
2.2 Byte Pair Encoding (BPE)(GPT 系列采用)
- 优点:
- 从字节级别开始合并,支持所有 Unicode 字符
- 适合多语言混合场景
- 缺点:
- 可能产生不符合语义的拆分(如数字拆分为单个字节)
2.3 Unigram(SentencePiece 默认)
- 优点:
- 支持概率化分词,同一文本可有多种合法切分
- 适合形态丰富的语言(如日语、土耳其语)
- 缺点:
- 训练复杂度高
示例对比(处理 ”unhappily”):
# WordPiece → ["un", "##happ", "##ily"]
# BPE → ["un", "happ", "ily"]
# Unigram → ["unhappy", "ly"] 或 ["un", "happily"]
3. Python 实战:HuggingFace Transformers 全流程
from transformers import AutoTokenizer
# 初始化分词器(以 BERT 为例)tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
text = "Let's explore tokenization!"
# 基础 token 化
inputs = tokenizer(text, return_tensors="pt")
print(inputs.input_ids) # 输出:tensor([[101, 2293, 1005, 1055, 4968, 19204, 999, 102]])
# 处理特殊 token([CLS], [SEP]等)print(tokenizer.special_tokens_map)
# 输出:{'unk_token': '[UNK]', 'sep_token': '[SEP]',...}
# 反向解码
decoded = tokenizer.decode(inputs.input_ids[0])
print(decoded) # 输出:"[CLS] let's explore tokenization! [SEP]"
# 长度限制处理(BERT 最大 512)long_text = "..." * 1000
truncated = tokenizer(long_text, truncation=True, max_length=512)
4. 性能优化技巧
4.1 长文本处理
- 分块策略:
- 按句子或段落切分
-
保持块间重叠(如 256 tokens 的窗口,步长 128)
-
流式处理:
for chunk in split_long_text(text, chunk_size=400): inputs = tokenizer(chunk, return_tensors="pt", truncation=True) # 增量处理...
4.2 内存管理
- 使用
return_overflowing_tokens自动分块 - 对固定模板文本(如问答对)预计算 token 长度
- 禁用不需要的输出(如
return_attention_mask=False)
5. 常见问题解决方案
- 编码错误:
- 现象:处理非 ASCII 文本时出现
[UNK] -
解决:强制 UTF- 8 编码
text.encode("utf-8", "ignore").decode() -
特殊符号冲突:
- 现象:医学文本中的 ”pH” 被拆分为 ”p”, “##h”
-
解决:添加自定义 token
tokenizer.add_tokens(["pH"]) -
数字处理:
- 现象:”123-456″ 被拆分为三个独立数字
-
解决:预处理时添加保护符 “123-456”
-
中英混合:
- 现象:”Python 很棒 ” 被错误分割
-
解决:使用专用多语言分词器(如
bert-base-multilingual) -
位置编码溢出:
- 现象:超过模型最大位置编码(如 512)
- 解决:使用
Longformer等支持长序列的模型架构
6. 定制化思考方向
- 领域适配:医疗领域是否需要合并化学式(如 ”C6H12O6″)?
- 效率权衡:增大词汇表减少 Token 数量 vs 增加模型参数
- 多模态扩展:如何统一处理文本与图像 / 音频的 Token?
开放性问题
- 如何设计动态词汇表应对网络新词(如 ” 元宇宙 ”)?
- 在低资源语言中,如何平衡子词拆分与语义完整性?
- Tokenizer 的差异如何影响模型微调时的知识迁移?
通过本文的实践方案,读者应能有效处理各类 Tokenization 挑战。建议在实际项目中监控 Token 分布(如长度百分位数),这对资源预估至关重要。
正文完
发表至: 自然语言处理
近一天内
