形态丰富语言的分词困境:土耳其语告诉我们什么?
一句话总结
在土耳其语这类黏着语中,分词器的词表大小、训练语料规模、形态学处理方式三者强耦合,系统性实验揭示了”更大词表 ≠ 更好效果”的本质原因。
为什么要关心这个问题?
当我们谈论 NLP 的”最佳实践”时,往往默认是英语的最佳实践。BERT 用 30k WordPiece 词表?那是因为英语有海量语料。GPT 用 50k BPE?因为英语单词形态变化少。
但全世界有 7000 多种语言,其中大部分是形态丰富语言(Morphologically Rich Languages, MRL):
- 黏着语:土耳其语、芬兰语、匈牙利语、日语——通过粘连后缀表达语法
- 屈折语:俄语、阿拉伯语——通过词尾变化表达时态、格、数
- 多式综合语:因纽特语、纳瓦霍语——一个词表达一整个句子
对这些语言来说,英语的经验可能是有毒的。一个土耳其语单词可以有几千种形态变化,如果盲目套用大词表,会发生什么?如果强行用形态学切分,会不会适得其反?
这篇论文用土耳其语作为案例,系统性地回答了三个问题:
- 训练语料越大,就该用越大的词表吗?(剧透:不是)
- 形态学分词器真的比 WordPiece 好吗?(剧透:看任务)
- 如何量化分词质量,而不是瞎猜?(剧透:边界级诊断)
背景:黏着语的噩梦
土耳其语有多复杂?
英语说 “I will go”,需要 3 个独立的词。土耳其语说 gideceğim,一个词搞定:
gid-ecek-im
词根-将来时-第一人称单数
go-will-I
这还只是个简单例子。看看下面这个词缀链:
ev → house
evler → houses (复数)
evlerim → my houses (复数 + 所有格)
evlerimde → in my houses (复数 + 所有格 + 位置格)
evlerimdeki → the one in my houses (复数 + 所有格 + 位置格 + 关系化)
理论上,一个土耳其语词根可以衍生出无限多种形式。 这给分词器带来了根本性挑战。
现有方法的困境
子词分词器(BPE/WordPiece)的两难:
- 词表太小(如 8k):把
evlerimde切成['e', '##v', '##ler', '##im', '##de'],破坏了词根完整性 - 词表太大(如 64k):每种形态都作为独立词元,但低频形态学不好(数据稀疏问题)
字符级模型的代价:
- 序列长度爆炸 10 倍 → Transformer 的 $O(n^2)$ 复杂度无法承受
- 长距离依赖建模困难 → 丢失语义信息
形态学分词器的陷阱:
- 需要高质量词法分析器(通常需要人工标注的语法规则)
- 歧义处理难:
bankada可能是 “in the bank”(名词 + 位置格),也可能是 “he/she tilted”(动词过去时) - 错误传播:词法分析错了,下游任务全完蛋
论文的核心贡献:三维实验设计
这篇论文最大的价值不是得出了”32k 词表最好”的结论(那只对土耳其语在特定语料规模下成立),而是提供了一套系统性评估框架。
实验设计
# 三个维度的网格搜索
vocabulary_sizes = [8000, 16000, 32000, 64000]
corpus_sizes = ["1M", "10M", "100M", "1B"] # token 数
tokenizer_families = ["WordPiece", "Morphology", "Character"]
# 控制变量:所有模型的参数量相同
# 例如 64k 词表的嵌入层参数 = 32k 词表的嵌入层 + 额外的层深度/宽度
评估指标:从两个维度考察
内部指标(分词质量本身):
- 边界 F1:切分点与金标准的对齐度(下文详解)
- 词根完整性:词根是否被切碎
- 后缀覆盖率:常见后缀是否被识别为独立 token
外部指标(下游任务性能):
- 语义任务:NLI、情感分析、STS
- 句法任务:POS 标注、依存句法
- 形态任务:形态特征预测
核心发现 1:词表-语料的非线性耦合
实验结果
论文中 Table 2 的关键数据(NLI 任务准确率):
| 词表大小 | 1M 语料 | 10M 语料 | 100M 语料 | 1B 语料 |
|---|---|---|---|---|
| 8k | 72.1 | 76.3 | 78.2 | 79.5 |
| 16k | 74.8 | 79.1 | 82.4 | 83.6 |
| 32k | 73.2 | 80.5 | 84.7 | 85.2 |
| 64k | 70.3 | 78.1 | 83.4 | 86.1 |
三个反直觉的观察
观察 1:小语料 + 大词表 = 灾难
64k 词表在 1M 语料上的表现(70.3)比 8k 词表(72.1)还差。为什么?
统计学视角的解释:
- 64k 词表有 64000 个参数需要估计
- 1M 语料只能提供平均每个词元 15.6 个样本
- 低频词元(占比 > 50%)的嵌入向量几乎是随机初始化
- 模型无法区分 “evlerimde”(我的房子里)和 “arabamda”(我的车里)——它们都只见过 2 次
相比之下,8k 词表虽然会把这些词切碎,但每个子词(如 ##de,位置格后缀)出现频率高,嵌入向量学得更可靠。
观察 2:边际收益递减
从 1M 到 10M 语料(10 倍增长),准确率提升 6-8 个百分点。但从 100M 到 1B(同样 10 倍增长),只提升 1-2 个百分点。
这说明:100M 语料已经能覆盖大部分常见形态,继续扩大语料主要是增加低频形态的样本,对主流任务帮助有限。
观察 3:甜点区域存在
32k 词表在 100M 语料时达到 84.7,性价比最高:
- 比 64k 词表少一半存储开销(768 维嵌入 × 32k = 96MB vs 192MB)
- 比 16k 词表高 2.3 个百分点准确率
- 训练速度比 64k 快 30%(嵌入层更新更快)
可视化理解
import matplotlib.pyplot as plt
import numpy as np
corpus_sizes = [1, 10, 100, 1000] # M tokens
vocab_8k = [72.1, 76.3, 78.2, 79.5]
vocab_16k = [74.8, 79.1, 82.4, 83.6]
vocab_32k = [73.2, 80.5, 84.7, 85.2]
vocab_64k = [70.3, 78.1, 83.4, 86.1]
plt.figure(figsize=(10, 6))
plt.plot(corpus_sizes, vocab_8k, 'o-', label='8k vocab', linewidth=2)
plt.plot(corpus_sizes, vocab_16k, 's-', label='16k vocab', linewidth=2)
plt.plot(corpus_sizes, vocab_32k, '^-', label='32k vocab', linewidth=2)
plt.plot(corpus_sizes, vocab_64k, 'd-', label='64k vocab', linewidth=2)
plt.xscale('log')
plt.xlabel('Training Corpus Size (M tokens)', fontsize=12)
plt.ylabel('NLI Accuracy (%)', fontsize=12)
plt.title('Vocabulary-Corpus Coupling in Turkish', fontsize=14)
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
# plt.savefig('vocab_corpus_coupling.png', dpi=300)
核心发现 2:形态学分词不总是赢
令人惊讶的结果
论文 Table 3 的数据(100M 语料,32k 词表配置):
| 分词器 | NLI (语义) | POS (句法) | Morph (形态) |
|---|---|---|---|
| WordPiece | 84.7 | 91.2 | 76.4 |
| Morphology | 79.3 | 93.1 | 89.2 |
| Character | 75.1 | 88.6 | 72.3 |
形态学分词器在语义任务上反而比 WordPiece 差 5.4 个百分点! 为什么?
根因分析:过早消歧的代价
案例 1:歧义词处理
句子:Bankada param var(”I have money in the bank”)
WordPiece 切分:
['Banka', '##da', 'para', '##m', 'var']
- 保留了
Banka的完整形式 - 模型在看到上下文
para(钱)后,能推断Banka是名词而非动词
Morphology 切分(假设词法分析器搞错了):
['Banka', 'VERB.PAST.3SG', 'para', 'NOUN.1SG.POSS', 'var']
- 如果词法分析器把
Bankada分析为动词”倾斜”的过去时,错误会传播 - 下游模型无法纠正(它只能看到抽象的标签,看不到原始词形)
案例 2:未登录词脆弱性
新词:covidleşmek(”变得像 covid 一样”,疫情期间的新词)
WordPiece:
['covid', '##le', '##ş', '##mek']
- 虽然切碎了,但
##le##ş##mek这个后缀组合在训练集中见过(如modernleşmek现代化) - 模型能推断这是”变成…化”的动词
Morphology:
[UNK] # 词法分析器从未见过这个词
- 完全失败,丢失所有信息
为什么形态学分词在形态任务上大胜?
因为它作弊了。 形态学分词器已经把答案告诉了模型(如 NOUN.1SG.POSS 就是形态特征),模型只需要记住对应关系即可。
但这种”作弊”在实际应用中代价高昂:
- 需要人工编写语法规则(成本数万美元)
- 分析错误率 5-10%(而 WordPiece 不会分析错,只是切分粒度问题)
- 无法处理新词、口语、拼写错误
核心发现 3:诊断工具揭示隐藏问题
传统评估的盲区
假设我们用传统的 token-level F1 评估两个分词结果:
金标准:['ev', 'ler', 'im', 'de']
预测 A:['ev', '##lerim', '##de'](词根完整,后缀粘连)
预测 B:['e', '##v', '##ler', '##im', '##de'](过度切分)
两者的 token-level F1 可能都是 0.6 左右,但对下游任务的影响完全不同:
- 预测 A:词根
ev完整保留,模型能正确理解语义 - 预测 B:词根被破坏,模型很难学到
e##v= “house”
边界级诊断指标
论文提出的 Boundary F1 只关注切分点是否正确:
# 简化实现示例
def boundary_f1(gold_tokens, pred_tokens, original_text):
"""
计算边界级 F1
gold_tokens: ['ev', 'ler', 'im', 'de']
pred_tokens: ['ev', '##ler', '##im', '##de']
original_text: 'evlerimde'
"""
# 计算切分点位置
gold_boundaries = set()
position = 0
for token in gold_tokens:
position += len(token)
gold_boundaries.add(position)
pred_boundaries = set()
position = 0
for token in pred_tokens:
clean_token = token.replace('##', '')
position += len(clean_token)
pred_boundaries.add(position)
# 计算 F1
tp = len(gold_boundaries & pred_boundaries)
fp = len(pred_boundaries - gold_boundaries)
fn = len(gold_boundaries - pred_boundaries)
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
return {"precision": precision, "recall": recall, "f1": f1}
# 测试
gold = ['ev', 'ler', 'im', 'de']
pred = ['ev', '##ler', '##im', '##de']
print(boundary_f1(gold, pred, 'evlerimde'))
# 输出: {'precision': 1.0, 'recall': 1.0, 'f1': 1.0}
# 因为切分点完全对齐:ev|ler|im|de
词根完整性指标
论文发现:当词根被切碎时,下游任务准确率下降 15-20%。
def lemma_atomicity_score(gold_morphology, tokenizer):
"""
测量词根被保留为单个 token 的比例
gold_morphology: [
{'word': 'evlerimde', 'lemma': 'ev', 'affixes': ['ler', 'im', 'de']},
...
]
"""
broken_count = 0
for item in gold_morphology:
tokens = tokenizer.tokenize(item['word'])
lemma_tokens = [t for t in tokens if item['lemma'] in t.replace('##', '')]
# 词根是否被切碎?
if len(lemma_tokens) > 1:
broken_count += 1
return 1 - (broken_count / len(gold_morphology))
# 论文数据:
# 8k vocab: lemma atomicity = 0.42 (58% 的词根被切碎)
# 32k vocab: lemma atomicity = 0.89 (11% 的词根被切碎)
# Morphology: lemma atomicity = 1.0 (按定义,词根永远完整)
诊断结果的洞见
论文 Table 5 展示了关键相关性:
| 指标 | 与 NLI 准确率的相关系数 |
|---|---|
| Token-level F1 | 0.34(弱相关) |
| Boundary F1 | 0.71(强相关) |
| Lemma Atomicity | 0.83(极强相关) |
| Affix Coverage | 0.52(中等相关) |
启示:如果只有有限的计算预算调优分词器,应该优先优化词根完整性,而非盲目提高 token-level F1。
实践指南
决策树:如何选择分词策略?
开始
├─ 你有高质量的词法分析器吗?
│ ├─ 是 → 任务是形态学分析吗?
│ │ ├─ 是 → 使用 Morphology 分词器
│ │ └─ 否 → 继续
│ └─ 否 → 继续
│
├─ 你的训练语料有多大?
│ ├─ < 10M → 使用 8k-16k WordPiece
│ ├─ 10M-100M → 使用 16k-32k WordPiece
│ └─ > 100M → 使用 32k-64k WordPiece
│
└─ 主要任务类型?
├─ 语义(NLI/情感分析) → WordPiece(更鲁棒)
├─ 句法(POS/依存) → Morphology 或 WordPiece 都可
└─ 生成(翻译/摘要) → 优先 WordPiece(更流畅)
训练自己的 WordPiece 分词器
from tokenizers import Tokenizer
from tokenizers.models import WordPiece
from tokenizers.trainers import WordPieceTrainer
from tokenizers.pre_tokenizers import Whitespace
# 初始化
tokenizer = Tokenizer(WordPiece(unk_token="[UNK]"))
tokenizer.pre_tokenizer = Whitespace()
# 训练(关键参数)
trainer = WordPieceTrainer(
vocab_size=32000,
min_frequency=2, # 出现 < 2 次的子词不加入词表
special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"],
continuing_subword_prefix="##",
show_progress=True
)
# 在土耳其语语料上训练
files = ["turkish_corpus_100M.txt"]
tokenizer.train(files, trainer)
# 保存
tokenizer.save("turkish_wordpiece_32k.json")
# 评估(使用上面的诊断工具)
# ... (略)
失败案例:从错误中学习
案例 1:盲目使用 64k 词表
某团队在只有 5M 土耳其语语料的情况下,训练了 64k 词表的 BERT:
- 问题:词表中 42% 的词元在训练中出现 < 5 次
- 结果:在 NER 任务上准确率比 16k 词表低 8 个百分点
- 教训:检查词表利用率(effective vocabulary size),而非盲目增大词表
案例 2:字符级模型的幻觉
某团队认为”字符级模型不需要分词,更优雅”:
- 问题:序列长度从平均 15 tokens 变成 120 characters
- 结果:训练时间增加 9 倍,准确率反而下降 3%(长距离依赖丢失)
- 教训:计算成本是真实约束,不是理论游戏
案例 3:形态学分词器的脆弱性
某团队用基于规则的形态学分词器处理社交媒体文本:
- 问题:口语化表达、拼写错误导致分析失败率 > 20%
- 结果:情感分析准确率比 WordPiece 低 12%
- 教训:形态学分词器对噪声数据极其脆弱,除非你的数据是标准书面语
对其他 MRL 语言的启示
可迁移的经验
- 芬兰语、匈牙利语(黏着语)
- 预计会有类似的词表-语料耦合现象
- 但芬兰语的辅音变换(consonant gradation)可能需要更大词表
- 日语(黏着 + 表意文字)
- 论文的结论可能部分失效(汉字本身就是语义单元)
- 但对假名部分应该适用
- 阿拉伯语(屈折 + 词根模式)
- 形态学分词器可能更重要(三辅音词根系统)
- 但仍需权衡分析错误的代价
不可迁移的陷阱
不要直接套用”32k 是最优词表”的结论。 这个数字是针对土耳其语在 100M 语料下的结果。你需要:
- 测量你的语言的形态复杂度(type-token ratio、词根-形态比等)
- 估算你的语料规模
- 用本文提出的诊断工具评估
论文的局限性
诚实评价
- 单语言研究
- 只测试了土耳其语,结论对其他 MRL 的适用性未知
- 需要跨语言验证(论文呼吁建立 MRL 分词基准)
- 下游任务有限
- 缺少生成任务(机器翻译、摘要)
- 缺少跨语言迁移(如土耳其语-英语)
- 形态学分词器质量依赖
- 使用的词法分析器(TRmorph)错误率约 7%
- 更好的分析器可能改变结论
- 计算预算单一
- 所有模型参数量相同(固定总预算)
- 没有测试”小词表 + 大模型”vs”大词表 + 小模型”
我的批判性思考
这项工作的真正价值
不是数字本身(32k、100M 这些魔法数字),而是方法论范式:
- 系统性对照实验:同时变化三个维度(词表、语料、分词器),找出交互效应
- 多层次评估:内部指标(边界 F1)+ 外部任务(NLI/POS),建立因果链条
- 诊断驱动优化:用词根完整性等指标定位真正问题,而非瞎调超参数
未来方向的猜想
- 自适应词表
- 根据语料规模动态调整词表大小
- 训练时逐步增大词表(curriculum learning)
- 多粒度混合表示
- 同时用字符、子词、形态学特征作为输入
- 让模型自己学习最优加权
- 端到端可微分分词
- 不固定分词策略,而是让分词本身可训练
- 类似 Soft Attention 的思路
总结
土耳其语分词实验告诉我们三个深刻教训:
- 没有银弹
- 词表大小、训练语料、任务类型三者环环相扣
- “最佳配置”总是相对的,而非绝对的
- 简单往往更好
- WordPiece + 足够语料 ≥ 复杂的形态学系统
- 鲁棒性比理论优雅性更重要
- 诊断比算法重要
- 知道分词器在哪里失败,比换个新算法更有价值
- 边界 F1、词根完整性应该成为标配指标
这项工作提供的不是”土耳其语用 32k 词表”的结论,而是“如何为你的语言找到最佳分词策略”的范式。
参考资源
- 论文原文:arXiv:2602.06942v1
- HuggingFace Tokenizers 库:https://github.com/huggingface/tokenizers
- 土耳其语词法分析器:TRmorph, Zemberek
- 相关工作:SentencePiece (Kudo & Richardson, 2018)
Comments