用transformers+datasets+trainer微调最稳妥,但必须手动对齐tokenizer与模型的vocab和特殊token,否则会静默崩溃或loss不降;加载时需统一pretrained_model_name_or_path,扩展词汇后须resize_token_embeddings并校验vocab_size一致。

直接说结论:用 transformers + datasets + Trainer 是当前最稳妥的微调路径,但必须手动对齐 tokenizer 与模型的 vocab 和特殊 token,否则训练会静默崩溃或 loss 不降。
加载模型和 tokenizer 时 vocab 不一致怎么办
常见错误现象:Trainer 训练几轮后 loss 突然飙升、NaN,或验证集准确率卡在随机水平。根本原因是 AutoTokenizer.from_pretrained("bert-base-uncased") 和 AutoModelForSequenceClassification.from_pretrained("bert-base-uncased") 虽然名字一样,但如果你中途修改过 tokenizer(比如加了新词),而没同步更新模型 embedding 层,就会导致 embedding lookup 错位。
- 始终用同一
pretrained_model_name_or_path加载二者,不要混用本地路径和 hub 名称 - 如果必须扩展词汇表,先调用
tokenizer.add_tokens([...]),再用model.resize_token_embeddings(len(tokenizer)) - 检查
tokenizer.vocab_size和model.config.vocab_size是否相等,不等就出问题
数据预处理中 truncation 和 padding 的坑
BERT 输入长度固定,但原始文本长短不一。直接 tokenizer(text, truncation=True, padding=True) 看似简单,实际容易埋雷:
-
padding=True默认 pad 到 batch 内最长句,但Trainer会自动 collate 成固定长度(如max_length=128),两者冲突会导致 batch 中部分样本被意外截断 - 正确做法是显式指定
max_length并设truncation="longest_first",同时padding="max_length" - 分类任务中,
return_tensors="pt"必须加,否则Trainer无法识别 tensor 类型 - 别忘了把 label 映射成 int,
datasets.Dataset.map()里用lambda x: {"label": label_to_id[x["label"]]}
Trainer 训练时 learning_rate 和 warmup_steps 设多少
不是越大越好,也不是照搬论文。BERT 微调本质是小步快跑,过大的 lr 会让早期梯度爆炸,warmup 不足则 embedding 层跟不上。
- 推荐起始
learning_rate=2e-5(5e-5仅适用于极小数据集或二分类) -
warmup_ratio=0.1比固定warmup_steps更鲁棒,Trainer支持直接设warmup_ratio - batch size 优先用
per_device_train_batch_size=16,显存不够再降到 8;增大 batch 不如多做 gradient accumulation -
load_best_model_at_end=True和metric_for_best_model="eval_accuracy"必须配对,否则早停无效
保存和推理时 model.eval() 和 tokenizer.pad_token_id 缺一不可
训完模型,直接 model(**inputs) 可能报错 IndexError: index out of range in self 或输出全零——大概率是推理时没关 dropout,或 padding token 被当成有效输入。
- 推理前务必
model.eval(),且禁用torch.no_grad()外的任何梯度操作 - 确保 tokenizer 有
pad_token_id:若为None,需tokenizer.pad_token = tokenizer.eos_token或tokenizer.add_special_tokens({"pad_token": "[PAD]"}) - 预测时用
tokenizer(..., return_tensors="pt", padding=True, truncation=True, max_length=128),别漏参数 - 模型保存用
trainer.save_model("my_bert_finetuned"),别只存state_dict,否则丢失 config 和 tokenizer 绑定信息
真正麻烦的不是写几行代码,而是 tokenizer 和 model 之间那些看不见的对齐细节——比如 cls_token_id 在 tokenizer 里是 101,但在你改过的 vocab 里挪到了 102,而 model 还在按老索引查表。这种错不会报错,只会让结果不可复现。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











