Hexo

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

NLP——WizardLM(Evol-Instruct)

注:本文包含 AI 辅助创作

  • 参考链接:
    • 原始论文:(Evol-Instruct)WizardLM: Empowering large pre-trained language models to follow complex instructions, Microsoft & PKU, arXiv 20230424, 20230610, 20250527
    • 相关 GitHub 地址:github.com/nlpxucan/evol-instruct

Paper Summary

  • 整体说明:
    • 论文提出了一种为 LLM 生成多样化和复杂指令数据的进化算法 Evol-Instruct(使用 LLM 而非人类来创建大量不同复杂度指令数据的途径),并基于此数据集微调得到了 LLaMA(得到 WizardLM)
    • WizardLM 在一系列公认的基准测试中显著超越了典型的开源 LLM,如 Alpaca 和 Vicuna
    • WizardLM 在代码、数学、GPT-4 和人工评估方面均以显著优势超越基线模型
  • 背景 & 问题:
    • 使用开放域指令跟随数据训练 LLM 已经取得了巨大成功
    • 问题1:人工创建此类指令数据非常耗时且劳动密集
    • 问题2:人类可能难以生成高复杂度的指令
  • 基本思路:从一个初始指令集出发,论文使用提出的 Evol-Instruct 方法逐步将其重写为更复杂的指令
  • 模型训练:论文将所有生成的指令数据混合以微调 LLaMA(论文将得到的模型称为 WizardLM)
    • 自动评估和人工评估均一致表明, WizardLM 的性能优于基线模型,如 Alpaca(基于 Self-Instruct 训练)和 Vicuna(基于人工创建的指令训练)
    • 论文通过实验结果证明:由 Evol-Instruct 精心构建的指令跟随数据集的质量能够显著提升 LLM 的性能

Introduction and Discussion

  • LLM 已成为众多自然语言处理任务的首选方法 (2020; 2022; 2023)
  • LLM 在大规模文本数据上进行训练以预测后续 Token ,使其能够针对各种输入生成连贯流畅的文本
  • 然而,这些模型通常难以遵循用户指定的指令或目标,这限制了它们在现实场景中的实用性和适用性
  • NLP 界近期见证了众多努力,旨在训练 LLM 更好地遵循指令并变得更有帮助 (2023;)
    • 训练指令跟随语言模型的初步尝试 (2022; 2021; ) 基于一系列不同的 NLP 任务集合,并辅以少量手写指令
  • 这些封闭域指令存在两个主要缺点:
    • 首先,一个 NLP 数据集中的所有样本仅共享少数几个常见指令,严重限制了其多样性;
    • 其次,指令通常只要求完成一项任务 (但在现实生活中,人类的指令通常具有多个且多样的任务需求)
  • 通过使用真实用户生成的开放域指令数据,OpenAI 的 LLM(例如 InstructGPT (2022) 和 ChatGPT)取得了巨大成功
    • 这些开放域指令能够充分释放 LLM 的无限潜力 (2023; ),并使它们能够执行更复杂多样的任务
  • 但像 OpenAI 那样使用人类创建开放域指令数据集会遇到以下挑战:
    • 整个标注过程极其昂贵且耗时 (2023; )
    • 人工创建指令的难度级别分布偏向于简单或中等,困难指令较少(根据图 5a 中 ShareGPT (2023) 的难度统计)
    • 人类标注者容易疲劳,无法持续高强度工作以产生足够比例的高难度指令 (2023; )
  • 基于这些问题,开发一种能够以相对较低成本大规模自动生产开放域指令(尤其是更困难的指令)的方法,成为进一步推进指令微调语言模型(instruction-tuned language models)的关键 (2023; )
  • 在这项工作中,论文引入了 Evol-Instruct ,一种使用 LLM 而非人类来自动大规模生成不同难度级别开放域指令的新方法,以提升 LLM 的性能
  • 图 1 展示了 Evol-Instruct 的运行示例
  • 从一个简单的初始指令 “1+1=?” 开始,论文的方法随机选择深度演化(In-depth Evolving)(蓝色方向线)或广度演化(In-breadth Evolving)(红色方向线)来将简单指令升级为更复杂的指令或创建新指令(以增加多样性)
    • 深度演化包括五种操作类型:添加约束(add constraints)、深化(deepening)、具体化(concretizing)、增加推理步骤(increase reasoning steps)和复杂化输入(complicate input)
    • 广度演化是突变(mutation) ,即基于给定指令生成一个全新的指令
    • 这六种操作通过使用特定的提示词(prompt)来提示(prompting)一个 LLM 来实现
  • 由于演化后的指令是由 LLM 生成的,有时演化会失败
    • 论文采用一个指令淘汰器(instruction eliminator)来过滤失败的指令,这被称为淘汰演化(Elimination Evolving)
    • 论文重复这个进化过程若干轮,以获得包含各种复杂度的足够指令数据
  • 为了验证 Evol-Instruct 的有效性以及它创建的用于微调的指令是否超越人类创建的指令
    • 论文演化来自 Alpaca (2023) 数据(由机器创建)的指令,微调 LLaMA (2023) 模型,并全面比较微调后的模型 WizardLM 与在 ShareGPT(指令由人类创建)上训练的 Vicuna (2023)
      • Alpaca 数据总共有 \(52k\) 个样本,是使用 self-instruct (2022a) 从仅 \(175\) 个人工创建的种子指令生成的
      • 论文选择 Alpaca 数据作为演化的初始数据,这可以确保 WizardLM 的训练指令几乎没有人直接参与标注
    • 论文使用 OpenAI ChatGPT API 执行了四轮演化 ,最终获得 \(250k\) 条指令
    • 为了与 Vicuna 的 \(70k\) 真实用户数据进行公平比较
      • 论文从完整的 \(250k\) 数据中采样了 \(70k\) 条指令 ,并微调了 LLaMA 13B 模型
    • 由于原始 Alpaca 数据只有 \(52k\) 个样本,论文使用其 self-instruct 方法生成了额外的 \(18k\) 数据,并使用其代码重新训练了 LLaMA 13B 模型,得到 Alpaca 13B 作为论文的基线
    • 由于先前指令跟随测试数据集中困难指令比例较低 ,论文手动创建了一个新的难度平衡的测试数据集 ,命名为 WizardEval
  • 论文在广泛的 LLM 基准测试(涵盖推理、代码、数学、通用对话等)上评估了 Alpaca、Vicuna、ChatGPT 和 WizardLM
  • 论文的主要发现如下:
    • 论文引入了 Evol-Instruct ,一种通过自动大规模生成各种主题和难度级别的开放域指令来大幅提升开源 LLM 性能的新方法
    • 论文开发了 WizardLM 模型,其在一系列基准测试中显著超越了典型的开源 LLM,如 Alpaca 和 Vicuna
      • WizardLM 在代码、数学、GPT-4 和人工评估方面均以显著优势优于基线模型
    • 论文进行了一项初步研究,强调了指令复杂度在监督微调大规模预训练语言模型中取得出色性能的重要性

Approach

  • 本节将详细阐述所提出的 Evol-Instruct 的细节
  • 如图 2 所示,该流程主要包含两个组件:
    • 指令演化器(Instruction Evolver)
    • 指令淘汰器(Instruction Eliminator)
  • 这些组件的细节将在第 3.2 节中介绍,指令微调方法将在第 3.3 节中描述

Definition of Instruction Data Evolution(指令数据演化)

  • 论文从给定的初始指令数据集开始演化:
    $$D^{(0)}=(I_{k}^{(0)},R_{k}^{(0)})_{1\leq k\leq N}$$
    • 其中 \(I_{k}^{(0)}\) 是 \(D^{(0)}\) 中的第 \(k\) 条指令
    • \(R_{k}^{(0)}\) 是第 \(k\) 条指令的相应响应
    • \(N\) 是 \(D^{(0)}\) 中的样本数量
  • 在每次演化中,论文通过使用 Evol-Instruct 提示词提示(prompting)一个 LLM
    • 将 \(D^{(t)}\) 中的所有 \(I^{(t)}\) 升级为 \(I^{(t+1)}\),然后使用该 LLM 为新演化出的 \(I^{t+1}\) 生成相应的响应 \(R^{t+1}\)
    • 从而,论文获得一个演化后的指令数据集 \(D^{t+1}\)
  • 通过迭代执行 \(M\) 次演化,我们可以顺序获得 \(M\) 个演化数据集:
    $$[D^{(1)}\cdots D^{(M)}]$$
  • 论文的工作专注于开放域指令数据,其中指令具有变化的输入和任务,指令部分和输入部分之间没有明确的区分

