一土水丰色今口 2025-12-17 17:15 采纳率: 97.6%
浏览 1
已采纳

千问模型SFT微调数据格式要求是什么?

在对千问模型进行SFT(监督微调)时,常见的技术问题之一是:**如何正确构造符合SFT微调要求的输入数据格式?** 具体而言,许多开发者不清楚训练样本应以何种结构组织,例如是否需包含“instruction-input-output”三元组,或仅需“prompt-response”对;输入文本是否需要特殊标识符(如`<|begin_of_text|>`)分隔;多轮对话场景下如何用角色标签(如user/assistant)组织会话流。此外,关于标签掩码(label masking)策略——即仅计算响应部分的损失,而非整个输入——也常因格式处理不当导致训练效率下降。这些问题直接影响微调的收敛性与模型输出质量。
  • 写回答

1条回答 默认 最新

  • 火星没有北极熊 2025-12-17 17:15
    关注

    一、SFT微调中的输入数据格式构建:从基础结构到高级实践

    1. SFT微调的基本概念与数据角色

    监督微调(Supervised Fine-Tuning, SFT)是大语言模型在预训练后,通过高质量的“输入-输出”对进行有监督学习的过程。其核心目标是让模型学会在给定上下文下生成符合预期的响应。

    在这一过程中,输入数据的组织方式直接影响模型的学习效率和推理能力。常见的困惑包括:

    • 是否必须使用 instruction-input-output 三元组?
    • prompt-response 对是否足够?
    • 如何处理多轮对话场景?
    • 是否需要特殊 token 分隔不同部分?

    这些问题的答案并非绝对,而是依赖于模型架构、Tokenizer 设计以及训练目标。

    2. 常见的数据结构模式对比

    结构类型适用场景示例优点缺点
    Prompt-Response单轮问答、代码生成{"prompt": "写一个冒泡排序", "response": "def bubble_sort..."}简洁明了,易于构造缺乏语义角色区分
    Instruction-Input-Output指令遵循任务{"instruction": "翻译成英文", "input": "你好", "output": "Hello"}结构清晰,适合复杂任务分解冗余字段,需额外解析
    Role-Based Conversation多轮对话系统[{"role": "user", "content": "你好"}, {"role": "assistant", "content": "您好!"}]支持上下文建模,贴近真实交互格式复杂,需严格对齐

    3. Token 分隔符与特殊标记的使用

    现代大模型(如Qwen系列)通常依赖特定的特殊token来标识文本边界或角色切换。例如:

    <|begin_of_text|>
    <|user|>
    <|assistant|>
    <|eot_id|>

    这些token由Tokenizer定义,并在分词阶段保留为独立ID。正确使用它们可以提升模型对结构的理解能力。

    以千问模型为例,推荐采用如下模板:

    <|begin_of_text|><|user|>{prompt}<|eot_id|><|assistant|>{response}<|eot_id|>

    该格式明确划分用户输入与模型响应,便于后续损失掩码处理。

    4. 多轮对话的序列组织策略

    对于包含历史对话的样本,应按时间顺序拼接会话流,并交替使用角色标签:

    <|begin_of_text|>
    <|user|>今天天气怎么样?<|eot_id|>
    <|assistant|>晴天,气温25度。<|eot_id|>
    <|user|>适合户外运动吗?<|eot_id|>
    <|assistant|>非常适合,建议外出活动。<|eot_id|>

    这种结构允许模型捕捉上下文依赖关系,同时保持训练时的因果注意力机制有效性。

    5. 标签掩码(Label Masking)机制详解

    在SFT训练中,仅应计算模型生成部分的损失,即response区域。若不对prompt部分进行掩码,则会导致梯度污染,降低收敛速度。

    实现方式如下:

    1. 将整个拼接后的文本送入Tokenizer,获取input_ids。
    2. 根据特殊token位置确定每个token所属区域(prompt 或 response)。
    3. 构建labels张量,将prompt部分设为-100(PyTorch中忽略损失),response部分保留原始token ID。

    代码示例如下:

    def construct_labels(input_ids, tokenizer):
        labels = input_ids.copy()
        special_tokens = [
            tokenizer.encode("<|begin_of_text|>", add_special_tokens=False)[0],
            tokenizer.encode("<|user|>", add_special_tokens=False)[0],
            tokenizer.encode("<|eot_id|>", add_special_tokens=False)[0]
        ]
        current_pos = 0
        for i, token_id in enumerate(input_ids):
            if token_id in special_tokens:
                current_pos = i + 1  # Skip prompt segments
            else:
                if input_ids[current_pos:i+1] contains assistant response start:
                    break
        # Set non-response parts to -100
        for j in range(current_pos):
            labels[j] = -100
        return labels

    6. 数据预处理流程图(Mermaid)

    graph TD A[原始数据] --> B{判断结构类型} B -->|Prompt-Response| C[添加role标签] B -->|IO Format| D[转换为对话格式] B -->|Conversation| E[保持原结构] C --> F[插入特殊token] D --> F E --> F F --> G[Tokenizer编码] G --> H[构建Labels掩码] H --> I[输出训练样本]

    7. 实践建议与常见陷阱

    以下是基于实际项目经验总结的关键点:

    • 始终使用与模型训练一致的Tokenizer版本。
    • 避免在response中引入无关内容(如解释性文字),保持输出纯净。
    • 确保所有样本的最大长度不超过模型上下文窗口(如Qwen-7B为32768)。
    • 对长文本进行截断时,优先保留尾部内容(最近上下文更重要)。
    • 在分布式训练中,注意pad_token_id与-100标签的一致性。
    • 使用Hugging Face的DataCollatorForLanguageModeling需自定义修改以支持SFT掩码逻辑。
    • 建议使用datasets库进行高效数据映射与缓存。
    • 验证集应与训练集采用相同格式处理流程,避免评估偏差。
    • 监控loss曲线时,关注每step的平均loss是否稳定下降。
    • 可通过生成少量样本反向解码,检查input_ids与labels对齐情况。
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 12月18日
  • 创建了问题 12月17日