基座模型用通用语料训练(网页、书籍、代码),但你的业务数据可能是医疗、法律、金融——领域词不在词表里,模型答不准。持续预训练(Continued Pre-Training)用领域数据继续训练基座模型,扩展词表和专业能力。
持续预训练 vs 微调
| 维度 | 持续预训练 | 微调(SFT/LoRA) |
|---|---|---|
| 数据量 | 10M-1B tokens | 10K-1M tokens |
| 训练目标 | MLM/CLM(无监督) | 指令跟随(有监督) |
| 词表 | 可扩展 | 不扩展 |
| 训练时间 | 数天到数周 | 数小时到数天 |
| 效果 | 领域知识内化 | 指令跟随能力 |
持续预训练是"给模型补知识",微调是"教模型怎么用知识回答问题"。
数据准备
领域数据需要跟预训练数据同分布(纯文本,无指令格式):
# 错误:带指令格式(这是 SFT 数据){"instruction":"解释心绞痛","input":"","output":"..."}# 正确:纯文本(跟预训练一样){"text":"心绞痛是由于冠状动脉供血不足引起的胸痛...\n\n病理机制:..."}数据量建议:
| 领域 | 最小数据量 | 推荐数据量 |
|---|---|---|
| 医疗 | 100M tokens | 1B tokens |
| 法律 | 50M tokens | 500M tokens |
| 金融 | 50M tokens | 500M tokens |
| 代码 | 100M tokens | 2B tokens |
训练配置
fromtransformersimportTrainer,TrainingArguments training_args=TrainingArguments(per_device_train_batch_size=4,gradient_accumulation_steps=32,# 有效 batch = 4×32 = 128max_seq_len=4096,learning_rate=1e-5,# 持续预训练用较小的 LRweight_decay=0.01,num_train_epochs=3,# 领域数据少的话 1-2 轮就够了save_steps=1000,fp16=False,bf16=True,# 昇腾NPU 推荐 bf16deepspeed="ds_config_zero3.json",# ZeRO-3 节省显存...)trainer=Trainer(model=model,args=training_args,train_dataset=dataset)trainer.train()学习率关键:持续预训练的 LR 应该比预训练小 10-100×。预训练 LR 通常是 1e-4 到 3e-4,持续预训练用 1e-5 到 3e-5。
词表扩展
领域专有词(如医疗的"阿司匹林肠溶片")被拆成多个 subword,模型难以理解。扩展词表:
fromsentencepieceimportSentencePieceTrainer# 1. 在领域数据上训练新词表SentencePieceTrainer.train(input="domain_corpus.txt",model_prefix="domain_bpe",vocab_size=5000,# 扩展 5000 个新词model_type="bpe",)# 2. 扩展模型词表model.resize_token_embeddings(old_vocab_size+5000,pad_to_multiple_of=64,# 对齐到 64(Cube 要求))扩展后的新词 embedding 用旧词 embedding 的平均值初始化,然后在持续预训练中更新。
防止灾难性遗忘
持续预训练容易让模型忘记通用能力。解决方案:
方案 1:混合通用数据
# 90% 领域数据 + 10% 通用数据mixed_dataset=concatenate_datasets([domain_dataset.shuffle().select(range(int(0.9*len(domain_dataset)))),general_dataset.shuffle().select(range(int(0.1*len(domain_dataset))),])方案 2:较小的学习率
LR=1e-5 比 LR=1e-4 的遗忘少 30-50%。
方案 3:LoRA 持续预训练
# 只用 LoRA 更新参数,原模型冻结model=get_peft_model(model,lora_config)# 持续预训练时只更新 LoRA 参数原模型参数不变,不会遗忘。
昇腾NPU 上的加速
持续预训练数据量大(10M-1B tokens),需要分布式训练:
# 8 卡 Atlas 800I A2training_args=TrainingArguments(...,deepspeed="ds_config_zero3.json",# ZeRO-3tf32=True,# 昇腾NPU 支持 TF32(等价 fp32 精度,fp16 速度))ZeRO-3 把优化器状态、梯度、参数都分片到各卡:
8 卡,Llama2-7B: 无 ZeRO: 每卡显存 76GB(OOM) ZeRO-1: 每卡显存 42GB ZeRO-2: 每卡显存 28GB ZeRO-3: 每卡显存 18GB ← 8 卡都能跑效果评估
持续预训练后,在领域任务和通用任务上分别评估:
# 领域任务(医疗问答)domain_score=evaluate(model,medical_qa_dataset)# 通用任务(MMLU)general_score=evaluate(model,mmlu_dataset)print(f"领域得分:{domain_score:.1f}(原模型: 45.2)")print(f"通用得分:{general_score:.1f}(原模型: 78.5)")好的持续预训练:领域得分提升 10-30%,通用得分下降 ❤️%。
持续预训练是给基座模型"补知识"的重要手段。关键是数据质量(跟预训练同分布)、学习率(小一点)、防止遗忘(混合通用数据)。在昇腾NPU上用 ZeRO-3 做分布式训练,8 卡能跑 7B 模型。仓库在这里:
https://atomgit.com/cann/torch_npu