Automatic Instruction Data Evolution

  • 论文的指令演化流程包括三个步骤:
    • 1) 指令演化(instruction evolving)
    • 2) 响应生成(response generation)
    • 3) 淘汰演化(elimination evolving),即过滤未能成功演化的指令
Instruction Evolution
  • 论文发现 LLM 可以使用特定的提示词使给定的指令变得更加复杂和困难。此外,它们可以生成完全全新、复杂度相当但完全不同的指令。利用这一发现,我们可以迭代地演化一个初始指令数据集,提高其难度级别并扩展其丰富性和多样性。论文使用给定的初始指令数据集 \(D^{(0)}\) 初始化指令池(instruction pool)。在每个演化轮次(epoch)中,从前一轮次升级的指令被从池中取出。然后论文利用指令演化器(instruction evolver)来演化每个取出的指令,并利用指令淘汰器(instruction eliminator)来检查演化是否失败。成功演化的指令被添加到池中,而不成功的指令则原样放回,希望在下个演化轮次中能成功升级它们
Instruction Evolver
  • 指令演化器是一个使用 Evol-Instruct 提示词来演化指令的 LLM,有两种类型:深度演化和广度演化
In-Depth Evolving
  • 通过五种类型的提示词来增强指令,使其更复杂和困难:

    • 添加约束(add constraints)、深化(deepening)、具体化(concretizing)、增加推理步骤(increased reasoning steps)和复杂化输入(complicating input)
  • 深度演化提示词的核心部分是“您的目标是将给定的提示词重写为一个更复杂的版本,以使那些著名的 AI 系统(例如 ChatGPT 和 GPT4 (OpenAI, 2023))更难处理。但重写后的提示词必须是合理的、可被人类理解并回应”

  • 论文要求 LLM 创建具有挑战性但合理且非 AI 任意想象的指令

  • 需要逐步增加难度以避免指令集中充斥极其复杂的指令,这会损害训练模型的泛化性能

  • 为了控制难度增加,论文使每次演化“更难一点”,并限制最多添加 10 到 20 个单词

  • 在上述五种演化中,除了复杂化输入(complicating input)外,其他都可以在没有任何上下文示例(in-context examples)的情况下实现

  • 论文展示添加约束的提示词如下(深化、具体化和增加推理步骤的提示词将在附录 A-C 中详述)

  • 示例 3.1:深度演化中添加约束的提示词(Prompt for Adding Constraints of In-Depth Evolving)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    I want you act as a Prompt Rewriter.
    Your objective is to rewrite a given prompt into a more complex version to make those famous AI systems (e.g., ChatGPT and GPT4) a bit harder to handle. But the rewritten prompt must be reasonable and must be understood and responded by humans.
    Your rewriting cannot omit the non-text parts such as the table and code in #Given Prompt#:. Also, please do not omit the input in #Given Prompt#.
    You SHOULD complicate the given prompt using the following method:
    **Please add one more constraints/requirements into #Given Prompt#**
    You should try your best not to make the #Rewritten Prompt# become verbose, #Rewritten Prompt# can only add 10 to 20 words into #Given Prompt#. ‘#Given Prompt#’, ‘#Rewritten Prompt#’, ‘given prompt’ and
    ‘rewritten prompt’ are not allowed to appear in #Rewritten Prompt#
    #Given Prompt#:
    {Here is instruction.} #Rewritten Prompt#:

    我希望您扮演一个提示词重写器(Prompt Rewriter)
    您的目标是将给定的提示词重写为一个更复杂的版本,以使那些著名的 AI 系统(例如 ChatGPT 和 GPT4)更难处理。但重写后的提示词必须是合理的、可被人类理解并回应
    您的重写不能省略 #给定提示词# 中的非文本部分,例如表格和代码。同时,请不要省略 #给定提示词# 中的输入
    您**应该**使用以下方法使给定提示词复杂化:
    **请向 #给定提示词# 中添加一个更多的约束/要求**
    您应尽力避免使 #重写后的提示词# 变得冗长,#重写后的提示词# 只能在 #给定提示词# 的基础上增加 10 到 20 个单词。“#给定提示词#”、“#重写后的提示词#”、“给定提示词”和“重写后的提示词”不允许出现在 #重写后的提示词# 中
    **#给定提示词#:**
    {这里是指令。}
    **#重写后的提示词#:**
  • 对于复杂化输入(complicating input),论文将使用上下文演示(in-context demonstration)。由于演示较长,论文在下面提供一个简要模板,完整提示词详见附录 D

  • 示例 3.2:深度演化中复杂化输入的提示词(Prompt for Complicating Input of In-Depth Evolving)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    I want you act as a Prompt Rewriter.
    Your objective is to rewrite a given prompt into a more complex version to make those famous AI systems (e.g., ChatGPT and GPT4) a bit harder to handle. But the rewritten prompt must be reasonable and must be understood and responded by humans.
    You must add [XML data] format data as input data in [Rewritten Prompt]
    #Given Prompt#:
    {Here is instruction of Example 1.}
    #Rewritten Prompt#:
    {Here is rewritten instruction of Example 1.} ... N -1 Examples ...
    You must add [#Given Dataformat#] format data as input data in [Rewritten Prompt] #Given Prompt#:
    {Here is instruction of Example N.}
    #Rewritten Prompt#:

    我希望您扮演一个提示词重写器(Prompt Rewriter)
    您的目标是将给定的提示词重写为一个更复杂的版本,以使那些著名的 AI 系统(例如 ChatGPT 和 GPT4)更难处理。但重写后的提示词必须是合理的、可被人类理解并回应
    您必须在 [重写后的提示词] 中添加 [XML 数据] 格式的数据作为输入数据
    **#给定提示词#:**
    {这里是示例 1 的指令。}
    **#重写后的提示词#:**
    {这里是示例 1 重写后的指令。}
    ... N -1 个示例 ...
    您必须在 [重写后的提示词] 中添加 [#给定数据格式#] 格式的数据作为输入数据
    **#给定提示词#:**
    {这里是示例 N 的指令。}
    **#重写后的提示词#:**
In-Breadth Evolving
  • 广度演化旨在增强主题覆盖度、技能覆盖度以及整体数据集的多样性
  • 开放域指令微调数据集(例如 Alpaca、ShareGPT 等)通常规模较小,缺乏主题和技能多样性
  • 为了解决这个问题,论文设计了一个提示词,基于给定指令生成一个全新的指令,要求新指令更加长尾(more long-tailed)。论文的广度演化提示词如下:
  • 示例 3.3:广度演化的提示词(Prompt for In-Breadth Evolving)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    I want you act as a Prompt Creator.
    Your goal is to draw inspiration from the #Given Prompt# to create a brand new prompt.
    This new prompt should belong to the same domain as the #Given Prompt# but be even more rare.
    The LENGTH and difficulty level of the #Created Prompt# should be similar to that of the #Given Prompt#. The #Created Prompt# must be reasonable and must be understood and responded by humans.
    ‘#Given Prompt#’, ‘#Created Prompt#’, ‘given prompt’ and ‘created prompt’ are not allowed to appear in #Created Prompt#.
    #Given Prompt#:
    {Here is instruction.} #Created Prompt#:

    我希望您扮演一个提示词创建者(Prompt Creator)
    您的目标是从 #给定提示词# 中汲取灵感,创建一个全新的提示词
    这个新提示词应该与 #给定提示词# 属于同一领域,但应更加罕见(rare)
    #创建出的提示词# 的长度和难度级别应与 #给定提示词# 相似
    #创建出的提示词# 必须是合理的、可被人类理解并回应
    “#给定提示词#”、“#创建出的提示词#”、“给定提示词”和“创建出的提示词”不允许出现在 #创建出的提示词# 中
    #给定提示词#:
    {这里是指令。}
    #创建出的提示词#:
Response Generation
  • 论文使用与演化相同的 LLM 来为演化后的指令生成相应的响应
  • 生成提示词是 “{Here is instruction.}”,论文将其输入到 ChatGPT-3.5 的请求中,并将返回的文本正文解析为响应
Elimination Evolving(淘汰演化)
  • 论文将以下四种情况归类为指令演化失败:
    • 1)演化后的指令与原始指令相比未提供任何信息增益
      • 论文使用 ChatGPT 来进行此判断,详情请参阅附录 G
    • 2)演化后的指令使得 LLM 难以生成响应
      • 论文发现当生成的响应包含“抱歉(sorry)”且长度相对较短(即少于 80 个单词)时,通常表明 LLM 难以响应演化后的指令
      • 因此我们可以使用此规则进行判断
    • 3)LLM 生成的响应仅包含标点符号和停用词(stop words)
    • 4)演化后的指令明显复制了演化提示词中的某些词语

