Oasis's Cloud

一个人的首要责任,就是要有雄心。雄心是一种高尚的激情,它可以采取多种合理的形式。
—— 《一个数学家的辩白》

从零构建大模型(六)

针对分类的微调

作者:oasis


针对分类微调的整体步骤:

  1. 选择微调策略
    • 先判断任务是否属于“需要把文本映射到一组固定类别”的场景,例如垃圾消息检测、情感分析等;若是,则采用分类微调而非指令微调。
    • 原因:分类微调在遇到明确类别标签时更直接、计算也更轻量。
  2. 准备数据集
    • 下载并清洗数据,将原始类别标签(如“ham”“spam”)映射为连续整数。
    • 对数据集做下采样、随机拆分(常见比例 70% 训练 / 10% 验证 / 20% 测试)。
    • 原因:保证类别平衡且有独立的评估集,避免后续过拟合难以发现。
  3. 初始化带有预训练权重的模型
    • 用与预训练时相同的配置创建 GPT 等模型,再把官方权重加载进来。
    • 原因:保留语言模型已学到的通用语言表示,再在此基础上做少量调整即可适应新任务。
  4. 创建数据加载器
    • 将所有消息填充到数据集中最长消息的长度或批次长度
    • 原因:避免信息丢失,而从降低模型性能
  5. 替换输出层(添加分类头)
    • 将原始输出层替换成适配新类别数的小型层;例如二分类就用 2 个输出节点。
    • 可选择只微调最后几层或全部层,通常只微调靠近输出的层既有效又省计算。
    • 原因:输出层负责把深层语义映射为类别分数,新任务需要新的映射;局部微调在保持效果的同时降低显存与时间开销。
  6. 定义损失函数与评估指标
    • 对每个批量计算交叉熵损失,并在批量上取 softmax 后求 argmax 得到类别预测,从而统计准确率。
    • 原因:交叉熵是可微且与准确率优化目标一致的替代函数;准确率直观反映分类性能。
  7. 在有监督数据上微调模型
    • 设置优化器、训练轮数,写训练循环:前向计算、反向传播、参数更新;每轮后评估训练/验证损失与准确率。
    • 原因:通过最小化交叉熵损失让模型把更多注意力集中在正确类别,同时用验证集监控过拟合。
  8. 评估与测试
    • 微调结束后,在测试集上计算最终准确率、混淆矩阵等指标,确认模型泛化能力。
    • 原因:独立测试集能给出对未知数据的可靠评估。
  9. 部署与复用
    • 将模型保存(torch.save)以便后续加载、推理或再微调。
    • 原因:便于集成到实际系统或进一步迁移学习。

替换输出层(添加分类头)

分类头初识时映射不对,但损失函数知道正确答案,并通过反向传播进行纠正。反向传播是在计算损失之后

for epoch in range(num_epochs):
    for batch_idx, batch in enumerate(train_loader):
        # ----- 前向传播阶段 -----
        input_ids = batch['input_ids'].to(device)
        labels = batch['labels'].to(device)

        # 模型前向(你定义的架构)
        outputs = model(input_ids)      # → 前向传播

        # 计算损失
        loss = criterion(outputs, labels)

        # ----- 反向传播阶段 -----
        optimizer.zero_grad()           # 清空旧梯度
        loss.backward()                 # 🎯 反向传播自动计算梯度

        # ----- 参数更新阶段 -----
        optimizer.step()                # 根据梯度更新参数

        # ----- 可选:梯度裁剪 -----
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)