基于bert-base-chinese训练新分词器时如何固定字符索引?
解决BERT中文医疗分词器与原模型索引不匹配问题
问题根源
使用train_new_from_iterator训练新分词器时,会基于训练语料重新训练WordPiece模型、完全重构词汇表,导致原有字符的索引被打乱,无法与预训练的bert-base-chinese模型参数匹配。
解决方案:保留原词汇表索引,扩展新词汇
核心思路是复用原tokenizer的全部词汇及索引,仅在词汇表末尾添加医疗领域新词汇,确保原有字符索引完全一致,同时扩展模型嵌入层适配新词汇。
步骤1:加载原tokenizer与模型,保存原词汇表
from transformers import AutoTokenizer, AutoModelForMaskedLM # 加载预训练组件 tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese") model = AutoModelForMaskedLM.from_pretrained("bert-base-chinese") # 保存原词汇表(字符:索引)及原词汇表大小 original_vocab = tokenizer.vocab.copy() original_vocab_size = len(original_vocab)
步骤2:准备并过滤医疗领域新词汇
从训练语料中提取需添加的新词汇,过滤掉原词汇表已存在的内容:
# 示例医疗领域新词汇,实际需从语料中提取 new_medical_tokens = ['健康', '医学', '试剂盒', '心内科', 'CT扫描'] # 过滤原词汇表已有的token new_medical_tokens = [token for token in new_medical_tokens if token not in original_vocab]
步骤3:扩展词汇表,保留原索引
将新词汇添加到原词汇表末尾,索引从原词汇表大小开始递增:
# 构建扩展后的词汇表 extended_vocab = original_vocab.copy() for idx, token in enumerate(new_medical_tokens, start=original_vocab_size): extended_vocab[token] = idx
步骤4:初始化新tokenizer并更新配置
基于原tokenizer的配置,替换为扩展后的词汇表:
# 创建新tokenizer(复用原配置) tokenizer_new = AutoTokenizer.from_pretrained("bert-base-chinese") # 更新词汇表及反向映射 tokenizer_new.vocab = extended_vocab tokenizer_new.ids_to_tokens = {v: k for k, v in extended_vocab.items()} # 更新词汇表大小 tokenizer_new.vocab_size = len(extended_vocab) # 若需添加新特殊token,使用add_special_tokens自动处理(不会影响原索引) # tokenizer_new.add_special_tokens({'additional_special_tokens': ['[MED]']})
步骤5:验证索引一致性
print(tokenizer.vocab['先']) # 输出原索引:1044 print(tokenizer_new.vocab['先']) # 输出相同索引:1044 print(tokenizer_new.vocab['健康']) # 输出原词汇表大小+0,例如21128(bert-base-chinese原vocab_size为21128)
步骤6:扩展模型嵌入层适配新词汇
原模型的嵌入层大小为原词汇表大小,需扩展嵌入层以容纳新词汇:
# 调整模型嵌入层大小,新词汇的嵌入参数会被随机初始化,后续训练中优化 model.resize_token_embeddings(len(tokenizer_new))
关键说明
- 此方法确保原有字符的索引与预训练模型完全匹配,原模型的预训练嵌入参数可直接复用,避免了重新训练整个词汇表导致的索引混乱。
- 若需更精准的医疗领域子词分割,可先对医疗语料进行子词统计,再将高频子词添加到词汇表,而非直接添加完整词汇。
内容的提问来源于stack exchange,提问作者Katelynn ruan
相关产品推荐
相关产品推荐

