当领域词汇表新增 8000 个中文医学术语时,如何扩展 tokenizer 并保证嵌入层初始化?

解读

面试官想验证三件事:

  1. 你是否理解中文医学术语对原有 tokenizer 的“切分灾难”——即被拆成无意义单字或子词,导致语义漂移。
  2. 你是否掌握无痛扩展方案:不重新预训练,却能将新词整体纳入词汇表,并同步完成嵌入层形状变更与初始化
  3. 你是否能在国内合规场景下,保证模型版本可追溯、服务可热更新、数据不出境
    回答时要体现“LLMOps 闭环”思维:从语料清洗、词频统计、tokenizer 重训、模型热插拔、到线上灰度回滚,每一步都要给出可落地脚本级细节

知识点

  1. SentencePiece 中文医学语料二次训练:–input_sentence_size、–shuffle_input_sentence、–character_coverage=0.995,防止过度拆字。
  2. Tokenizer 合并策略:老 vocab 保留原始 id,新词追加到尾部,id 连续不跳号,避免下游 embedding 矩阵错位。
  3. Embedding 层形状扩展
    new_emb = torch.nn.Embedding(old_weight.size(0)+8000, hidden_size)
    new_emb.weight.data[:old_weight.size(0)] = old_weight
    新 8000 行用高斯初始化σ=1e-4,防止梯度爆炸。
  4. 国内合规细节
    • 训练语料必须脱敏,剔除患者隐私;
    • 若使用境外开源词表,需做哈希校验保证未被投毒;
    • 最终模型文件存于境内私有云 OSS,调用链路走VPC 内网
  5. LLMOps 热更新
    • 使用TorchScript + Redis 配置中心,embedding 权重以safetensors格式分片存储;
    • 上线采用影子流量双跑 24h,指标漂移>1% 自动回滚。

答案

步骤一:语料准备
中国知网医学期刊万方医学术语词典拉取近 3 年文献,经隐私脱敏脚本(正则剔除姓名、手机号、医院名)后得到 2.3 G 纯文本,用jieba 医学自定义词典预切分,统计 8000 个出现频次≥50 的术语作为新增词表。

步骤二:Tokenizer 重训
基于原模型目录下的tokenizer.model(SentencePiece),执行:
spm_train –input=new_corpus.txt –model_prefix=med –vocab_size=原vocab_size+8000 –character_coverage=0.995 –model_type=unigram –control_symbols=<pad>,<unk>,<cls>,<sep>,<mask>
得到med.modelmed.vocab后,用官方脚本spiece_to_hf_tokenizer.py转成 HuggingFace 格式,保证token id 从 32000 开始连续追加

步骤三:模型权重扩展

  1. 加载原 pytorch_model.bin,提取model.embed_tokens.weightlm_head.weight
  2. 新建 Embedding 层:
    old_num, dim = embed_tokens.weight.shape
    new_embed = nn.Embedding(old_num + 8000, dim, dtype=torch.float16)
    new_embed.weight.data[:old_num] = embed_tokens.weight.data
    new_embed.weight.data[old_num:] = torch.normal(0, 1e-4, size=(8000, dim), dtype=torch.float16)
  3. 同步扩展lm_head( tied embedding 场景下共享权重,需同样追加 8000 行);
  4. 保存为model-00001-of-00002.safetensors,使用transformers.modeling_utils.load_sharded_checkpoint验证加载无报错。

步骤四:继续预训练 & 微调
采用LoRA + DeepSpeed Zero-28×A100 40G(境内阿里云 ecs.ebmgn7t 裸金属)继续预训练 2 个 epoch,lr=5e-5,warmup=3%,global_batch=256。训练脚本中加入token_loss_mask:对新 8000 个 token 的 loss 乘以 2.0,加速收敛。

步骤五:上线与监控

  1. 通过FlagAI-LLMOps平台打 Tag:v1.3.0-med-token-8k;
  2. 镜像推送到阿里云 ACR 华北 2 私有仓库,部署Triton Inference Server,开启decoupled mode
  3. 线上灰度 5% 流量,核心指标:
    • 医学术语整词召回率提升≥6%
    • 首 token 延迟 P99 增加≤8 ms
    • GPU 显存占用增加≤1.2 GB
      连续 24h 未触发回滚阈值,则全量发布。

拓展思考

  1. 如果新增术语达到8 万规模,上述高斯初始化会导致语义空间稀释,可引入对比学习:把新词在医学语料中的上下文向量作为正样本,随机采样其他词做负样本,warmup 阶段用 InfoNCE loss 预对齐
  2. 当业务要求多租户隔离(医院 A 与医院 B 术语冲突),可采用动态 vocab 路由:底层仍共享主模型,上层通过prefix token(如 <@A>、<@B>)把不同租户的新词映射到独立的 embedding shard,实现一份权重、多份视图,节省 30% 显存。
  3. 国内数据跨境监管趋严,若未来需引入海外医学文献,必须走数据出境安全评估。可在境内建立只读镜像,用差分隐私(ε=1.0)对词频加噪,再合并到 tokenizer 训练,既合规又不显著降低术语命中率。