共计 2156 个字符,预计需要花费 6 分钟才能阅读完成。
背景与痛点
BERT 等预训练语言模型的微调阶段是将其应用于下游任务的关键步骤。但在实际应用中,开发者常遇到以下问题:

- 数据不平衡 :部分类别样本量极少,导致模型偏向多数类
- 灾难性遗忘 :微调过程中丢失预训练获得的知识
- 计算资源消耗大 :全参数微调需要大量显存和计算时间
- 过拟合风险 :小数据集上微调容易出现过拟合现象
技术方案对比
针对不同场景,主流微调策略各有优劣:
- 全参数微调 (Full Fine-tuning)
- 优点:充分利用模型容量,通常能达到最佳效果
-
缺点:计算成本高,需要大量训练数据
-
Layer-wise 学习率调整
- 底层使用较小学习率 (1e-5),顶层使用较大学习率 (5e-5)
-
平衡底层通用特征和顶层任务特定特征的学习
-
Adapter 模块 [1]
- 在 Transformer 层间插入小型全连接层
- 冻结原始参数,只训练 Adapter 层
-
显著减少可训练参数 (约 3 -5%)
-
Prefix Tuning[2]
- 在输入前添加可训练的前缀向量
- 保持预训练参数完全冻结
- 适合 few-shot 场景
核心实现 (PyTorch 示例)
数据预处理
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
def preprocess(text, label, max_len=128):
inputs = tokenizer(
text,
max_length=max_len,
padding='max_length',
truncation=True,
return_tensors='pt'
)
return {'input_ids': inputs['input_ids'].squeeze(0),
'attention_mask': inputs['attention_mask'].squeeze(0),
'labels': torch.tensor([label])
}
模型定义
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained(
'bert-base-uncased',
num_labels=2
)
# 只微调顶层 3 层
for param in model.bert.encoder.layer[:-3].parameters():
param.requires_grad = False
训练循环
from transformers import AdamW, get_linear_schedule_with_warmup
optimizer = AdamW(filter(lambda p: p.requires_grad, model.parameters()),
lr=2e-5
)
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps=100,
num_training_steps=1000
)
for batch in train_loader:
inputs = {'input_ids': batch['input_ids'],
'attention_mask': batch['attention_mask'],
'labels': batch['labels']
}
outputs = model(**inputs)
loss = outputs.loss
loss.backward()
optimizer.step()
scheduler.step()
optimizer.zero_grad()
性能优化技巧
-
混合精度训练
from torch.cuda.amp import GradScaler, autocast scaler = GradScaler() with autocast(): outputs = model(**inputs) loss = outputs.loss scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() -
梯度累积
accumulation_steps = 4 if (i + 1) % accumulation_steps == 0: optimizer.step() optimizer.zero_grad() -
动态填充
- 按 batch 内最长样本进行填充,减少计算浪费
避坑指南
- 学习率设置不当 :BERT 微调通常使用 2e- 5 到 5e- 5 的小学习率
- 训练 epoch 过多 :文本分类任务通常 3 - 5 个 epoch 足够
- 未冻结底层参数 :小数据场景应冻结部分底层参数
- 忽略类别不平衡 :使用加权损失函数或过采样技术
实践建议
根据业务场景选择合适的微调策略:
- 大数据场景 :全参数微调 + 学习率分层
- 小数据场景 :Adapter/Prefix Tuning+ 数据增强
- 领域适应 :先进行领域预训练再进行任务微调
[1] Houlsby N, et al. Parameter-Efficient Transfer Learning for NLP. ICML 2019.
[2] Li XL, Liang P. Prefix-Tuning: Optimizing Continuous Prompts for Generation. ACL 2021.
正文完
