一、分词的核心粒度
1. 通用定义
粒度 | 英文示例 | 中文示例 | 特点 |
---|---|---|---|
词级 | ["unhappy"] |
["自然语言处理"] |
语义完整,但OOV严重 |
子词级 | ["un", "happy"] |
["深度", "学习"] |
平衡效率与语义(主流) |
字符级 | ["u", "n",...] |
["深", "度",...] |
无OOV但语义模糊 |
2. 中文特殊挑战
- 无空格分隔:需依赖算法或工具切分
- 多粒度语义 :
- 字:
"鱼"
(单字成词) - 词:
"鲨鱼"
(复合语义) - 短语:
"鲸鲨保护区"
(需动态拆分)
- 字:
二、分词算法原理深度剖析
1. BPE(Byte Pair Encoding)
-
核心思想:通过迭代合并最高频的字节对构建子词词汇表
-
训练步骤 :
- 初始化:将所有单词拆分为字符(如
"low"
→l, o, w
) - 统计相邻符号对频率,合并最高频对(如
e
和s
合并为es
) - 重复合并直到达到目标词汇表大小
- 初始化:将所有单词拆分为字符(如
-
中文适配 :
- 将中文按单字初始化,合并高频字组合(如
"中国"
可能合并为整体)
- 将中文按单字初始化,合并高频字组合(如
-
典型应用:GPT系列、DeepSeek
-
示例 :
python# 训练语料:"low", "lower", "newest" # 第1轮合并:e+s → "es"(出现2次) # 第2轮合并:"es"+"t" → "est" # 最终词汇表:["l", "o", "w", "e", "r", "n", "est", ...]
2. WordPiece
-
核心思想:基于概率合并子词(BERT专用)
-
与BPE的区别 :
特性 BPE WordPiece 合并标准 频率最高 概率提升最大 数学基础 计数统计 语言模型概率 标记方式 直接合并 ##
前缀标记延续 -
训练步骤 :
- 初始化同BPE(字符级拆分)
- 计算每对子词合并后的语言模型概率提升:
score = (freq_pair) / (freq_first × freq_second)
- 合并得分最高的子词对
-
中文处理 :
- 强制单字拆分(原始BERT中文版),但可训练自定义合并
-
示例 :
python# 合并计算:"un"+"happy" vs "unh"+"appy" # 选择使得P("unhappy")/P("un")P("happy")最大化的组合
3. Unigram Language Model
-
核心思想:反向删除最不重要的子词(SentencePiece默认)
-
训练步骤 :
- 初始化一个大词汇表(如所有常见子词+字符)
- 迭代删除使得整体语言模型概率损失最小的子词
- 保留最终目标大小的词汇表
-
优势 :
- 可动态调整词汇表大小
- 支持概率采样生成多种分词结果
-
中文示例 :
python# 初始词汇表:["自然", "语言", "自", "然", "语", "言"] # 删除"自"后检查语料概率变化,保留最优组合
4. 中文专属方法
最大匹配算法
- 原理 :基于词典的贪婪匹配
- 正向最大匹配:从首字开始找最长词(
"中国人民银行"
→["中国", "人民", "银行"]
) - 反向最大匹配:从末尾倒推(更准确)
- 正向最大匹配:从首字开始找最长词(
- 缺点:依赖词典质量,无法处理新词
HMM/CRF序列标注
-
原理 :将分词转化为字标签预测(B:词首,M:词中,E:词尾)
python# 输入:"深度学习" → 标签序列:["B", "E", "B", "E"] # 输出:["深度", "学习"]
-
优势 :能学习上下文依赖(如
"下雨天留客天"
的歧义切分)
三、四大模型分词实战
1. BERT家族
- 算法:WordPiece
- 英文 :
"playing"
→["play", "##ing"]
- 中文 :
- 官方版:强制单字
"模型"
→["模", "型"]
- 优化版:部分词保留(需自定义训练)
- 官方版:强制单字
2. GPT/DeepSeek
- 算法:BPE(多语言优化)
- 英文 :
"deepseek"
→["deep", "seek"]
- 中文 :
- 高频词保留:
"中国"
→["中国"]
- 低频词拆分:
"区块链"
→["区块", "链"]
- 高频词保留:
3. 传统Transformer
- 灵活适配:可配置BPE/WordPiece
- 中文建议:使用SentencePiece支持混合粒度
四、中英文对比案例
1. 同一模型对比
python
# BERT英文 vs 中文
英文:tokenizer("unhappy") → ["un", "##happy"]
中文:tokenizer("不开心") → ["不", "##开", "##心"]
# DeepSeek英文 vs 中文
英文:tokenizer("deepseek") → ["deep", "seek"]
中文:tokenizer("深度求索") → ["深度", "求索"] # 若词汇表存在
2. 同一文本不同模型
python
输入:"自然语言处理强大"
- BERT中文:["自", "然", "语", "言", "处", "理", "强", "大"]
- DeepSeek:["自然语言处理", "强大"] # 理想情况
- Jieba+Word级:["自然语言", "处理", "强大"]
五、技术选型指南
算法 | 训练复杂度 | 支持OOV | 多语言友好 | 典型应用场景 |
---|---|---|---|---|
BPE | 中 | 是 | ★★★★★ | GPT、多语言生成 |
WordPiece | 高 | 是 | ★★★☆☆ | BERT、分类任务 |
Unigram LM | 高 | 是 | ★★★★☆ | SentencePiece通用场景 |
最大匹配 | 低 | 否 | ★☆☆☆☆ | 词典驱动的简单系统 |
HMM/CRF | 中 | 部分 | ★★☆☆☆ | 中文精准切分 |
1. 根据任务选择
任务类型 | 推荐方案 | 原因 |
---|---|---|
中文分类/NER | BERT单字+CRF层 | 细粒度捕捉实体边界 |
中英混合生成 | DeepSeek/GPT的BPE | 统一处理多语言 |
小样本中文任务 | 词级+传统模型 | 避免子词拆分带来的噪声 |
2. 根据数据选择
- 数据量小 :
使用预训练分词器(如BERT中文版) - 垂直领域 :
自定义训练子词模型(添加术语如"磷酸奥司他韦"
)
六、进阶优化技巧
1. 中文混合分词
python
# 结合Jieba与子词(适合专业领域)
import jieba
from transformers import AutoTokenizer
text = "量子计算突破性进展"
words = jieba.lcut(text) # ["量子计算", "突破性", "进展"]
tokenizer = AutoTokenizer.from_pretrained("deepseek-ai/deepseek-llm")
tokens = [token for word in words for token in tokenizer.tokenize(word)]
# 输出:["量子", "计算", "突破性", "进展"]
2. 词汇表扩展
python
# 在DeepSeek中新增专业词汇
new_tokens = ["大语言模型", "AGI"]
tokenizer.add_tokens(new_tokens)
print(tokenizer.tokenize("AGI将推动大语言模型发展"))
# 输出:["AGI", "将", "推动", "大语言模型", "发展"]
七、总结图表
维度 | BERT系列 | GPT/DeepSeek | 传统词级 |
---|---|---|---|
中文粒度 | 单字为主 | 动态子词 | 固定词语 |
英文处理 | WordPiece细拆 | BPE合并高频 | 空格分词 |
适合场景 | 理解任务 | 生成/跨语言 | 小样本/规则系统 |
扩展成本 | 需重新训练 | 可动态添加 | 需更新分词词典 |
八、实战:训练自定义分词器
1. 使用SentencePiece训练BPE模型
python
import sentencepiece as spm
# 训练配置
spm.SentencePieceTrainer.train(
input="corpus.txt",
model_prefix="zh_bpe",
vocab_size=30000,
character_coverage=0.9995,
model_type="bpe" # 可改为"unigram"
)
# 加载使用
sp = spm.SentencePieceProcessor()
sp.load("zh_bpe.model")
print(sp.encode_as_pieces("深度学习模型")) # ['▁深度', '学习', '模型']
2. HuggingFace训练WordPiece
python
from tokenizers import BertWordPieceTokenizer
tokenizer = BertWordPieceTokenizer()
tokenizer.train(
files=["corpus.txt"],
vocab_size=30000,
special_tokens=["[UNK]", "[PAD]"]
)
tokenizer.save_model("output_dir")
# 测试
tokenizer.tokenize("气候变化应对") # ['气候', '变化', '应对']
九、关键问题解答
Q1:为什么BERT中文版坚持用单字?
- 确保所有文本可处理(规避分词错误传递)
- 汉字本身携带语义(相比英文字母)
- 可通过Transformer层学习词语组合
Q2:如何选择词汇表大小?
- 英文:通常30K-50K
- 中文:
- 单字级:6K-8K(覆盖常用汉字)
- 子词级:建议20K-50K(平衡效率与语义)
- 多语言:100K+(如DeepSeek的128K)
Q3:处理专业术语(如医学名词)?
-
方法1:添加强制保留词到分词器
python# DeepSeek添加新词 tokenizer.add_tokens(["冠状动脉粥样硬化"])
-
方法2:领域语料重训练BPE模型
附录:快速测试代码
python
# 一键对比三大模型中文分词
from transformers import AutoTokenizer
text = "人工智能的颠覆性创新"
# BERT风格
bert_tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
print("BERT:", bert_tokenizer.tokenize(text)) # 单字拆分
# DeepSeek风格
ds_tokenizer = AutoTokenizer.from_pretrained("deepseek-ai/deepseek-llm")
print("DeepSeek:", ds_tokenizer.tokenize(text)) # 子词混合
# 词级风格(需安装Jieba)
import jieba
print("Jieba词级:", jieba.lcut(text))