Finetuning The LLM On The Evolved Instructions

  • 所有演化完成后,论文将初始指令数据集与所有轮次中演化得到的指令数据合并,并对样本进行随机打乱,以创建用于微调的数据集
    • 这种处理方式确保了数据集中不同难度级别的指令均匀分布,从而最大化模型微调的平滑性
  • 为了证明性能提升并非源于合并后数据量的增加,而是源于论文提出的新方法 Evol-Instruct,论文从合并后的数据中随机抽取与训练基线模型(例如 Vicuna)等量的数据,作为论文最终的微调数据
  • 论文选择 Vicuna 的提示词(prompt)作为论文微调所用的提示词,其具体格式为:“A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user’s questions. USER: Hi ASSISTANT: Hello. USER: Who are you? ASSISTANT: I am WizardLM ……”

Experiment

  • 论文通过自动评估和人工评估两种方式对 WizardLM、Alpaca、Vicuna 和 ChatGPT 进行了评估

Baselines

  • (1) ChatGPT 是由 OpenAI 开发的一款 AI 聊天机器人,能够以自然且引人入胜的方式与用户互动
    • 它建立在 GPT-3.5 和 GPT-4 等 LLM 之上,并基于海量的互联网文本数据进行训练
  • (2) Alpaca 是由斯坦福大学开发的开源指令遵循模型
    • 为了公平比较,论文使用 Alpaca 采用的 Self-Instruct 方法将指令数量从 52k 扩展到 70k,并将原始的 David+003 响应替换为 ChatGPT 的响应
    • 论文基于这份新的 Alpaca 数据,从 LLaMA 13B (2023) 重新训练了 Alpaca 13B
  • (3) Vicuna 基于 LLaMA,并在从 ShareGPT 收集的 70k 用户共享对话上进行了微调
    • 它是目前最先进、最通用的开源指令遵循模型之一
    • 论文使用来自 FastChat 的 13B-v1.1 模型
  • (4) 基于 Llama 13B 训练的开源模型,包括 Baize (2023)、CAMEL (2023a) 和 Tulu (2023)

Experiment detail

  • 为了构建数据集,论文使用 Alpaca 的 \(52k\) 指令数据集进行初始化,并迭代执行 \(M\) 轮演化,其中 \(M=4\)
  • 在每一轮演化中,对于每条指令,论文以相等概率从总共六个演化提示(即五个来自深度演化,一个来自广度演化)中随机选择一个
    • 论文使用 Azure OpenAI ChatGPT API5 执行上述过程
    • 然后,论文利用 ChatGPT 生成响应
    • 最终,论文获得了 \(250k\) 条指令
  • 为了公平比较,论文从 \(250k\) 数据中以相等概率随机采样 \(70k\) 数据作为 WizardLM 的最终训练数据,与 Vicuna 的训练数据量相同
    • 论文使用温度为 1 来生成响应,并将生成的最大 token 数设置为 2048
    • 此外,论文将频率惩罚设置为零,top-p 设置为 \(0.9\)
    • 总共,论文请求 API \(52k\times 4\times 3=624k\) 次以构建完整的数据集
  • 论文使用预训练的 LLaMA 13B (2023) 来初始化论文的模型
    • 论文采用 Adam 优化器,初始学习率为 \(2\times 10^{-5}\),最大 token 数为 2048,每个 GPU 的批次大小为 4
  • 论文在 8 个 V100 GPU 上使用 Deepspeed Zero-3 训练了论文的模型 3 个 epoch ,耗时 140 小时
  • 对于推理,论文对 WizardLM 和基线模型使用贪心搜索,并将最大生成长度设置为 2048

Automatic Evaluation

  • 为了全面概述论文的 WizardLM 的性能,论文在多个 LLM 基准测试中对论文的模型与既定基线进行了比较
  • HuggingFace 的 OpenLLM 排行榜 :(2023) 包括 MMLU (2020)、ARC (2018)、HellaSwag (2019) 和 TruthfulQA (2022)
    • MMLU 包含一系列多项选择的学术问题
    • ARC 是一组小学科学问题
    • HellaSwag 是一个常识推理测试
    • TruthfulQA 衡量模型再现错误陈述的倾向
    • 论文采用了 OpenLLM 的评估代码 (2021)
  • 代码生成 (Code Generation)
    • 论文使用广泛使用的 HumanEval (2021) 基准测试,该测试包含 164 个编码问题,通过报告 pass@1 指标来评估 LLM 在函数级别的代码编写能力
  • 数学推理 (Math Reasoning)
    • 论文使用 GSM8k (2021) 来评估模型的数学能力,GSM8k 包含 1319 个小学数学测试数据
    • 论文采用 4-shot 测试并报告 pass@1
  • GPT-4 评估 (GPT-4 Evaluation)
    • 论文采用了两个广泛认可的 GPT-4 评估基准,包括 AlpacaEval (2023c) 和 MT-Bench (2023)
    • 论文还使用 GPT-4 在论文后续提出的 WizardEval 上评判 LLM
  • 如图 3 和表 1 所示,与其他相同规模的开源模型相比, WizardLM 在大多数基准测试中都具有显著的性能优势。特别是在数学、代码和 GPT-4 评估方面,它相比 Alpaca、Vicuna、Baize、CAMEL 和 Tulu 取得了显著提升

