BERT框架微调阶段实战指南:从数据准备到模型优化

1次阅读
没有评论

共计 2156 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

背景与痛点

BERT 等预训练语言模型的微调阶段是将其应用于下游任务的关键步骤。但在实际应用中,开发者常遇到以下问题:

BERT 框架微调阶段实战指南:从数据准备到模型优化

  • 数据不平衡 :部分类别样本量极少,导致模型偏向多数类
  • 灾难性遗忘 :微调过程中丢失预训练获得的知识
  • 计算资源消耗大 :全参数微调需要大量显存和计算时间
  • 过拟合风险 :小数据集上微调容易出现过拟合现象

技术方案对比

针对不同场景,主流微调策略各有优劣:

  1. 全参数微调 (Full Fine-tuning)
  2. 优点:充分利用模型容量,通常能达到最佳效果
  3. 缺点:计算成本高,需要大量训练数据

  4. Layer-wise 学习率调整

  5. 底层使用较小学习率 (1e-5),顶层使用较大学习率 (5e-5)
  6. 平衡底层通用特征和顶层任务特定特征的学习

  7. Adapter 模块 [1]

  8. 在 Transformer 层间插入小型全连接层
  9. 冻结原始参数,只训练 Adapter 层
  10. 显著减少可训练参数 (约 3 -5%)

  11. Prefix Tuning[2]

  12. 在输入前添加可训练的前缀向量
  13. 保持预训练参数完全冻结
  14. 适合 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()

性能优化技巧

  1. 混合精度训练

    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()

  2. 梯度累积

    accumulation_steps = 4
    
    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

  3. 动态填充

  4. 按 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.

正文完
 0
评论(没有评论)