本节摘要:SOURCE 3.3 讲解 ELMo 与 Transformer/BERT:同一词在不同上下文得到不同向量。本节覆盖 [CLS] 分类头、微调流程与 Hugging Face 最小示例。
| 阶段 | 代表 | 表示 |
|---|---|---|
| 静态 | Word2Vec | 一词一向量 |
| 双向 RNN | ELMo | 上下文函数 |
| Transformer | BERT | 多层自注意力 |
SOURCE 3.3:BERT 在大规模语料预训练 MLM+NSP,下游加分类头微调即可。
from transformers import AutoTokenizer, AutoModelForSequenceClassification model_name = "bert-base-chinese" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForSequenceClassification.from_pretrained( model_name, num_labels=3 ) # inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt") # loss = model(**inputs, labels=labels).loss

| 因素 | BERT | TF-IDF+SVM |
|---|---|---|
| 标注需求 | 数百~数千 | 数百即可 |
| 推理延迟 | 十~百 ms/条 | <1 ms |
| 多义词 | 强 | 弱 |
⚠️ 常见坑:max_length 设 512 但评论平均 20 字——浪费算力;应按 P95 长度设。
💡 关键直觉:BERT 是表示演化史的里程碑,但不是所有项目的默认答案。
微调不是「加载模型跑几个 epoch」就结束。关键点之一是文本长度:中文 BERT 的 max_length 一般设 512,但如果你的评论平均只有 30 字,把 max_length 降到 64 可以显著提速且不损失精度;反过来,如果任务文本普遍超过 512,需要截断或分块后聚合。一个稳妥做法是统计训练集长度分布,取 P95 作为 max_length。
学习率是微调里最容易出问题的超参。BERT 微调常用 2e-5 到 5e-5,配合 warmup(前 10% 步数线性升温)与 AdamW;学习率过高会导致训练前期 loss 震荡甚至发散,过低则收敛慢。Batch size 一般 8 到 32,显存不够时优先用梯度累积而不是砍 batch。另外,序列 padding 要用注意力掩码,变长 batch 要统一长度,否则要么浪费算力要么出错。
数据量不足时,冻结策略很有效:冻结底层的 Transformer 层、只训分类头,相当于把 BERT 当特征提取器,可大幅降低过拟合;数据多时再逐层解冻。轻量方案还有 DistilBERT、TinyBERT 这类蒸馏模型,推理快、精度损失小,适合线上部署。不要一上来就全量微调 12 层大模型,先跑通小模型与冻结方案,拿到分数再逐步升级。
评估微调效果要留意「分桶验证」:模型分数高不等于各类都好。BERT 在常见类别上往往表现好,在稀有类别(如「讽刺」)上可能仍然很差。因此微调完成后,除了整体 Macro-F1,还要按类别分别打印精确率、召回率,回到第 5 章的错误分析流程看混淆矩阵。若发现某个小类召回率只有 30%,与其加数据,不如先检查标注一致性——预训练模型对噪声很敏感,标注错的比例超过 10% 时,微调收益会被明显吃掉。
最后是「什么时候不上 BERT」的判断:推理延迟要求 10ms 以内、无 GPU、标注量小于 500、需要完全可解释——这些场景里 TF-IDF 加 SVM 或词向量基线仍是更优解。BERT 解决的是「语义质量」问题,不是「所有问题」,把它放在演化谱系的正确位置上,比盲目追新更重要。
from transformers import AutoTokenizer, AutoModelForSequenceClassification tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese") inputs = tokenizer( ["这条裙子很好看,但发货太慢了"], max_length=64, padding="max_length", truncation=True, return_tensors="pt", ) # model = AutoModelForSequenceClassification.from_pretrained("bert-base-chinese", num_labels=3)
| 超参 | 常见范围 | 注意事项 |
|---|---|---|
| max_length | 按 P95 | 过长浪费算力 |
| 学习率 | 2e-5~5e-5 | 配 warmup |
| batch_size | 8~32 | 显存不足用累积 |
| 冻结策略 | 底层冻结 | 小数据防过拟合 |
| 蒸馏模型 | DistilBERT | 精度小幅损失 |
微调前先做资源预算,避免中途才发现显存不够或工期不匹配。
| 配置 | 显存参考 | 说明 |
|---|---|---|
| 12 层基础模型 | 较大 | 全量微调需较大显存 |
| 冻结底层 | 中等 | 只训分类头 |
| 蒸馏小模型 | 小 | 线上部署友好 |
| 梯度累积 | 视情况 | 小 batch 等效大 batch |
一个省事的策略:先用小模型跑通全流程(数据处理、训练、评估、导出),确认指标合理后再换大模型;这样把「流程 bug」与「模型容量」两类问题分开排查。导出环节注意与推理框架版本对齐,量化时验证精度损失是否在可接受范围,并把对比记录存档。
BERT 不是凭空出现的:它是「表示能力」这一条线演化到顶点时的产物。回看第 3 章整条线——BoW 提供统计共现,TF-IDF 强调稀有词,静态嵌入引入语义相似,BERT 把「上下文」纳入表示——每一代都在解决前一代的遗留缺陷,同时也引入新的工程约束(算力、标注、延迟)。理解这条线,你就能在具体项目里用「代际坐标」定位自己的问题:数据少用统计时代方案,数据中等着重静态嵌入,数据足、语义要求高再考虑 BERT 微调。这也是本教程把表示演进放在模型演进之前的原因。