BERT预训练语言模型实战:从零构建到微调避坑指南

1次阅读
没有评论

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

image.webp

背景痛点

初学者在接触 BERT 时往往会遇到几个常见问题:

  • 硬件资源不足:BERT 模型参数量大,对显存要求高,普通消费级显卡难以直接运行
  • 微调效果不稳定:同一套代码在不同数据集上表现差异大,训练过程难以复现
  • 模型选择困难:BERT 变体众多,不清楚何时该选用哪种预训练模型
  • 超参数敏感:学习率、batch size 等参数对最终效果影响显著但缺乏调参指导

技术对比

主流的预训练语言模型各有特点:

  1. BERT (2018)
  2. 优势:首个双向 Transformer 模型,开源实现成熟
  3. 缺点:最大长度 512,计算资源消耗大

  4. RoBERTa (2019)

  5. 改进:更长的训练步数、更大的 batch size
  6. 适用:需要更高精度的场景

  7. ALBERT (2019)

  8. 创新:参数共享技术大幅减少参数量
  9. 适用:资源受限的移动端或嵌入式设备

核心实现

BERT 架构解析

BERT 的核心是 Transformer 编码器堆叠,其核心组件包括:

  • 多头注意力机制:允许模型同时关注不同位置的语义信息
  • 位置编码:通过正弦函数为输入添加位置信息
  • 层归一化:稳定深层网络的训练过程

BERT 预训练语言模型实战:从零构建到微调避坑指南

加载预训练模型

使用 Hugging Face Transformers 库可以轻松加载 BERT:

from transformers import BertModel, BertTokenizer

# 初始化 tokenizer 和 model
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

# 示例文本处理
inputs = tokenizer("Hello world!", return_tensors="pt")
outputs = model(**inputs)

完整微调示例

数据准备

from torch.utils.data import Dataset, DataLoader

class TextDataset(Dataset):
    def __init__(self, texts, labels, tokenizer, max_len):
        self.texts = texts
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_len = max_len

    def __len__(self):
        return len(self.texts)

    def __getitem__(self, idx):
        text = str(self.texts[idx])
        label = self.labels[idx]

        encoding = self.tokenizer.encode_plus(
            text,
            add_special_tokens=True,
            max_length=self.max_len,
            padding='max_length',
            truncation=True,
            return_attention_mask=True,
            return_tensors='pt'
        )

        return {'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'labels': torch.tensor(label, dtype=torch.long)
        }

训练循环

from transformers import BertForSequenceClassification, AdamW
from transformers import get_linear_schedule_with_warmup

# 初始化模型
model = BertForSequenceClassification.from_pretrained(
    'bert-base-uncased', 
    num_labels=num_classes
)

# 优化器设置
optimizer = AdamW(model.parameters(), lr=2e-5, correct_bias=False)

# 学习率预热
scheduler = get_linear_schedule_with_warmup(
    optimizer,
    num_warmup_steps=100,
    num_training_steps=len(train_loader) * epochs
)

# 训练循环
for epoch in range(epochs):
    model.train()
    for batch in train_loader:
        optimizer.zero_grad()

        # 前向传播
        outputs = model(input_ids=batch['input_ids'],
            attention_mask=batch['attention_mask'],
            labels=batch['labels']
        )

        loss = outputs.loss
        loss.backward()
        optimizer.step()
        scheduler.step()

性能优化技巧

梯度累积

当显存不足时,可以通过多次小 batch 的前向传播累积梯度,再统一更新参数:

accumulation_steps = 4

for i, batch in enumerate(train_loader):
    # 前向传播和损失计算
    loss = model(batch).loss

    # 梯度归一化
    loss = loss / accumulation_steps

    # 反向传播
    loss.backward()

    # 累积足够 steps 后更新参数
    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

混合精度训练

使用 AMP(Automatic Mixed Precision)可以显著减少显存占用并加速训练:

from torch.cuda.amp import GradScaler, autocast

scaler = GradScaler()

with autocast():
    outputs = model(input_ids, attention_mask, labels)
    loss = outputs.loss

scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()

避坑指南

类别不平衡处理

  1. 加权交叉熵:为少数类别分配更大的权重
  2. 过采样 / 欠采样:调整数据分布使其均衡
  3. Focal Loss:降低易分类样本的权重

层学习率衰减

BERT 的不同层应使用不同的学习率,底层使用较小学习率:

param_optimizer = list(model.named_parameters())
no_decay = ['bias', 'LayerNorm.weight']
optimizer_grouped_parameters = [
    {
        'params': [p for n, p in param_optimizer 
                  if not any(nd in n for nd in no_decay)],
        'weight_decay': 0.01
    },
    {
        'params': [p for n, p in param_optimizer 
                  if any(nd in n for nd in no_decay)],
        'weight_decay': 0.0
    }
]

延伸思考

  1. 如何通过知识蒸馏将 BERT 大模型压缩为轻量级模型?
  2. 在 Colab 等免费平台上运行 BERT 有哪些资源优化技巧?
  3. 不同领域的文本 (如医疗、法律) 是否需要特殊的预训练策略?

建议读者在 Colab 上尝试复现本文示例,并分享在不同数据集上的实验结果对比。通过实践可以更深入地理解 BERT 的微调过程和优化方法。

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