Human evaluation

  • 为了评估 WizardLM ,论文在论文精心制作的测试平台 WizardEval 上进行了人工评估,该测试集包含 218 条来自不同来源(如在线开源项目(Github, ShareGPT)、平台(Twitter)和论坛(Reddit, Discord)的真实世界人类指令
    • 数据包含 29 项技能和领域,代表了人类的主要需求,例如代码生成(Coding Generation)、Math、Reasoning、复杂格式(Complex Formats)、写作(Writing)、广泛学科(Extensive Disciplines)等
  • 如图 3(a) 和附录图 6 所示,论文还分别分析了 WizardEval 的难度和技能分布,这表明 WizardEval 能够处理比 Self-Instruct 和 Vicuna 测试集更复杂和要求更高的场景的评估
  • 论文在 WizardLM-13b 和基线模型之间进行了盲法成对比较
    • 论文招募了 10 名受过良好教育的标注员
    • 向每位标注员展示来自 Alpaca-13b、Vicuna-13b、 WizardLM 和 ChatGPT 的四条响应,这些响应被随机打乱以隐藏其来源
  • 然后,标注员根据以下标准(详细定义请参阅附录 K)判断哪个响应更好:
    • (1) 相关性(Relevance)
    • (2) 知识性(Knowledgeable)
    • (3) Reasoning
    • (4) 计算(Calculation)
    • (5) 准确性(Accuracy)
  • 然后,他们应将四个响应从 1 到 5 排名(1 表示最好),并允许对可比较的实例给出相同的分数
    • 为了估计胜率,论文比较了每对模型之间的获胜、失败和平局频率
  • 如图 4 (b) 所示
    • WizardLM 取得了比 Alpaca 和 Vicuna 好得多的结果,这证明了 Evol-Instruct 方法的有效性
    • 所有的 Kappa 分数均大于 0.6,这表明标注员之间具有良好的一致性

Ablation Study

  • 使用不同的数据(种子、大小)、演化模型和基础模型大小进行训练 (Training with different data (seed, size), evol model, and base model size)
    • 为了研究不同数据种子、演化模型、演化数据集规模、预训练模型对论文提出的方法的影响,论文进行了以下实验:
      • a)使用 70k ShareGPT 作为种子数据获得 WizardLM-13b (ShareGPT Seed);
      • b)使用 LlaMA-2-70B-Chat 替代 ChatGPT 作为演化执行模型获得 WizardLM-13b (LlaMA-2-70B-Chat Evol);
      • c)论文在更大规模的预训练模型 Llama-1 65B 和 Llama-2 70B 上训练,分别获得 WizardLM-65b 和 WizardLM-70b;
      • d)使用完整的 250k 演化数据获得 WizardLM-13b (250K);
      • e)使用与 LlaMA 系列完全不同的基础模型 Mistral-7B,获得 WizardLM-7b (Mistral);
      • f)为了比较更多样化的指令数据,论文选择 Supernatural Instructions (2022b) 并随机抽取 70k 数据训练 llama-13b 获得 LlaMA-13b (SNI)
    • 完整结果如表 2 所示:
      • 为了探究 WizardLM-13b (ShareGPT Seed) 在 GSM8k 上表现较差的原因,论文分别从 ShareGPT 和 Alpaca 数据中随机采样 2000 条指令,然后使用 ChatGPT 判断(提示词请参阅附录 G)一条指令是否与“数学”相关,论文发现 ShareGPT 仅包含 4.3% 的数学数据,而 Alpaca 数据包含 11.8% 的数学数据,因此作者认为较少的数学数据导致 WizardLM-13b (ShareGPT Seed) 的 GSM8k 性能较差
    • 表2结果表明:
      • (i) ShareGPT 是比 Alpaca 更好的 evol-instruct 种子;
      • (ii) 更大的演化数据规模可以提高模型能力;
      • (iii) 论文提出的 Evol-Instruct 方法不依赖于 ChatGPT,其他强大的开源模型如 Llama-2 也是 ChatGPT 的良好替代品;
      • (iv) 论文的演化数据也显示出比 Supernatural Instructions 更好的微调性能
      • 此外,在不同预训练基础(例如 Llama-1 65B、Llama-2、Mistral-7B)上的结果表明,论文的 Evol-Instruct 可以广泛应用于各种预训练模型
  • 深度演化分析 (Analysis of In-depth Evolving)
    • 图 4(a) 和 4(b) 展示了一项消融研究,调查了数据演化轮数的影响
    • 为了研究演化过程的深度,论文使用 ChatGPT 来判断指令的难度级别。使用的提示词请参阅附录 E
    • 图 4(b) 显示了使用每轮演化数据微调的模型在(第 4.3 节中的九个自动基准测试上的)平均分数
    • 从 \(C0\) 到 \(C4\) 的每轮数据大约为 \(52k\)
    • 从该图的趋势可以看出,随着训练指令数据复杂度的逐渐增加,微调模型的性能也同步提高
    • 为了探究 ChatGPT 难度评分的正确性,论文还使用 GPT-4 和人工来测量指令难度,附录 I 表 3 中的详细结果表明 ChatGPT、GPT-4 和人工标注员之间具有良好的一致性
  • 广度演化分析 (Analysis of In-breadth Evolving)
    • 论文旨在检查指令的语义广度
    • 论文使用 t-SNE (2008) 和 k-means (1979) 算法将指令的 BERT 嵌入划分为 20 个簇
    • 附录 F 中的图 6 显示了聚类情况,突出了论文的方法与 ShareGPT 和 Alpaca 相比具有更优越的分散性,表明论文的指令具有更大的主题多样性

Related Work

Closed domain instruction tuning

  • 早期的指令跟随训练工作 (2021; 2023) 关注 LM 的跨任务泛化能力,其中 LM 在广泛的公共 NLP 数据集上进行微调,并在不同的 NLP 任务集上进行评估
    • T5 (2020) 做出了最早的尝试,使用统一的文本到文本(text-to-text)格式共同训练自然语言处理(NLP)任务,如问答、文档摘要和情感分类
    • 诸如 FLAN (2021)、ExT5 (2022)、T0 (2022) 和 KnowDA (2022c) 等工作将 NLP 任务的数量增加到大约一百个,并为每个任务精心设计了几个指令 (2023;)
    • 诸如 ZeroPrompt (2022) 和 FLAN-T5 (2022) 等工作将任务数量提升至数千个
  • 这些研究一致表明,使用多样化的 NLP 任务指令微调 LM 可以增强它们在新任务上的性能
  • 但使用这些封闭形式指令(即指令通常仅针对单个 NLP 任务,且输入数据形式简单)训练的 LLM 在真实用户场景中往往表现不佳

Open domain instruction tuning

  • 论文的工作属于这一研究路线
  • OpenAI 雇佣了许多标注员并编写了许多带有相应正确答案的指令
    • 这些人工创建的指令形式多样,任务类型丰富
    • 基于这个数据集,OpenAI 将 GPT-3 (2020) 训练成 InstructGPT (2022),它可以处理各种真实用户指令,并导致了 ChatGPT 的成功
  • Orca (2023) 不仅学习来自 LLM 的表层响应文本,还捕获复杂的推理过程信号
  • 由于 OpenAI 的这些杰出工作并未开源,Alpaca (2023) 和 Vicuna (2023) 随后基于开源 LLM LLaMA (2023) 积极探索了开放域指令微调
  • Alpaca 使用了一个包含 \(50k\) 条指令的数据集,这些指令是从有限(例如 175 个样本)的手写指令种子集中生成的
  • 论文的工作与 InstructGPT 和 Vicuna 的不同之处在于:
    • 论文使用 AI 生成的数据进行指令微调
    • 与 Alpaca 的 self-instruct (2022a) 生成方法不同, Evol-Instruct 可以控制生成指令的难度和复杂度级别

