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 数据
总体流程:
最终指令跟踪难度(IFD)定义为:\(r_\theta(Q,A)=\frac{s_\theta(A)}{s_\theta(A|Q)}\)。该比值越高,说明指令对生成答案的帮助越大,代表指令执行根据挑战性和价值
(2) MoDS 过滤
主要通过质量过滤、多样性筛选、必要性筛选三个方面来进行数据的筛选
(3) 数据多样性探索
数据多样性主要包含三个维度:数据用途、数据形式和数据语义
-
数据用途(也叫 task_type):在实际工作中,双层 task_type 都很常见,例如 "逻辑推理—常识推理"、"逻辑推理—COT 多步骤推理"。一般难 task_type 数据多点,简单 task_type 数据少点
-
数据形式:数据形式不能让模型找到规律,关键信息在 prompt 中的位置分布要足够随机。目的是避免模型在训练时退化,只聚焦于某些位置的 token
-
数据语义:嵌入在空间中的分布多样性变化,向量平均距离等等
SFT 训练
(1) 训练框架
建议使用 OpenRLHF 框架(https://github.com/OpenRLHF/OpenRLHF),简单易用,核心是 Ray+DeepSpeed
一般而言我们很少在 SFT 的部分魔改损失函数和训练策略,一般就改改 checkpoint_path, model_path, data_path, dp, pp, lr 这些参数
(2) 参数设置
不管使用哪个框架,有几个参数是着重需要注意的 - epoch: "一般就 1 epoch,如果微调垂域模型且数据量较少,可以 3 epoch"
- gradient_accumulation_steps: "表示在更新模型参数之前,梯度会在多少个小批次上累积"
- global_batch_size: "训练过程中所有设备上的总批量大小"
- learning_rate: "SFT 阶段的 lr 一般是 Pretrain 阶段的 10 倍左右"
- lr_scheduler_type: "学习率调度器的类型,主要分为 constant/linear/cosine/exponential,一般用 cosine 比较多"
- dropout: "一般不用,主要因为没啥用,并且还会拖累训练效率"
- max_seq_len: "模型可以处理的输入序列的最大长度,一般设置 4K 就行"
- offload: "用于将部分计算或数据从 GPU 专业到 CPU 中,一般不设置"
- gradient_checkpointing: "通过在前向传播中节省部分中间计算结果的存储,降低内存使用量"
- seq_parallel_size: "序列并行的分块大小,用于分布式训练时将输入序列分成更小的块以实现并行化"
- per_device_train_batch_size: "每个 GPU 上的训练批量大小,一般就设置为 1"
- weight_decay: "权重衰弱参数,默认设置为 0.01"
- num_warmup_steps: "通常设置为总训练部步数的 5%-10%"
复制代码(3) 训练技巧
不建议在 SFT 阶段使用 packing,不少实验表明 SFT packing 后削弱了模型对难的短 query 和短答案的拟合
(4) 训练策略
-
多任务学习:直接混合不同的 SFT 数据源并引用 SFT,如果将每个数据源视为不同的任务,这就可以视为多任务学习
-
顺序训练:在每个数据集上依次应用 SFT
-
混合序列训练:先在专业数据集(代码、数学)上应用多任务学习,然后在通用能力数据集上应用 SFT
-
双阶段混合微调(DMT):首先在专业数据集上应用 SFT,在第二阶段使用混合数据源应用 SFT
(5) 多轮对话专项提升
主题没有变化的真多轮数据直接加入训练数据,主图发生变化的伪多轮数据选取一小部分加入到训练数据中;可以用一些单轮对话合成多轮对话数据;多轮计算 loss
SFT 评估
不同于 Pretrain 只需要看知识能力,SFT 的评估需要看经典的 3H 原则:Helpfulness、Honesty、Harmlessness,或者按需求制定指标
评估的时候每个唯独有一个单独的得分,根据相应的加权公式来确定这条回复的可用性
目前的评估方式主要是机评和人评 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |