监督微调(SFT)在应用中如何实现?
摘要:SFT 简介 (1) 什么是监督微调 监督微调(SFT)通过利用特定于任务的标签数据集将预训练的 LLM 适应特定任务。SFT 的数据集通常组织如下,一条样本包含一个指令和对应的回答:(D={(I_K,A_K)}_{K=1}^N)
SFT 简介
(1) 什么是监督微调
监督微调(SFT)通过利用特定于任务的标签数据集将预训练的 LLM 适应特定任务。SFT 的数据集通常组织如下,一条样本包含一个指令和对应的回答:\(D=\{(I_K,A_K)\}_{K=1}^N\)
(2) 监督微调和预训练的区别
在训练方式上没有任何区别,损失函数一样,主要区别在于数据的组成形式上:
预训练的每条数据都是满长度,即达到模型设置的输入长度上限;SFT 的每条数据原本多长就是多长,不需要做 packing,即每条数据不需要拼接起来
SFT 会引入预训练阶段从未见过的 special_token,来让它们学习全新的语义,借助 special_token,SFT 会把语料切分成不同的角色,标配的有 system/user/assistant。此外,SFT 会让模型见到最重要的 eos_token,预训练模型因为没有见过该 token 而无法停止生成
SFT 的 prompt 部分对应的输出不做 loss,主要原因是 prompt 的同质化比较严重,不做 loss_mask 的话,同一句话就会被翻来覆去的学
此外,它们的训练目的也不一样:预训练是在背书,纯粹的学习知识;SFT 则是在做题,学习的是指令遵循能力。切勿在 SFT 阶段强行给模型做知识注入,所有的知识注入工作应该采用 continue-pretrain 的思路进行,否则都会使得模型的通用能力掉点明显
SFT 数据处理
(1) 业内共识
prompt 的质量和多样性远重要于数据量级,微调一个 30b 量级的 base model 只需要 10w 量级的数据即可
合成数据很重要!一般需要通过不同的方式进行多路合成,减少合成数据的 bias
可以加点预训练的数据,减轻灾难性遗忘现象
一般训练一个 epoch,垂域模型数据少训练 3 epoch 去过拟合
可以做全量微调就不要做 PEFT
由于在 pretrain 的退火阶段(有的基座会称为 enhance,即知识富集)会加入高质量的 SFT、Math、Code、STEM、Agent 等数据,所以 SFT 阶段不需要做过多的工作
(2) 数据飞轮
最简单的做法,拉取线上近半个月的真实用户 prompt,先用启发式规则进行清洗,然后 GPT-4o 打标,留下可用的数据
为什么要用数据飞轮:prompt 的生产是需要有 seed 种子的,而 seed 的数据量和多样性有限,数据合成的质量不够高;可以利用数据飞轮收集线上真实的日志,基于 bad case(可来源于用户反馈、模型打标)构建高质量 SFT 数据修复 bad case
(3) 数据生产合成
一句话总结:通过不同的数据合成方法确保 prompt 的多样性,满足大模型在各个专项能力的需求
生产合成 prompt:比较有名的工作 self-instruct,划分技能库,即给每个数据打上 task_type,越细越好,然后每个 task_type 准备一些 seed propmt,然后随机采样 seed,再喂给一个能力很强的模型,让它基于这些 seed 问题再续写出一些问题;或者利用各种启发式规则合成数据造 prompt,然后用强大模型来做改写
生产合成 answer:GPT4 is all your need,用一个效果好的模型来生产 answer;也可以利用 GPT4 生产 1000 条 answer 然后去训小模型
工业界做法:拒绝采样,通过 Bon 或者其他 Won 等方式对同一个 prompt 采样多个推理路径,通过人工或者 verifiers 挑选出最佳路径为 response,或者构造 chosen 和 rejected 偏好数据。最后基于这些数据进行 SFT 或者 DPO 训练
数据质量过滤
(1) IFD 过滤
在应用 IFD 过滤前,需要用精心选择的指令数据对模型进行初步训练,使模型具备基本的指令跟随能力。这样,模型才能有效地判断不同指令的难度。
核心点:根据指令跟踪难度筛选 SFT 数据
总体流程:
第一步从简单经验中学习:从目标数据集中抽取一个多样性较高的子集,对指令嵌入使用 K-Means 聚类,建立基本的指令跟随能力
第二步评估指令执行难度:用第一步微调后的模型计算两个得分,带指令损失(衡量模型在给定指令 \(Q\) 的情况下,模型生成正确答案 \(A\) 的难度)和直接答案得分(衡量模型在没有指令的情况下,单独生成正确答案的难度)
最终指令跟踪难度(IFD)定义为:\(r_\theta(Q,A)=\frac{s_\theta(A)}{s_\theta(A|Q)}\)。