附录 A:Deepening Prompt(深化 Prompt)

  • 示例 A.1:用于深度演化的深化提示 (Prompt for Deepening of In-Depth Evolving)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    I want you act as a Prompt Rewriter.
    Your objective is to rewrite a given prompt into a more complex version to make those famous AI systems (e.g., ChatGPT and GPT4) a bit harder to handle. But the rewritten prompt must be reasonable and must be understood and responded by humans.
    Your rewriting cannot omit the non-text parts such as the table and code in #Given Prompt#:. Also, please do not omit the input in #Given Prompt#.
    You SHOULD complicate the given prompt using the following method:
    If #Given Prompt# contains inquiries about certain issues, the depth and breadth of the inquiry can be increased.
    You should try your best not to make the #Rewritten Prompt# become verbose, #Rewritten Prompt# can only add 10 to 20 words into #Given Prompt#. ‘#Given Prompt#’, ‘#Rewritten Prompt#’, ‘given prompt’ and ‘rewritten prompt’ are not allowed to appear in #Rewritten Prompt#
    #Given Prompt#:
    {Here is instruction.} #Rewritten Prompt#:

    我希望你扮演一个提示词重写器(Prompt Rewriter)。
    你的目标是将给定的提示词(prompt)改写成更复杂的版本,以使那些著名的人工智能系统(例如 ChatGPT 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。
    你的改写不能省略 #给定提示词#(#Given Prompt#)中的非文本部分,例如表格和代码。此外,请不要省略 #给定提示词# 中的输入部分。
    你应当通过以下方法来使给定提示词复杂化:
    如果 #给定提示词# 包含对某些问题的询问,可以增加询问的深度和广度。
    你应尽力避免使 #改写后的提示词#(#Rewritten Prompt#)变得冗长,#改写后的提示词# 只能在 #给定提示词# 的基础上增加 10 到 20 个词。禁止在 #改写后的提示词# 中出现“#给定提示词#”、“#改写后的提示词#”、“given prompt”或“rewritten prompt”这些短语。
    **#给定提示词#:**
    {这里是指令。}
    **#改写后的提示词#:**

附录 B: Concretizing Prompt(具体化 Prompt)

  • 示例 B.1:用于深度演化的具体化提示 (Prompt for Concretizing of In-Depth Evolving)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    I want you act as a Prompt Rewriter.
    Your objective is to rewrite a given prompt into a more complex version to make those famous AI systems (e.g., ChatGPT and GPT4) a bit harder to handle. But the rewritten prompt must be reasonable and must be understood and responded by humans.
    Your rewriting cannot omit the non-text parts such as the table and code in #Given Prompt#:. Also, please do not omit the input in #Given Prompt#.
    You SHOULD complicate the given prompt using the following method:
    Please replace general concepts with more specific concepts.
    You should try your best not to make the #Rewritten Prompt# become verbose, #Rewritten Prompt# can only add 10 to 20 words into #Given Prompt#. ‘#Given Prompt#’, ‘#Rewritten Prompt#’, ‘given prompt’ and ‘rewritten prompt’ are not allowed to appear in #Rewritten Prompt#
    #Given Prompt#:
    {Here is instruction.} #Rewritten Prompt#:

    我希望你扮演一个提示词重写器(Prompt Rewriter)。
    你的目标是将给定的提示词改写成更复杂的版本,以使那些著名的人工智能系统(例如 ChatGPT 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。
    你的改写不能省略 #给定提示词# 中的非文本部分,例如表格和代码。此外,请不要省略 #给定提示词# 中的输入部分。
    你应当通过以下方法来使给定提示词复杂化:
    请将一般性概念替换为更具体的概念。
    你应尽力避免使 #改写后的提示词# 变得冗长,#改写后的提示词# 只能在 #给定提示词# 的基础上增加 10 到 20 个词。禁止在 #改写后的提示词# 中出现“#给定提示词#”、“#改写后的提示词#”、“given prompt”或“rewritten prompt”这些短语。
    **#给定提示词#:**
    {这里是指令。}
    **#改写后的提示词#:**

附录 C:Increased Reasoning Steps Prompt

  • 示例 C.1:用于深度演化的增加推理步骤提示 (Prompt for Increased Reasoning Steps of In-Depth Evolving)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    I want you act as a Prompt Rewriter.
    Your objective is to rewrite a given prompt into a more complex version to make those famous AI systems (e.g., ChatGPT and GPT4) a bit harder to handle. But the rewritten prompt must bereasonable and must be understood and responded by humans.
    Your rewriting cannot omit the non-text parts such as the table and code in #Given Prompt#:. Also, please do not omit the input in #Given Prompt#.
    You SHOULD complicate the given prompt using the following method:
    If #Given Prompt# can be solved with just a few simple thinking processes, you can rewrite it to explicitly request multiple-step reasoning.
    You should try your best not to make the #Rewritten Prompt# become verbose, #Rewritten Prompt# can only add 10 to 20 words into #Given Prompt#. ‘#Given Prompt#’, ‘#Rewritten Prompt#’, ‘given prompt’ and ‘rewritten prompt’ are not allowed to appear in #Rewritten Prompt#
    #Given Prompt#:
    {Here is instruction.} #Rewritten Prompt#:

    我希望你扮演一个提示词重写器(Prompt Rewriter)。
    你的目标是将给定的提示词改写成更复杂的版本,以使那些著名的人工智能系统(例如 ChatGPT 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。
    你的改写不能省略 #给定提示词# 中的非文本部分,例如表格和代码。此外,请不要省略 #给定提示词# 中的输入部分。
    你应当通过以下方法来使给定提示词复杂化:
    如果 #给定提示词# 可以通过几个简单的思考过程解决,你可以将其改写成明确要求多步推理的形式。
    你应尽力避免使 #改写后的提示词# 变得冗长,#改写后的提示词# 只能在 #给定提示词# 的基础上增加 10 到 20 个词。禁止在 #改写后的提示词# 中出现“#给定提示词#”、“#改写后的提示词#”、“given prompt”或“rewritten prompt”这些短语。
    **#给定提示词#:**
    {这里是指令。}
    **#改写后的提示词#:**

附录 D:Complicate Input Prompt

  • 示例 D.1:用于演化的复杂化输入提示 (Prompt for Complicate Input of Evolving)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    我希望你扮演一个提示词重写器。你的目标是使用数据格式将给定的提示词改写成更复杂的版本,以使那些著名的人工智能系统(例如 chatgpt 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。  
    你必须在 [改写后的提示词] 中添加 [XML 数据] 格式的文本作为输入数据。

    **#给定提示词#:**
    我正在使用这段 php 代码来获取 xml 数据

    **#改写后的提示词#:** 我有这个 xml 文件,我想获取 xml 数据以自动填充 HTML 表格,代码可以运行,但会导致表格内容重复。

    以下是 xml 数据:

    <root>
    <stats>
    <item>
    <day>2017-11-01</day>
    <impressions>2192</impressions>
    <money>1.96790003</money>
    </item>
    <item>
    <day>2017-11-02</day>
    <impressions>2824</impressions>
    <money>3.208500033</money>
    </item>
    <item>
    <day>2017-11-03</day>
    <impressions>3680</impressions>
    <money>3.321799981</money>
    </item>
    </stats>
    <total>
    <impressions>8696</impressions>
    <money>8.498200044</money>
    </total>
    <filter>
    <dateFrom>2017-11-01</dateFrom>
    <dateTo>2017-11-03</dateTo>
    <groupBy>day</groupBy>
    <format>xml</format>
    </filter>
    </root>

    我正在使用这段 php 代码来获取 xml 数据,但这段代码是从整个 xml 数据中获取,导致表格字段重复。

    <?php
    \$dom = new DOMDocument;
    \$dom -> load('http://example.com/', \$dateselected . '&dateTo =', \$dateselected2 . '&format=xml');
    \$day = \$dom->getElementsByTagName('day');
    \\$impressions = \$dom->getElementsByTagName('impressions');
    echo ( "<table>");
    foreach(\$day as \$node1) {
    foreach(\$impressions as \$node2) {
    echo '<tr>';
    echo "<td>", \$node1 -> textContent . "<td>";
    echo "<td>", \$node2 -> textContent . "<td>";
    echo "<td>", \$node2 -> textContent *0.5/1000 . "<td>";
    echo '</tr>';
    }
    }
    echo( "<table>");
    ?>

    有人能提示我如何修复这个问题吗?谢谢

    ####
  • 示例 D.2:用于演化的复杂化输入提示 (Prompt for Complicate Input of Evolving)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    我希望你扮演一个提示词重写器。你的目标是使用数据格式将给定的提示词改写成更复杂的版本,以使那些著名的人工智能系统(例如 chatgpt 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。  

    你必须在 [改写后的提示词] 中添加 [SQL 数据库] 格式的文本作为输入数据。

    **#给定提示词#**
    实现 SQL 查询结果

    **#改写后的提示词#**(必须包含一个具体的 SQL 数据库作为输入):
    有一个名为 messages 的表格,包含的数据如下所示:

    | Id | Name | Other_Columns |
    |----|------|---------------|
    | 1 | A | A_data_1 |
    | 2 | A | A_data_2 |
    | 3 | A | A_data_3 |
    | 4 | B | B_data_1 |
    | 5 | B | B_data_2 |
    | 6 | C | C_data_1 |

    如果我运行查询 `select * from messages group by name`,我将得到以下结果:

    | 1 | A | A_data_1 |
    | 4 | B | B_data_1 |
    | 6 | C | C_data_1 |

    哪个查询会返回以下结果?

    | 3 | A | A_data_3 |
    | 5 | B | B_data_2 |
    | 6 | C | C_data_1 |

    也就是说,应返回每个组中的最后一条记录。目前,我使用的查询是:

    SELECT *
    FROM (SELECT *
    FROM messages
    ORDER BY id DESC) AS x
    GROUP BY name

    但这看起来非常低效。是否有其他方法可以实现相同的结果?

    ####
  • 示例 D.3:用于演化的复杂化输入提示 (Prompt for Complicate Input of Evolving)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    我希望你扮演一个提示词重写器。你的目标是使用数据格式将给定的提示词改写成更复杂的版本,以使那些著名的人工智能系统(例如 chatgpt 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。  

    你必须在 [改写后的提示词] 中添加 [python 代码] 格式的文本作为输入数据。

    **#给定提示词#**
    转换 python 代码

    **#改写后的提示词#**(必须包含一个具体的 python 代码作为输入):
    我有以下 Python 代码:

    ```python
    cursor.execute("INSERT INTO table VALUES var1, var2, var3,")

    其中 var1 是整数,var2 和 var3 是字符串。
    如何编写变量名而不让 Python 将它们作为查询文本的一部分?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    * **示例 D.4:用于演化的复杂化输入提示 (Prompt for Complicate Input of Evolving)**  
    ```md
    我希望你扮演一个提示词重写器。你的目标是使用数据格式将给定的提示词改写成更复杂的版本,以使那些著名的人工智能系统(例如 chatgpt 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。
    你必须在 [改写后的提示词] 中添加 [HTML 页面] 格式的文本作为输入数据。

    **#给定提示词#**
    滚动整个 HTML 页面

    **#改写后的提示词#**(必须包含一个具体的 HTML 页面作为输入):
    我希望能够滚动整个页面,但不显示滚动条。
    在 Google Chrome 中,可以使用:


    ::-webkit-scrollbar {
    display: none;
    }

    但 Mozilla Firefox 和 Internet Explorer 似乎不支持这种方式。
    我也在 CSS 中尝试了:

    overflow: hidden;

    这样可以隐藏滚动条,但我无法再滚动了。有没有办法可以在隐藏滚动条的同时仍然能够滚动整个页面?

    请仅使用 CSS 或 HTML。

    ###
  • 示例 D.5:用于演化的复杂化输入提示 (Prompt for Complicate Input of Evolving)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    我希望你扮演一个提示词重写器。你的目标是使用数据格式将给定的提示词改写成更复杂的版本,以使那些著名的人工智能系统(例如 chatgpt 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。  

    你必须在 [改写后的提示词] 中添加 [Shell 命令] 格式的文本作为输入数据。

    **#给定提示词#**
    Shell scp 文件

    **#改写后的提示词#**(必须包含一个具体的 Shell 命令作为输入):
    我正在尝试从远程服务器 scp 一个文件到我的本地机器。只有端口 80 是可访问的。
    我尝试了:

    ```shell
    scp -p 80 username@www.myserver.com/root/file.txt .

    但出现了这个错误:cp: 80: No such file or directory
    如何在 scp 命令中指定端口号?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    * **示例 D.6:用于演化的复杂化输入提示 (Prompt for Complicate Input of Evolving)**  
    ```md
    我希望你扮演一个提示词重写器。你的目标是使用数据格式将给定的提示词改写成更复杂的版本,以使那些著名的人工智能系统(例如 chatgpt 和 GPT4)更难处理。但改写后的提示词必须是合理的,且必须能被人类理解并回应。

    你必须在 [改写后的提示词] 中添加 [JSON 数据] 格式的数据作为输入数据,添加 [JSON 数据] 代码作为输入代码。

    改写后的提示词必须是一个问题式指令。

    **#给定提示词#:**
    给定一个客户购买历史的 JSON 数据集,我们如何计算客户在同一商店进行重复购买的概率?我们能否利用条件概率公式:\(P(A|B)=P(A\cap B)/P(B)\),其中 A 表示客户进行重复购买的事件,B 表示客户再次在同一商店购买的事件?此外,我们如何应用这个公式来识别最有可能进行重复购买的客户群体?你能提供一个使用给定 JSON 数据集实现这个公式的示例吗?

    改写后的提示词必须是一个问题式指令。

    **#改写后的提示词#**(必须包含一个具体的 JSON 数据作为输入):

附录 E:Difficulty Judge Prompt

  • 示例 E.1:用于判断指令难度的提示 (Prompt for Juding the Difficulty of Instructions)
    1
    2
    3
    4
    5
    6
    我们希望您评估并评定以下问题的难度和复杂性。您应给出一个从 1 到 10 的整体分数,分数越高表示难度和复杂性越高。您必须仅给出分数,不提供任何其他理由。  

    **## 问题:**
    { 这里是指令。 }

    **## 分数:**

附录 F:Equal Prompt

  • 示例 F.1:用于判断两个指令是否等价的提示 (Prompt for Determining whether Two Instructions are Equal)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    以下是两个给 ChatGPT AI 的指令,您认为它们是否彼此等价,需满足以下要求:  

    1. 它们具有相同的约束和要求。
    2. 它们具有相同的询问深度和广度。

    第一个提示:{这里是第一个指令。}

    第二个提示:{这里是第二个指令。}

    您的判断(仅回答:等价 或 不等价。无需解释原因。):

附录 G:Math Judgement Prompt

  • 示例 G.1:用于判断指令是否与数学相关的提示 (Prompt for judging whether an instruction is math related)
    1
    2
    3
    请判断以下问题是否是一个数学问题,并仅返回 True 或 False,不提供任何解释。  

    问题:{指令}

附录 H:WizardEval Analysis

  • 论文收集了 Evol-Instruct 测试集,其中包含来自各种来源的真实世界人类指令,例如在线开源项目、平台和论坛
  • 论文分析了数据并识别出 29 种不同的技能,这些技能代表了人类的主要需求,例如代码生成与调试、数学、推理、复杂格式、写作、广泛学科等等
  • 图 6 展示了论文测试集中实例和技能的分布情况
  • 论文的测试集包含 218 个实例,每个实例都是针对特定技能的指令
  • 论文将论文的测试集与 Vicuna 的测试集进行了比较,后者是用于评估指令遵循模型的基准数据集
  • 论文发现 Vicuna 的测试集只有 80 个实例和 9 种技能,比论文的测试集小得多且多样性低得多
  • 图 4a 显示了测试数据的难度和复杂性如何在不同实例间变化
  • 论文的测试数据具有更均匀的分布,这意味着它包含不同难度和复杂性级别的指令
  • 另一方面,Vicuna 和 Alpaca 的分布则存在偏差,这意味着它们主要包含低难度和低复杂性的指令
  • 这表明这两个语料库无法处理对更复杂和要求更高场景的评估

附录 I:Different difficulty Annotators

  • 论文仅使用 ChatGPT 来事后分析生成指令的“难度”分布,但论文并未使用此分析结果来指导数据生成或模型训练
  • 为了探索 ChatGPT 执行难度分析的能力,论文采样了 600 条指令,并使用更强大的 GPT-4 模型和 5 位受过良好教育的人类标注者一起进行难度评估
  • 评估结果见表 3。结果表明,ChatGPT、GPT-4 和人工标注在难度变化趋势上表现出高度的一致性
  • 为了研究 ChatGPT 难度评分的正确性,论文增加了一个新的实验来测量 ChatGPT 与人类之间在难度判断上的一致性:
    • 论文每次以相等概率从六个数据集(Alpaca、ShareGPT、C1 到 C4)中随机选择两条指令,组成一对
    • 总共论文选择了 300 个指令对
    • 然后,论文请 ChatGPT 和 5 位受过良好教育的人类标注者判断在一个指令对中哪一条更难,人类之间的 Kappa 分数为 0.68,ChatGPT 与人类(多数投票)之间的 Kappa 分数为 0.66,这表明 ChatGPT 和人类标注者之间具有良好的一致性

附录 J: Cluster Scatter Plot(聚类散点图)

  • 广度演化旨在增强主题覆盖度、技能覆盖度和整体数据集的多样性。为了(定性分析)检查不同数据集的广度(多样性),论文首先使用 BERT
  • 对每条指令进行编码并获得其 768 维的嵌入向量,然后使用名为 t-SNE 的降维算法将嵌入维度降至 2 维,最后论文应用聚类算法 k-means 将每个数据集的指令划分为 20 个簇,以便进行直观的可视化
  • 如图 7 所示,论文数据集的数据点比 ShareGPT 和 Alpaca(Self-Instruct)的数据点更加分散,这表明论文的指令具有更好的主题多样性

附录 K: Human Evaluation Aspects

  • 标注者从以下五个维度判断哪个回答更好:
    • (1) 相关性 (Relevance): 评估模型正确理解上下文和问题语义含义的能力
    • (2) 知识性 (Knowledgeable): 模型是否能够准确使用各种详细的知识来解决问题
    • (3) 推理能力 (Reasoning): 评估模型执行正确推理过程或设计有效推理概念以解决问题的能力
    • (4) 计算能力 (Calculation): 评估模型是否能在数学、生物、化学和物理领域对所提供的公式进行准确的数学计算
    • (5) 准确性 (Accuracy): 评估模型对于给定指令是否能在相应领域正确执行

附录 L: Performance details of different checkpoints

  • 在论文中,论文使用 3 个训练周期 (epoch) 训练论文的模型,并且在上文的“第 4 节 实验”中仅报告了最终检查点的性能,以与之前的工作保持一致
  • 如下表 4 所示,论文报告了模型在不同周期(2.5, 2.75, 3)的检查点性能
    • 对于 13B 模型,我们可以看到除了 GSM8k 之外,在每个基准测试上表现最好的始终是 WizardLM-13b (ShareGPT Seed)
    • 对于 65b/70b 模型,论文也看到 WizardLM-70b 在所有基准测试中都是最好的
    • 因此,作者认为这主要是由模型训练过程中在某些基准测试上的波动引起的

NLP——HF-Trainer使用总结

  • 参考链接:
    • 官方链接:HuggingFace Trainer 官网

HuggingFace Trainer 整体介绍

  • HuggingFace Trainer 是 transformers 库中提供的一个高级训练 API,旨在简化深度学习模型的训练流程
    • 尤其是对 Transformer 类模型的支持很丰富
  • Trainer 封装了训练循环的核心逻辑,支持多种常见任务(如文本分类、问答、翻译等),并内置了以下多种关键功能:
    • 自动处理单卡/多卡训练、分布式训练
    • 集成日志记录(TensorBoard、W&B等)
    • 支持模型保存、加载和断点续训
    • 内置评估机制,可自定义评估指标
    • 支持早停(Early Stopping)、学习率调度等训练策略
    • 兼容 datasets 库的数据集格式
    • 支持混合精度训练
    • 支持梯度累积(Gradient Accumulation),即在较小的批次上累积梯度,以模拟更大的批次大小
  • HuggingFace Trainer 为 PyTorch 模型提供了完整的训练和评估循环,极大地简化了训练过程,让用户可以专注于模型、数据集和训练参数的配置,而无需手动编写复杂的训练代码
  • Trainer 是一个 开箱即用(out-of-the-box) 的训练工具,它将训练中的各种细节全部封装起来:
    • 前向传播(Forward pass)
    • 计算损失(Loss calculation)
    • 反向传播(Backward pass)
    • 参数更新(Optimizer step)
    • 学习率调整(Learning rate scheduling)
    • 检查点保存(Checkpoint saving)
    • 日志记录(Logging)

对 Trainer 的定制和生态集成

  • Trainer 提供了全面的默认功能,也设计了高度的可定制性
  • 可以通过 子类化(subclassing) 或 重写(overriding) 其内部方法来满足特定的需求,例如:
    • 重写 get_train_dataloader() 方法以自定义数据加载器
    • 重写 compute_loss() 方法以使用自定义的损失函数
    • 重写 compute_metrics() 方法以计算和报告自定义的评估指标
  • Trainer 与 Hugging Face 的其他库(如 datasets 和 accelerate)无缝集成,这使得数据加载、预处理和模型部署变得更加流畅
    • Trainer 特别针对 transformers 库中的模型进行了优化,但也可以与用户自定义的 PyTorch 模型一起使用
  • Trainer 的设计理念是 “开箱即用” ,对于大多数任务,只需要提供 model、args 和 train_dataset 即可
    • 如果需要更精细的控制,可以逐步添加 eval_dataset、data_collator 和 compute_metrics
    • 对于更高级的定制,如自定义训练循环或高级优化策略,则可以利用 callbacks 和 optimizers 等参数

Trainer 类使用示例

安装依赖

  • 首先安装必要的库:
    1
    pip install transformers datasets evaluate accelerate

完整代码示例

  • Trainer 抽象了整个训练循环,只需要提供模型、训练参数、数据集和数据整理器(data_collator),然后调用 trainer.train() 就可以开始训练
  • 以加载一个预训练好的 DistilBERT 模型为例,实现 IMDb 情感分析(正面/负面分类):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    from datasets import load_dataset
    from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    TrainingArguments,
    Trainer,
    DataCollatorWithPadding
    )
    import evaluate
    import numpy as np

    dataset = load_dataset("imdb") # 加载数据集(IMDb情感分析:正面/负面分类),包含train和test拆分

    model_name = "distilbert-base-uncased" # 可选择自己的模型
    tokenizer = AutoTokenizer.from_pretrained(model_name) # 加载分词器
    model = AutoModelForSequenceClassification.from_pretrained( # 用 Classification 类加载预训练模型
    model_name,
    num_labels=2 # 二分类任务(正面/负面)
    )
    # Trainer支持多种任务,只需替换对应的模型(如 `AutoModelForQuestionAnswering` 用于问答)和数据集即可

    def preprocess_function(examples): # 数据预处理函数(分词)
    return tokenizer(examples["text"], truncation=True, max_length=512)
    tokenized_dataset = dataset.map(preprocess_function, batched=True) # 对数据集应用预处理

    accuracy = evaluate.load("accuracy") # 定义评估指标(准确率)

    def compute_metrics(eval_pred): # 定义指标计算函数
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1) # 取概率最大的类别
    return accuracy.compute(predictions=predictions, references=labels)

    # 重点:配置训练参数(TrainingArguments)
    training_args = TrainingArguments(
    output_dir="./save", # 模型保存路径
    learning_rate=2e-5, # 学习率
    per_device_train_batch_size=8, # 每个设备的训练批次大小
    per_device_eval_batch_size=8, # 每个设备的评估批次大小
    num_train_epochs=3, # 训练轮数
    weight_decay=0.01, # 权重衰减系数(防止过拟合)
    eval_strategy="epoch", # 每轮结束后评估,旧版本中该参数名为 eval_strategy,新版本中为 evaluation_strategy
    save_strategy="epoch", # 每轮结束后保存模型
    load_best_model_at_end=True, # 训练结束后加载最优模型,确保最终保存的是验证集上表现最好的模型
    logging_dir="./logs", # 日志保存路径
    logging_steps=100, # 每100步记录一次日志
    )

    trainer = Trainer( # 重点:初始化Trainer
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset["train"], # 训练集
    eval_dataset=tokenized_dataset["test"], # 评估集
    tokenizer=tokenizer, # 传入 Tokenizer
    compute_metrics=compute_metrics, # 评估指标函数
    data_collator=DataCollatorWithPadding(tokenizer=tokenizer) # 使用动态padding
    )

    trainer.train() # 重点:开始训练

    # 测试训练结果:使用最优模型进行预测
    sample_text = "This movie is amazing! The acting was incredible and the plot was gripping."
    inputs = tokenizer(sample_text, return_tensors="pt").to(model.device)
    outputs = model(**inputs)
    predicted_class = outputs.logits.argmax().item()
    labels = ["negative", "positive"]
    print(f"预测结果:{labels[predicted_class]}")

启动命令

  • 根据需要启动 trainer_demo.py 的命令需要根据训练需求(如单卡训练、多卡训练、调试模式等)选择不同的方式
基础启动方式(单卡训练)
  • 直接用Python命令运行即可:
    1
    python trainer_demo.py

多卡训练(使用 accelerate)

  • 为了满足分布式训练,无需修改代码 ,通过 accelerate 工具(已在依赖中安装)启动即可,即 accelerate launch 命令即可启动多卡训练:

    1
    accelerate launch --num_processes=2 trainer_demo.py
  • 更多 accelerate launch 命令启动的用法见:/Notes/PyTorch/PyTorch——HF-Accelerate使用总结

调试模式(单步执行)启动

  • 如需调试代码(如设置断点),可使用 debugpy 或 Python 内置调试器:

    1
    python -m debugpy --wait-for-client --listen 5678 trainer_demo.py
    • 会在5678端口等待调试客户端连接(如 VS Code、PyCharm)
    • 适合排查训练逻辑或数据处理问题

启动混合精读训练

  • 启用 FP16/FP8 混合精度训练,有两种方法

  • 方法一:可在启动命令中指定 ,详情参考 accelerate launch 用法

    1
    accelerate launch --mixed_precision=fp16 trainer_demo.py
    • --mixed_precision 可选值:no(默认)、fp16、bf16、fp8(需特定GPU支持)
  • 方法二:可在 TrainingArguments 中配置:

    1
    2
    3
    4
    training_args = TrainingArguments(
    ...
    fp16=True, # 启用FP16混合精度
    )
    • 此时用基础命令启动即可:python trainer_demo.py

附录:Trainer 类的详细参数说明

核心参数(通常必须提供)

  • model: 训练或评估的 PyTorch 模型
    • 通常是 transformers 库中继承自 PreTrainedModel 的模型,比如 BertForSequenceClassification
    • 也可以传入一个自定义的 PyTorch 模型,但它需要与 Trainer 的其他组件兼容
  • args: 是一个 TrainingArguments 类的实例
    • 封装了所有的训练超参数,例如学习率、批次大小、训练轮次、日志目录等,是控制训练行为的主要方式
  • train_dataset: 训练数据集
    • 通常是 torch.utils.data.Dataset 或 Hugging Face datasets 库中的 Dataset 对象
    • 提供了这个参数后,就可以调用 trainer.train() 方法开始训练
  • eval_dataset: 评估数据集
    • Trainer 会定期在这个数据集上运行评估,以监控模型性能,并作为最佳模型的选择依据

数据处理相关参数

  • data_collator: 数据整理器
    • 作用是接收数据集中的多个样本,并将它们组合成一个批次(batch)
    • 对于 NLP 任务,它通常会负责对文本序列进行填充(padding)和张量化(tensorizing)
    • 如果不提供,Trainer 会使用一个默认的整理器,但通常只适用于固定长度的输入
    • 对于变长序列,需要提供一个 DataCollatorWithPadding
  • tokenizer: 分词器
    • 用于与 data_collator 协同工作,通常用于处理文本数据
    • 提供分词器可以让 Trainer 在内部自动处理一些数据预处理工作,比如与 DataCollatorWithPadding 结合使用来填充序列
  • model_init: 一个用于初始化模型的函数
    • 如果想使用 Trainer 的超参数搜索(hyperparameter search)功能 ,或者在每次训练前都重新初始化模型,而不是使用固定的 model 实例,就可以使用这个参数
    • 该函数应该不带参数,并返回一个模型实例

训练与评估相关参数

  • compute_metrics: 一个计算评估指标的函数
    • 这个函数接收一个 EvalPrediction 对象(包含预测结果和标签),并返回一个字典,其中包含在评估过程中报告的指标(例如准确率、F1 分数等)
  • callbacks: 一个 TrainerCallback 实例列表
    • 回调函数让你可以在训练循环的特定点(如每个训练步、每个 epoch 结束时)执行自定义逻辑,比如提前停止训练、在 TensorBoard 中记录额外信息等
  • optimizers: 一个包含 优化器和学习率调度器 的元组 (optimizer, lr_scheduler)
    • 这里配置的自定义优化器优先级比 TrainingArguments 中默认配置的更高

其他高级参数

  • preprocess_logits_for_metrics: 一个预处理逻辑的函数
    • 这个函数会在计算指标之前对模型的输出(logits)进行预处理
    • 例如,对于分类任务,你可能希望在计算准确率之前应用 argmax 来获取预测的类别索引
  • args.deepspeed: 这个参数不是直接给 Trainer 的,而是 TrainingArguments 的一部分
    • 可以传入一个 DeepSpeed 配置文件的路径或字典,从而启用 DeepSpeed 进行大规模分布式训练,以实现模型并行、梯度检查点等高级功能

附录:Trainer 中优化器设置

  • 在 HuggingFace Trainer 中设置优化器有两种主要方式:使用内置优化器或自定义优化器

使用内置优化器(简单方式)

  • Trainer 内置了常见优化器(如 Adamw_hf、adamw_torch 等),可通过 TrainingArguments 直接配置,无需额外代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from transformers import TrainingArguments

    training_args = TrainingArguments(
    output_dir="./saves",
    optim="adamw_torch", # 优化器设置,可选: adamw_hf, adamw_torch_fused, adafactor等,默认值为 adamw_hf
    learning_rate=2e-5, # 学习率,默认值 5e-5
    weight_decay=0.01, # 权重衰减(仅AdamW等支持),默认值为 0
    adam_beta1=0.9, # Adam优化器的beta1参数,默认值 0.9
    adam_beta2=0.999, # Adam优化器的beta2参数,默认值 0.999
    adam_epsilon=1e-8, # Adam优化器的epsilon参数,默认值 1e-8
    )
  • 常用内置优化器 :

    • adamw_hf:HuggingFace 实现的 AdamW(默认值)
    • adamw_torch:PyTorch 原生 AdamW
    • adamw_torch_fused:PyTorch 融合版 AdamW(速度更快)
    • adafactor:适合大模型的 Adafactor 优化器(无需设置学习率)

自定义优化器(灵活方式)

  • 如果需要使用 Trainer 不包含的优化器(如RAdam、SGD等),可通过重写 Trainer 类的 create_optimizer 方法实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class CustomOptimizerTrainer(Trainer):
    def create_optimizer(self): # 重写 Trainer 类
    params = [p for p in self.model.parameters() if p.requires_grad] # 定义需要优化的参数(排除冻结层)
    optimizer = optim.SGD( # 自定义优化器(这里以SGD为例,也可替换为其他优化器)
    params,
    lr=5e-5, # 学习率
    momentum=0.9, # SGD动量参数
    weight_decay=0.01
    )
    return optimizer # 返回优化器对象

    training_args = TrainingArguments(
    output_dir="./custom_optimizer_results",
    # ...
    # 注意:自定义优化器时,无需在TrainingArguments中设置optim参数
    )

    # 使用自定义 Trainer 初始化
    trainer = CustomOptimizerTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    tokenizer=tokenizer,
    )

    # 训练
    trainer.train()
  • 常用在需要对不同层设置不同学习率(如微调时固定底层,只训练顶层),可在 create_optimizer 中对参数分组:

    1
    2
    3
    4
    5
    6
    # PyTorch 官方库 `optim` 支持的分不同参数设置不同的学习率
    params = [
    {"params": model.pretrained_layer.parameters(), "lr": 1e-5}, # 底层参数
    {"params": model.classifier.parameters(), "lr": 1e-4} # 分类头参数
    ]
    optimizer = optim.AdamW(params, weight_decay=0.01)
  • 配合优化器使用时,可通过 TrainingArguments 的 lr_scheduler_type 配置调度策略(如线性衰减、余弦退火等)

  • 自定义优化器需兼容 PyTorch 的优化器接口(继承 torch.optim.Optimizer),否则可能导致训练异常

1…105106107…352
San Ye

San Ye

Stay Hungry. Stay Foolish.

704 posts
53 tags
© 2026 San Ye
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4