共计 1968 个字符,预计需要花费 5 分钟才能阅读完成。
1. 背景痛点
BERT 作为 NLP 领域的里程碑模型,在实际应用中常面临预训练(Pre-training)与微调(Fine-tuning)的认知混淆。两者的核心区别在于:

- 预训练:在海量无标注数据上通过 MLM(Masked Language Model)和 NSP(Next Sentence Prediction)任务学习通用语言表示,耗时耗资源
- 微调:在特定任务(如文本分类)上用少量标注数据调整模型参数,通常只需 1 - 5 个 epoch
新手常遇到的三大拦路虎:
- 文本编码错误:未统一处理中英文混合文本,或未正确截断超长序列
- OOM(内存溢出):盲目使用大 batch_size 导致显存爆炸
- Loss 震荡:学习率设置不当或未做梯度裁剪
2. 技术方案
2.1 数据集预处理
关键步骤:
- 使用
BertTokenizer进行子词切分(Subword Tokenization) - 动态生成
attention_mask标记有效文本区域 - 实现动态 padding——仅在 batch 内统一长度,减少无效计算
推荐数据格式(JSON 示例):
{"text": "这家餐厅服务很棒", "label": 1}
2.2 模型架构改造
基于 BertForSequenceClassification 的二次开发:
- 修改
num_labels参数适配分类类别数 - 添加
dropout=0.1防止小样本过拟合 - 输出层改用
CrossEntropyLoss
2.3 训练优化技巧
- 梯度累积 :通过
accumulation_steps=4模拟更大 batch - 混合精度 :启用
torch.cuda.amp自动管理 FP16/FP32 - 学习率预热:前 10% 训练步线性增大学习率
3. 代码实现
完整训练循环核心代码(带关键注释):
# 初始化分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
# 自定义 Dataset
class MyDataset(Dataset):
def __getitem__(self, idx):
item = self.data[idx]
# 动态生成 input_ids 和 attention_mask
encoded = tokenizer(item['text'],
truncation=True,
max_length=512,
padding=False) # 留到 DataLoader 统一 padding
return {'input_ids': torch.tensor(encoded['input_ids']),
'attention_mask': torch.tensor(encoded['attention_mask']),
'labels': torch.tensor(item['label'])
}
# 混合精度训练
scaler = torch.cuda.amp.GradScaler()
for epoch in range(epochs):
model.train()
for step, batch in enumerate(train_loader):
with torch.cuda.amp.autocast():
outputs = model(**batch)
loss = outputs.loss / accumulation_steps # 梯度累积
scaler.scale(loss).backward()
if (step+1) % accumulation_steps == 0:
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad()
4. 避坑指南
4.1 小样本策略
- 冻结前 8 层 BERT 参数:
model.bert.embeddings.requires_grad_(False) - 只训练最后 2 层和分类头
4.2 梯度爆炸检测
# 在 backward 前添加检查
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
if torch.isnan(loss).any():
print("出现 NaN 值!")
4.3 GPU 监控
安装 nvtop:
sudo apt install nvtop
实时观察显存占用和利用率。
5. 验证环节
在 ChnSentiCorp 情感分类数据集上的测试结果:
| batch_size | 显存占用 | 准确率 |
|---|---|---|
| 8 | 5.2GB | 92.1% |
| 16 | 8.7GB | 91.8% |
| 32 | OOM | – |
延伸思考
- 如何修改模型结构适配多标签分类(如新闻多标签)?
- 当遇到类别不平衡时,损失函数应如何调整?
- 能否用知识蒸馏压缩模型尺寸?
通过这套流程,我在电商评论分类任务上仅用 3000 条数据就达到了 89% 的准确率。建议初次尝试时先用 Colab 的免费 GPU 跑通流程,再迁移到本地环境优化。遇到问题不妨打印出第一个 batch 的数据检查 tokenizer 结果,往往能快速定位问题根源。
正文完
