3.3 上下文嵌入 BERT


3.3 上下文嵌入 BERT

本节摘要:SOURCE 3.3 讲解 ELMo 与 Transformer/BERT:同一词在不同上下文得到不同向量。本节覆盖 [CLS] 分类头、微调流程与 Hugging Face 最小示例。

读前必看

  1. 解释自注意力如何生成动态表示
  2. 描述 BERT 微调文本分类步骤
  3. 权衡推理成本与精度收益

一、从 ELMo 到 BERT

阶段 代表 表示
静态 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

loss = model(**inputs, labels=labels).loss

三、工程取舍

因素 BERT TF-IDF+SVM
标注需求 数百~数千 数百即可
推理延迟 十~百 ms/条 <1 ms
多义词

⚠️ 常见坑:max_length 设 512 但评论平均 20 字——浪费算力;应按 P95 长度设。

💡 关键直觉:BERT 是表示演化史的里程碑,但不是所有项目的默认答案。

核心回顾

  • 上下文嵌入解决一词多义
  • BERT:预训练 + 分类头微调
  • Hugging Face 生态降低落地门槛
  • 部署前评估延迟与 GPU 成本

纵深: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 精度小幅损失

BERT 微调的资源与速度预算

微调前先做资源预算,避免中途才发现显存不够或工期不匹配。

配置 显存参考 说明
12 层基础模型 较大 全量微调需较大显存
冻结底层 中等 只训分类头
蒸馏小模型 线上部署友好
梯度累积 视情况 小 batch 等效大 batch

一个省事的策略:先用小模型跑通全流程(数据处理、训练、评估、导出),确认指标合理后再换大模型;这样把「流程 bug」与「模型容量」两类问题分开排查。导出环节注意与推理框架版本对齐,量化时验证精度损失是否在可接受范围,并把对比记录存档。

与演化史主线的关系

BERT 不是凭空出现的:它是「表示能力」这一条线演化到顶点时的产物。回看第 3 章整条线——BoW 提供统计共现,TF-IDF 强调稀有词,静态嵌入引入语义相似,BERT 把「上下文」纳入表示——每一代都在解决前一代的遗留缺陷,同时也引入新的工程约束(算力、标注、延迟)。理解这条线,你就能在具体项目里用「代际坐标」定位自己的问题:数据少用统计时代方案,数据中等着重静态嵌入,数据足、语义要求高再考虑 BERT 微调。这也是本教程把表示演进放在模型演进之前的原因。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U