Jiahong 的个人博客

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

Python——OrderedDict类的使用


整体说明

  • OrderedDict 是 Python 标准库中的一个类,它位于 collections 模块下
  • 相对于普通字典,OrderedDict 能够记住元素插入的顺序,在迭代时,元素会按照插入的先后顺序被返回
  • OrderedDict 类对象的主要特点有:
    • 仍然是一个字典 :即仍然是 Key-Value 结构,且 Key 值不能重复
    • 保持插入顺序 :迭代时,元素会按照插入的顺序返回
    • 顺序敏感 :如果两个 OrderedDict 包含相同内容,但元素插入顺序不同,它们会被视为不相等
    • 支持移动操作 :可以通过 move_to_end() 方法将元素移动到开头或末尾
    • 删除操作保留顺序 :删除元素后,剩余元素的顺序保持不变

OrderedDict 的基本用法

  • OrderedDict 的一些常见操作示例:
    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
    from collections import OrderedDict

    od = OrderedDict()
    od['a'] = 1
    od['b'] = 2
    od['c'] = 3
    od['b'] = 10

    # 迭代时保持插入顺序
    for key, value in od.items():
    print(key, value) # 输出: a 1, b 10, c 3

    print('---')
    # 移动元素到开始
    od.move_to_end('b', last=False)
    for key, value in od.items():
    print(key, value) # 输出: b 10, a 1, c 3

    print('---')
    # 移动元素到末尾
    od.move_to_end('b', last=True)
    for key, value in od.items():
    print(key, value) # 输出: a 1, c 3, b 10

    print('---')
    # 删除元素后结果不变
    del od['c']
    for key, value in od.items():
    print(key, value) # 输出: a 1, b 10

比较:与普通字典的区别

  • 在 Python 3.7 及以后的版本中,普通字典也会保持插入顺序,但 OrderedDict 仍然有其独特优势:
    • 明确的顺序语义 :使用 OrderedDict 可以更清晰地表达代码对顺序的依赖
    • 支持顺序相关的方法 :如 move_to_end()、popitem(last=False) 等,且这两个函数都可以通过 last 参数指定是操作第一个还是最后一个
    • 顺序敏感的比较 :两个字典即使包含元素相同,但元素顺序不同也会被视为不等

Math——博弈论-Shapley-Value


整体说明

  • Shapley Value(沙普利值) 是合作博弈论中的一个核心概念,由诺贝尔经济学奖得主 Lloyd Shapley 于 1953 年提出
  • Shapley Value 用于解决一个基本问题:在一个多人合作博弈中,如何公平地分配所有合作者共同创造的总收益?
  • Shapley Value 给出了一种按照每个参与者的“边际贡献”来分配总收益的数学方法

Shapley Value 核心思想

  • Shapley Value 分配的唯一标准是:每个参与者获得的收益,应该等于他在所有可能的加入顺序下,对联盟平均边际贡献的期望值

Shapley Value 数学定义

  • 场景设定:
    • 参与者集合为 \(N = \{1, 2, \dots, n\}\)
    • 特征函数 \(v(S)\):表示任意子集 \(S \subseteq N\) 合作所能创造的总价值(满足 \(v(\emptyset) = 0\))
  • 参与者 \(i\) 的 Shapley Value \(\phi_i(v)\) 定义为:
    $$
    \phi_i(v) = \frac{1}{n!} \sum_{\pi \in \Pi_n} \left[ v(S_{\pi,i} \cup \{i\}) - v(S_{\pi,i}) \right]
    $$
  • 其中:
    • \(\Pi_n\) 是所有 \(n!\) 种排列(加入顺序)的集合
    • \(S_{\pi,i}\) 是在排列 \(\pi\) 中出现在参与者 \(i\) 之前的所有参与者集合
    • 方括号内就是参与者 \(i\) 加入当前联盟 \(S_{\pi,i}\) 时带来的边际贡献

Shapley Value 直观理解

  • 假设有三个朋友 A、B、C 一起做项目,总共赚了 100 元,他们想知道:每个人应该分多少才公平 ?
  • Shapley Value 的思路是:
    • 1)考虑所有可能的加入顺序(3! = 6 种)
    • 2)在每种顺序下,计算每个人“刚好加入时”带来的价值增量
    • 3)把每个人在所有顺序下的边际贡献求平均
  • 例如:
    加入顺序 A 的边际贡献 B 的边际贡献 C 的边际贡献
    A → B → C v(A) v(AB) - v(A) v(ABC) - v(AB)
    A → C → B v(A) v(ABC) - v(AC) v(AC) - v(A)
    … … … …
  • 最终:
    $$
    \phi_A = \frac{\text{A 在 6 种顺序下的边际贡献之和} }{6}
    $$

更细节的示例

  • 假设一个简单的投票博弈:
    • 参与者:A、B、C
    • 总收益:1 元
    • 获胜规则:多数票(至少 2 票)
  • 特征函数:
    • \(v(\{A,B\}) = 1\)
    • \(v(\{A,C\}) = 1\)
    • \(v(\{B,C\}) = 1\)
    • \(v(\{A,B,C\}) = 1\)
    • 其余子集为 0
  • 计算 A 的 Shapley Value:
    • 考虑 6 种顺序:
      • A 先来:边际贡献 = 0(A 一个人无法获胜)
      • A 在 B 之后加入(B → A):此时已有 B(0 票),A 加入后达到 2 票 → 边际贡献 = 1
      • A 在 C 之后加入 → 边际贡献 = 1
      • A 在 B、C 之后加入 → 边际贡献 = 0(已经赢了)
  • 平均后:\(\phi_A = \frac{1}{3}\)
  • 同理:
    • \(\phi_B = \frac{1}{3}\)
    • \(\phi_C = \frac{1}{3}\)
  • 结果:三个人平分收益,符合多数投票博弈的对称性直觉

Shapley Vlaue 满足四大公理(唯一性保证)

  • Shapley Value 是唯一同时满足以下四条公理的分配方式:
    • 效率性(Efficiency / 帕累托最优) :所有参与者的 Shapley Value 之和等于总收益:
      $$ \sum_{i=1}^n \phi_i(v) = v(N) $$
      • 合作产生的总收益必须被全部分配完,既没有剩余,也没有透支,即所有人的沙普利值之和等于团队总收益
    • 对称性(Symmetry) : 如果两个参与者对任何联盟的贡献相同,则他们获得相同的收益
    • 线性性(Additivity) : 若游戏可分解为两个独立子游戏的求和,则 Shapley Value 也相应求和
    • 哑元性(Dummy Player / 零贡献者) : 如果一个参与者对所有联盟的边际贡献均为 0,则其收益为 0
  • 这些公理使得 Shapley Value 成为一种公平、无偏、可解释 的信用分配方式

Shapley Value 的其他定义形式

  • Shapley Value 是一种公平分配合作总收益的方法:每个人得到的等于他在所有可能加入顺序下的平均边际贡献
  • 假设有一群人要组成一个团队,每个人是一个接一个加入的
    • 当一个人加入团队时,团队的总收益会因为他的加入而增加,这个增加的量就是他的“边际贡献”
    • 参与者加入团队的先后顺序会影响他的边际贡献
    • 沙普利值的做法是:考虑所有可能的加入顺序,计算该参与者在每种顺序下的边际贡献,然后求平均值

Shapley Value 数学公式

  • 沙普利值的计算公式如下:
    $$ \phi_i(v) = \sum_{S \subseteq N \setminus \{i\} } \frac{|S|! (n - |S| - 1)!}{n!} (v(S \cup \{i\}) - v(S)) $$
    • \(N\):所有参与者的集合
    • \(n\):参与者的总人数
    • \(S\):不包含参与者 \(i\) 的某个子联盟
    • \(|S|\):子联盟 \(S\) 中的人数
    • \(v(S)\):子联盟 \(S\) 能创造的收益
    • \(v(S \cup \{i\}) - v(S)\):参与者 \(i\) 加入联盟 \(S\) 后带来的边际贡献
    • \(\frac{|S|! (n - |S| - 1)!}{n!}\):这是一个权重,代表在所有可能的排列顺序中,刚好形成联盟 \(S\) 并且 \(i\) 紧接着加入的概率

Math——证明笔记-对数似然的梯度期望为零


证明目标(对数似然的梯度期望为零)

  • 证明恒等式
    $$
    \mathbb{E}_{z \sim m_\theta(\cdot|x)} \left[ \nabla_\theta \log m_\theta(z \mid x) \right] = 0,
    $$
  • 这个恒等式是对数似然梯度的期望为零的性质,是强化学习和变分推断中的一个基本结果

证明过程

符号定义

  • 给定输入 \( x \),模型输出一个概率分布 \( m_\theta(z \mid x) \)(对离散 \( z \) 是概率质量函数,对连续 \( z \) 是概率密度函数)
  • 我们有归一化条件(离散):
    $$
    \sum_z m_\theta(z \mid x) = 1
    $$
  • 或(连续)
    $$
    \int_z m_\theta(z \mid x) , dz = 1
    $$

期望的定义

  • 对于连续情况(离散类似):
    $$
    \mathbb{E}_{z \sim m_\theta(\cdot|x)} \left[ \nabla_\theta \log m_\theta(z \mid x) \right]
    = \int_z m_\theta(z \mid x) \cdot \nabla_\theta \log m_\theta(z \mid x) , dz
    $$

代入梯度对数项

  • 因为
    $$
    \nabla_\theta \log m_\theta(z \mid x) = \frac{\nabla_\theta m_\theta(z \mid x)}{m_\theta(z \mid x)},
    $$
  • 所以:
    $$
    m_\theta(z \mid x) \cdot \nabla_\theta \log m_\theta(z \mid x) = \nabla_\theta m_\theta(z \mid x)
    $$
  • 于是期望变成:
    $$
    \int_z \nabla_\theta m_\theta(z \mid x) , dz
    $$

交换梯度与积分

  • 如果 \( m_\theta(z \mid x) \) 对 \( \theta \) 足够光滑,且积分与梯度可交换(通常成立),则:
    $$
    \int_z \nabla_\theta m_\theta(z \mid x) , dz
    = \nabla_\theta \int_z m_\theta(z \mid x) , dz
    $$

利用归一化条件

  • 归一化条件下
    $$
    \int_z m_\theta(z \mid x) , dz = 1 \quad \Rightarrow \quad \nabla_\theta 1 = 0
    $$
  • 因此:
    $$
    \mathbb{E}_{z \sim m_\theta(\cdot|x)} \left[ \nabla_\theta \log m_\theta(z \mid x) \right] = 0
    $$

附录:恒等式的直观解释

  • \( \nabla_\theta \log m_\theta(z \mid x) \) 称为 score function
  • 它的期望为零,是因为概率分布的总概率必须保持为 1
    • 改变参数 \(\theta\) 时,概率质量在不同 \(z\) 间重新分配,但增加某些地方的概率必然减少其他地方的概率,平均起来“变化方向”的期望为零
  • 这个性质在 REINFORCE 算法中用于引入基线(baseline)而不引入偏差,因为对任意只依赖 \(x\) 而不依赖 \(z\) 的 \(b(x)\):
    $$
    \mathbb{E} \left[ b(x) \cdot \nabla_\theta \log m_\theta(z \mid x) \right]
    = b(x) \cdot \mathbb{E} \left[ \nabla_\theta \log m_\theta(z \mid x) \right] = 0
    $$

NLP——LLM排行榜


整体说明

  • 目前,大模型(如LLM、多模态模型等)的评测和排名主要通过一些权威的基准测试和第三方平台进行
  • 本文记录并持续更新一些常见的在线排名网站和评测平台,涵盖不同领域的模型能力评估

LMSYS Chatbot Arena(LMArena)

整体介绍

  • 链接:https://lmarena.ai/
  • 领域:通用大模型排名
  • 基于人类反馈的实时对战排名(如 GPT-4、Claude、Gemini 等)
  • 采用 Elo 评分机制,反映用户偏好
  • 包含闭源模型
  • Chatbot Arena LLM Leaderboard: Community-driven Evaluation for Best LLM and AI chatbots
  • LmArena(原LMSYS)是一个由加州大学伯克利分校SkyLab和LMSYS研究团队开发的开源平台,专注于通过众包方式评估和比较不同AI模型的性能
  • LMArena 是目前大家最相信的人类偏好排行榜

文本子榜详细介绍

  • 对于 文本子榜,LMArena 会报告两种分数:
    • 基础分(wo style control):arena.ai/zh/leaderboard/text/overall-no-style-control
      • 基于模型对战,人类原始打分结果得到的 Elo Rating 分(不做任何修改)
    • 风格分(w style control):arena.ai/zh/leaderboard/text/overall
      • 默认是打开 Style Control 的形式:https://arena.ai/zh/leaderboard/text
      • Style Control 是指在 基础分 的基础上,通过一些消偏模型将模型的回复长度、格式等对齐后得到的 “回复内容” 本身的得分
    • 实践中,基础分和风格分的相对值可以看出一个模型风格的好坏来
      • 如果一个模型的 基础分 > 风格分,说明模型风格不错(比如回复较短)
      • 如果一个模型的 风格分 > 基础分,说明模型回复可能风格不行(比如回复偏长)
  • 从之前的经验看,LMArena 对战并不是严格遵循相似能力模型对战,而是更多的让部分模型参战(比如靠前的 Gemini 系列/Claude 系列等的参战频率就比较高),至少看着模型的 vote 数量是不完全对齐的
  • LMArena 有很多细分的榜单,比如 Text 榜单下还有类似 Arena Hard V2 中提到的 Hard Prompts 榜单和 Creative Writing 榜单等

文本子榜 AutoEval & 人工测评

  • 可以提交自己的模型(付钱)让对方进行打分(提交包括模型名称,URL,API Key 等信息即可),AutoEval 提交后大概几个小时可以得到结果,一般包含三个文件:
    • autoeval_leaderboards__{model_name}__xxx.html: 包含参与本次对战的模型整体评分
    • autoeval_report__{model_name}__xxx.html: 包含参与本次对战的报告细节分数
    • {model_name}__1.jsonl: 包含本次对战的 Prompt 和 Response 详细细节
  • 人工测评:将模型真实部署到线上共人类真实响应,需要的时间较久,收费也更高
  • 注:不同榜单的收费也不一样

OpenCompass

  • 链接:https://rank.opencompass.org.cn/home
  • 领域:通用大模型排名、多模态模型排名、对战排名均有
  • 包含豆包、Qwen、DeepSeek等
  • 司南 OpenCompass 是由上海人工智能实验室(Shanghai AI Lab)推出的一个开源、中立、全面的 LLM 评测体系,旨在对各类大模型进行系统性、标准化的能力评估与排名

LiveCodeBench

  • 链接:https://livecodebench.github.io/leaderboard.html
  • 领域:代码能力排名

Open LLM Leaderboard (Hugging Face)

  • 链接:https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard
  • 领域:通用大模型排名
  • 评估开源大模型在多项任务(如ARC、HellaSwag、MMLU等)上的表现
  • 涵盖模型:LLaMA、Falcon、Mistral等诸多模型,还有许多名字不出名的是基于其他模型微调后改名的
  • 仅评估开源模型

Stanford HELM (Holistic Evaluation of Language Models)

  • 链接:https://crfm.stanford.edu/helm/
  • 领域:通用大模型排名
  • 斯坦福的全面评测框架,覆盖准确性、公平性、鲁棒性等维度
  • 其中可选很多评估标注,比如MMLU,Finance等

C-Eval (中文评测基准)

  • 链接:https://cevalbenchmark.com/static/leaderboard.html
  • 领域:中文测评
  • 评估中文知识、推理能力的测试集,涵盖52个学科
  • 排名包含:GPT-4、ChatGLM、通义千问等

SuperCLUE (中文通用大模型评测)

  • 链接:https://www.superclueai.com/
  • 领域:中文测评
  • 中文版综合性评测,包括基础能力、专业任务等

MMBench

  • 链接:https://mmbench.opencompass.org.cn/leaderboard
  • 领域:多模态模型排名
  • 评估图文理解、生成能力的基准(如GPT-4V、Gemini Vision)

GLUE/SuperGLUE

  • 链接:https://gluebenchmark.com/
  • 领域:自然语言理解
  • 经典NLU任务评测,但近年逐渐被更大基准取代,都是一些比较老的模型评估

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),否则可能导致训练异常

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——Megatron框架的使用

  • 参考链接:
    • 原始论文:Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism, arXiv 20200313, NVIDIA
    • 官网主页:Megatron Core User Guide
    • GitHub开源地址:github.com/NVIDIA/Megatron-LM

Megatron 整体说明

  • Megatron 是由 NVIDIA 开发的一个用于训练 LLM 的高性能框架
  • Megatron 专为分布式训练优化,支持模型并行、数据并行和混合精度训练,能够在数千个 GPU 上高效运行
  • Megatron 集成了多种优化技术,包括张量并行、管道并行、激活检查点等,显著提升了超大规模模型的训练效率
  • Megatron 通常与 DeepSpeed 结合使用,形成 Megatron-DeepSpeed 框架,进一步增强训练能力

Megatron 安装

  • Megatron 的安装需要结合 NVIDIA 的环境和依赖,以下是详细的安装步骤:
  • 推荐在 Linux 系统上安装

安装依赖项 PyTorch

  • 根据 CUDA 版本安装对应的 PyTorch:
    1
    2
    3
    4
    5
    # 对于CUDA 11.8
    pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

    # 对于CUDA 12.1
    pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

安装 Megatron

  • 下载源码并安装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    git clone https://github.com/NVIDIA/Megatron-LM.git
    cd Megatron-LM

    pip install -r requirements.txt

    # 安装apex(用于混合精度训练)
    git clone https://github.com/NVIDIA/apex
    cd apex
    pip install -v --disable-pip-version-check --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" ./
    cd ..

    # 安装其他依赖
    pip install sentencepiece regex datasets

验证安装是否成功

  • 创建一个简单的Python脚本来验证 Megatron 是否正确安装:
    1
    2
    3
    4
    5
    6
    import torch
    from megatron import get_args
    from megatron.model import GPTModel

    # 能导入相关包,说明 Megatron 已成功安装
    print("Megatron安装成功!")

Megatron 数据处理

  • 详情参考:github.com/NVIDIA/Megatron-LM

  • 数据预处理负责将 .jsonl 的文本数据 tokenize 并处理成 Megatron 可以直接读取的数据格式(.bin 和 .idx 类型的文件),减少训练时的数据处理时间

  • 准备 .jsonl 文件,文件格式如下:

    1
    2
    {"text": "Your training text here..."}
    {"text": "Another training sample..."}
  • 数据预处理:

    1
    2
    3
    4
    5
    6
    7
    python tools/preprocess_data.py \
    --input data.jsonl \
    --output-prefix processed_data \
    --tokenizer-type HuggingFaceTokenizer \
    --tokenizer-model /path/to/tokenizer.model \
    --workers 8 \
    --append-eod
    • output-prefix:输出文件的前缀
    • append-eod:是否添加 EOD Token?
    • 注意:还可以根据需要设置 split_sentences 参数,对文档进行拆分成 sentence 再做 tokenize

Megatron 训练开源标准大模型

  • Megatron 支持对一些标准的开源大模型进行训练,比如 GPT2,此时不需要修改代码
  • 这种标准流程包含以下两部分:
    • 需要处理数据为 Megatron 支持的格式
    • 使用命令行启动任务
  • 本文暂不对这部分进行详细讲解

Megatron 训练自定义模型

核心思路

  • 需要做到如下事情
    • 1)先把 Megatron 自带的 GPT/BERT/T5 的「壳」理解透
    • 2)再把自己的网络结构「套」进去
    • 3)最后复用 Megatron 的并行、优化器、数据管道即可
  • 基本思路:
    • 使用 Megatron 训练自定义模型,不需要改 Megatron 核心 ,只实现 3-4 个钩子即可
    • 一些底层的高阶功能,如并行、混合精度、检查点 全部复用官方实现
    • 任何模型(CNN、RWKV、RetNet…)只要包装成 MegatronModule 并返回 loss ,都能用 Megatron 训练

目录结构

  • 目录结构如下,建议自建文件都放到统一的新文件夹 my_model 下
    1
    2
    3
    4
    5
    6
    7
    8
    Megatron-LM/
    ├─ megatron/ # 官方代码不动
    ├─ examples/ # 官方示例
    ├─ my_model/ # 我们自己的
    │ ├─ __init__.py
    │ ├─ model.py # 自定义网络
    │ ├─ layer.py # 自定义层
    │ └─ train.py # 入口脚本

自定义模型的 4 个关键钩子说明

  • Megatron 的训练循环入口是 pretrain(),它通过 4 个可插拔函数 决定「数据长什么样、模型长什么样、前向怎么算、验证看啥指标」:
    钩子名称 作用 示例文件
    model_provider 返回 nn.Module my_model/model.py
    train_valid_test_dataset_provider 返回三个 Dataset my_model/data.py
    forward_step_func 定义一次前向/损失 my_model/train.py
    process_non_loss_data_func TensorBoard 画额外指标(可选) my_model/train.py

模型钩子(model_provider)

  • 新建 my_model/model.py:
    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
    from megatron.model.module import MegatronModule
    from megatron import get_args

    class MyCustomModel(MegatronModule):
    def __init__(self, num_tokentypes=0):
    super().__init__()
    args = get_args()
    self.embed = nn.Embedding(args.padded_vocab_size, args.hidden_size)
    # 这里换成自己的自定义层
    self.backbone = ConvNextBackbone(args.hidden_size, args.num_layers)
    self.lm_head = nn.Linear(args.hidden_size, args.padded_vocab_size)

    def forward(self, input_ids, position_ids, attention_mask, labels=None):
    x = self.embed(input_ids)
    x = self.backbone(x, attention_mask) # [b, s, h]
    logits = self.lm_head(x) # [b, s, V]

    if labels is None:
    return logits
    loss = F.cross_entropy(logits.view(-1, logits.size(-1)), labels.view(-1))

    # Megatron 的 GPTModel 实现也是在 Model.forward 直接返回 loss 的
    return loss

    def model_provider(pre_process=True, post_process=True):
    """给 Megatron 调用的工厂函数,负责返回 MegatronModule 类的模型对象"""
    return MyCustomModel()

数据钩子(train_valid_test_dataset_provider)

  • 把 .jsonl/txt 转成 Megatron 的 IndexedDataset:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # my_model/data.py
    from megatron.data.dataset_utils import build_train_valid_test_datasets
    from megatron import get_args

    def train_valid_test_dataset_provider(train_val_test_num_samples):
    args = get_args()
    return build_train_valid_test_datasets(
    data_prefix=args.data_path,
    splits_string=args.split,
    train_valid_test_num_samples=train_val_test_num_samples,
    seq_length=args.seq_length,
    masked_lm_prob=0.15 if args.task == 'BERT' else 0.0,
    seed=args.seed,
    skip_warmup=True,
    )

前向钩子(forward_step_func)

  • 定义前向过程(包含 loss 计算,需要返回 loss)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # my_model/train.py
    from megatron.training import get_model
    from megatron.utils import average_losses_across_data_parallel_group
    from megatron import get_args

    def forward_step(data_iterator, model):
    """一次 micro-batch 的前向"""
    args = get_args()
    tokens = next(data_iterator)['text'].long().cuda()
    labels = tokens[:, 1:].contiguous()
    tokens = tokens[:, :-1]
    position_ids = torch.arange(tokens.size(1), device=tokens.device).unsqueeze(0)
    attention_mask = (tokens != args.pad_token_id).unsqueeze(1).unsqueeze(2)

    # 因为传入 labels 参数时,模型的 forward 已经计算出来 loss 了,这里可以不需要自己写参数
    loss = model(tokens, position_ids, attention_mask, labels)
    reduced = average_losses_across_data_parallel_group([loss])

    # 第一个返回值必须是 loss,第二个返回值可以是任意想要记录的辅助信息
    return loss, {'lm loss': reduced[0]}

将钩子传入 pretrain() 函数

  • 调用 pretrain(),传入前面定义的钩子
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # my_model/train.py
    from megatron.training import pretrain

    if __name__ == '__main__':
    pretrain(
    train_valid_test_dataset_provider, # 用于提供训练、验证和测试数据集的函数或模块
    model_provider, # 用于构建模型的函数,调用它可返回模型实例
    ModelType.encoder_or_decoder, # 或 encoder_decoder
    forward_step, # 定义模型前向传播步骤的函数,包括输入处理、模型计算和损失计算等
    process_non_loss_data_func=None # tensorboard 的 额外指标,process_non_loss_data_func 在这里暂未实现
    )

启动训练

  • 单节点 8 卡示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    torchrun --nproc_per_node=8 my_model/train.py \
    --tensor-model-parallel-size 2 \
    --pipeline-model-parallel-size 2 \
    --num-layers 12 \
    --hidden-size 768 \
    --data-path data/my_corpus \
    --seq-length 1024 \
    --micro-batch-size 4 \
    --global-batch-size 64 \
    --train-iters 50000 \
    --lr 1e-4 \
    --save checkpoints/myconvnext

附录:使用中遇到的常见问题

  • 显存 OOM 问题:
    • 解决方案:降 micro-batch、开 --recompute-activations、或加大 TP/PP
  • loss 不收敛 问题:
    • 检查学习率、warmup、初始化;确认 pad_token_id 设置正确
  • 多机多卡时卡死不动:
    • 确认 MASTER_ADDR/MASTER_PORT 一致,NCCL 版本统一等

附录:pretrain 函数的详细说明

  • Megatron-LM 中的 pretrain 函数是模型预训练的核心入口,定义在 megatron/training.py 文件中

pretrain() 函数参数

  • train_valid_test_dataset_provider:用于提供训练、验证和测试数据集的函数或模块
  • model_provider:用于构建模型的函数,调用它可返回模型实例
  • model_type:模型的类型,如ModelType.encoder_or_decoder
  • forward_step_func:定义模型前向传播步骤的函数,包括输入处理、模型计算和损失计算等
  • valid_forward_step_func:可选参数,用于验证阶段的前向传播函数
  • args_defaults:可选参数,包含默认的参数设置

pretrain() 函数内部执行的主要流程

  • 第一步,初始化Megatron :
    • 调用 initialize_megatron 函数,初始化 Megatron-LM 所需的分布式环境,包括设置分布式通信后端、初始化分布式进程组、配置日志记录等
    • 还会调用get_args()与get_timers()函数获取配置参数与计时器,并设置 PyTorch JIT 融合选项,同步启动时间
  • 第二步,设置模型、优化器和学习率计划 :
    • 调用setup_model_and_optimizer函数,传入model_provider和model_type,返回模型、优化器以及学习率调度器
  • 第三步,获取训练/验证/测试数据集 :
    • 根据 args.virtual_pipeline_model_parallel_size 是否为 None 来判断是否需要进行虚拟流水线模型并行处理
    • 如果不进行虚拟流水线模型并行,则直接调用build_train_valid_test_data_iterators函数,获取训练、验证和测试数据迭代器
  • 第四步,调用train函数训练模型 :
    • 判断 args.do_train 和 args.train_iters 是否满足条件,若满足则调用 train 函数执行训练过程
    • train 函数接收多个参数,包括前向传播步骤函数、模型、优化器、学习率调度器、训练数据和验证数据迭代器等,并返回最后一次迭代的索引和到目前为止执行的浮点运算次数

附录:如何修改优化器

  • Megatron-LM 里“指定/切换优化器”有两种主流做法:
    • 第一种:不动源码,靠命令行参数(最简单,官方已内置)
    • 第二种:改源码,注册自定义优化器(想换 Lion、RAdam 等第三方优化器时用)

第一种:命令行直接切换(无需改代码)

  • 注:Megatron 从 2024-10 之后的版本开始,把 optimizer 也暴露成了 CLI 参数:
    主要参数 取值 说明
    --optimizer adam, sgd 默认 adam,会自动选用 Apex 的 FusedAdam
    --adam-beta1 0.9
    --adam-beta2 0.95
    --adam-eps 1e-8
    --sgd-momentum 0.9 只在 --optimizer sgd 时生效
    --weight-decay 0.1 通用
    --clip-grad 1.0 梯度裁剪
  • 示例:把优化器换成 SGD + momentum 的启动脚本如下:
    1
    2
    3
    4
    5
    torchrun --nproc_per_node=8 pretrain_gpt.py \
    ... \
    --optimizer sgd \
    --sgd-momentum 0.9 \
    --weight-decay 1e-4

第二种:源码级自定义优化器(以 Lion 为例)

  • 当你想用官方未内置的优化器(Lion、AdaFactor、RAdam等)时,只要三步:

  • 第一步:在 megatron/core/optimizer/ 里新建文件 lion.py,并定义自己的优化器类

    1
    2
    3
    4
    import torch
    class Lion(torch.optim.Optimizer): # 继承 Optimizer 类
    def __init__(self, params, lr=1e-4, betas=(0.9, 0.99), weight_decay=0.0):
    ...
    • 问题:需要特殊处理的优化器,比如可能涉及其他更多超参数的优化器,还需要考虑
  • 第二步:在 megatron/core/optimizer/__init__.py 的 _get_megatron_optimizer() 中注册新的优化器

    1
    2
    elif opt == 'lion':
    optimizer = Lion(param_groups, lr=args.lr, weight_decay=args.weight_decay)
  • 第三步:启动脚本里添加新的优化器选项

    1
    --optimizer lion
  • Megatron 会自动把上述自定义的 Lion 优化器包装到 DistributedOptimizer(或 DeepSpeed ZeRO,如果启用)里,梯度同步、fp16/bf16 主参数、checkpoint 保存/加载全部复用现有逻辑


附录:CPU Offload & 显存优化

  • Megatron 支持把优化器状态卸载到 CPU 以减少显存:
    1
    2
    --optimizer-cpu-offload \
    --optimizer-offload-fraction 0.8 # 80% 状态放 CPU

附录:Megatron 使用代码简单示例

  • 下面是一个使用 Megatron 训练 GPT 模型的示例代码:

    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
    import os
    import torch
    from megatron import get_args
    from megatron import mpu
    from megatron.initialize import initialize_megatron
    from megatron.training import setup_model_and_optimizer
    from megatron.model import GPTModel
    from megatron.training import train_step
    from megatron.data.gpt_dataset import build_train_valid_test_datasets

    def main():
    # 初始化Megatron
    initialize_megatron(extra_args_provider=None, args_defaults={
    'tokenizer_type': 'GPT2BPETokenizer',
    'micro_batch_size': 4,
    'global_batch_size': 32,
    'lr': 0.00015,
    'min_lr': 0.00001,
    'lr_decay_style': 'cosine',
    'weight_decay': 0.1,
    'clip_grad': 1.0,
    'lr_warmup_fraction': 0.01,
    'num_layers': 24,
    'hidden_size': 1024,
    'num_attention_heads': 16,
    'seq_length': 1024,
    'max_position_embeddings': 1024,
    'vocab_size': 50257, # GPT-2 vocab size
    'tensor_model_parallel_size': 2,
    'pipeline_model_parallel_size': 2,
    'pipeline_model_parallel_split_rank': 0,
    'fp16': True,
    'bf16': False,
    'seed': 1234,
    })

    args = get_args()

    # 构建数据集
    train_ds, valid_ds, test_ds = build_train_valid_test_datasets(
    data_prefix=args.data_path,
    data_impl=args.data_impl,
    splits_string=args.split,
    train_valid_test_num_samples=[args.train_samples, args.valid_samples, args.test_samples],
    seq_length=args.seq_length,
    seed=args.seed,
    skip_warmup=(not args.mmap_warmup)
    )

    # 设置模型和优化器
    model, optimizer, lr_scheduler = setup_model_and_optimizer()

    # 训练循环
    iteration = 0
    max_iterations = args.train_iters

    while iteration < max_iterations:
    loss = train_step(model, optimizer, lr_scheduler, train_ds)
    if torch.distributed.get_rank() == 0 and iteration % args.log_interval == 0:
    print(f"迭代: {iteration}/{max_iterations}, 损失: {loss.item()}")
    iteration += 1

    if __name__ == "__main__":
    main()
  • 通常使用以下命令启动训练(假设每台机器使用 4 个 GPU):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 使用torchrun启动分布式训练
    torchrun --nproc_per_node=4 --master_port=12345 your_script.py \
    --data-path /path/to/your/dataset \
    --vocab-file /path/to/vocab.json \
    --merge-file /path/to/merges.txt \
    --save /path/to/save/checkpoints \
    --load /path/to/load/checkpoints \
    --num-layers 24 \
    --hidden-size 1024 \
    --num-attention-heads 16 \
    --seq-length 1024 \
    --max-position-embeddings 1024 \
    --micro-batch-size 4 \
    --global-batch-size 32 \
    --lr 0.00015 \
    --min-lr 0.00001 \
    --lr-decay-style cosine \
    --lr-warmup-fraction 0.01 \
    --weight-decay 0.1 \
    --clip-grad 1.0 \
    --fp16 \
    --seed 1234

附录:Megatron 中间数据 decode 查看

  • 示例代码
    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
    import os
    import torch

    # 读取 tokens.bin(假设为 int32 类型,根据预处理配置调整 dtype)
    tokens_path = "./processed_tokens_document.bin"

    dtype = torch.int32 # 假设是int32,根据实际预处理配置调整(如int64)
    bytes_per_token = dtype.itemsize # int32->4字节,int64->8字节

    # 获取文件总大小(字节)
    file_size = os.path.getsize(tokens_path)
    # 计算总token数(总字节数 / 每个token的字节数)
    total_tokens = file_size // bytes_per_token
    print(f"文件总大小:{file_size} 字节,总token数:{total_tokens}")

    from transformers import AutoModelForCausalLM, AutoTokenizer
    model_name = "./path_to_model/"
    # load the tokenizer and the model
    tokenizer = AutoTokenizer.from_pretrained(model_name)

    # 抽查的 Token 数量
    block_size = 10000

    # 分块读取并解码
    with open(tokens_path, "rb") as f:
    for start in range(0, total_tokens, block_size):
    # 计算当前块的结束位置(不超过总token数)
    end = min(start + block_size, total_tokens)
    current_block_size = end - start

    # 读取当前块的二进制数据(字节数 = token数 × 每个token的字节数)
    f.seek(start * bytes_per_token) # 移动到当前块的起始位置
    block_bytes = f.read(current_block_size * bytes_per_token)

    # 将二进制数据转为torch tensor(token索引)
    block_tokens = torch.frombuffer(block_bytes, dtype=dtype)

    # 解码当前块为文本
    block_text = tokenizer.decode(block_tokens.tolist(), skip_special_tokens=False) # skip_special_tokens=True 会缺失 Special Token

    print(f"处理块 {start//block_size + 1}/{(total_tokens + block_size -1)//block_size}")
    print(block_text)
    break # 打开 break 可不断循环读取

附录:ckpt 文件清理

  • 使用 Megatron-LM 训练模型时,为了保证可恢复,常常会出现存储多个 ckpt 的情况,一般是一定的步骤就存储一个 ckpt
  • 当实验完成后一般仅保留最后一个即可
  • Meagtron-LM 的每个 ckpt 中,都完整存储着从这个 ckpt 启动继续训练所需的所有文件,包括模型权重文件等
    1
    2
    distrib_optim.pt:分布式优化器状态分片,训练恢复时用
    model_optim_rng.pt:随机数生成器状态,训练恢复时用,可能包含模型权重等,根据具体场景可能部分

脚本编写

  • 脚本编写的基本要求为:
    • 删除某个目录(用参数传入)下所有满足条件的文件夹(包括子文件夹):
      • 1)创建日期在 “2024-08-01” 到 “2024-08-10” 之间的
      • 2)文件名以 iter_000x 命名,且x是100 的整倍数,比如 iter_0000600 或 iter_0001000 等
      • 3)当前同级目录下还存在以 iter_000x 命名,且 x 比自己大的文件夹
    • 删除前要求如下:
      • 删除每个文件夹时,先询问是否删除,必须等待回应才继续(同时打印被删除的文件夹及其同级的其他文件夹和文件),Y表示删除,N表示不删除,直接Enter表示不删除;
      • 注意:为了安全起见,一定要收到 “Y” 作为输入再删除,否则不删除,避免误删
  • 下面是一个脚本实现(大模型实现,经过本人部分修改),用于帮助清理 ckpt(已经测试过可以使用):
    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
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    #!/bin/bash

    # 用法: ./clean_x00_steps_in_ckpt.sh /path/to/target_dir
    TARGET_DIR="$1"

    if [ -z "$TARGET_DIR" ]; then
    echo "❌ 请传入目标目录路径"
    exit 1
    fi

    if [ ! -d "$TARGET_DIR" ]; then
    echo "❌ 目录不存在: $TARGET_DIR"
    exit 1
    fi

    # step_interval
    step_interval=100

    # 日期范围
    START_DATE="2025-08-01"
    END_DATE="2025-11-10"

    # 转换为时间戳方便比较
    start_ts=$(date -d "$START_DATE" +%s)
    end_ts=$(date -d "$END_DATE" +%s)

    # 遍历所有匹配 iter_000x 的文件夹(递归)
    find "$TARGET_DIR" -type d -regextype posix-egrep -regex '.*/iter_000[0-9]+' | while read -r dir; do
    basename=$(basename "$dir")

    # 提取数字部分
    num=$(echo "$basename" | sed -E 's/iter_0+([0-9]+)/\1/')

    # 判断是否是 step_interval 的倍数
    if (( num % step_interval != 0 )); then
    continue
    fi

    # 获取创建日期(Linux & macOS兼容)
    if [[ "$OSTYPE" == "darwin"* ]]; then
    create_date=$(stat -f "%SB" -t "%Y-%m-%d" "$dir")
    else
    create_date=$(stat -c %w "$dir")
    if [ "$create_date" = "-" ]; then
    create_date=$(stat -c %y "$dir" | cut -d' ' -f1)
    fi
    fi

    # 转换为时间戳
    create_ts=$(date -d "$create_date" +%s 2>/dev/null)
    if [ -z "$create_ts" ]; then
    continue
    fi

    # 日期范围判断
    if (( create_ts < start_ts || create_ts > end_ts )); then
    continue
    fi

    # 检查同级目录是否存在更大的 iter_000y
    parent_dir=$(dirname "$dir")
    bigger_exist=false
    for sibling in "$parent_dir"/iter_000*; do
    if [ -d "$sibling" ]; then
    sib_num=$(echo "$(basename "$sibling")" | sed -E 's/iter_0+([0-9]+)/\1/')
    if (( sib_num > num )); then
    bigger_exist=true
    break
    fi
    fi
    done

    if [ "$bigger_exist" = false ]; then
    continue
    fi

    # 符合条件 -> 询问是否删除
    # 打印当前目录及同级目录内容
    echo "----------------------------------------"
    echo "📂 待删除文件夹: $dir ✅"
    echo "----------------------------------------"
    echo "同级目录内容:"
    ls -l "$parent_dir"
    echo "----------------------------------------"
    read -p "是否删除? (Y/N, 回车默认不删除): " choice < /dev/tty # 必须使用 < /dev/tty 以确保从交互界面接收到一个输入

    if [[ "$choice" =~ ^[Yy]$ ]]; then
    rm -rf "$dir" && echo "✅ 已删除 $dir"
    else
    echo "跳过 $dir"
    fi
    done

附录:使用问题记录

问题:lr 配置需要注意加小数点

  • 在 yaml 文件中进行 lr 等配置时,使用类似 8e-6 的配置可能会出现问题,使用 8.0e-6 会更保险
  • 原因:某些 yaml 配置文件解析器可能将 8e-6 解析为字符串或其他类型,而不是小数类型

NLP——LLM对齐微调相关总结

本文简单录对齐微调的一些方法,部分方法的详细内容可以在本人其他博客搜索到


熵的定义

  • 熵的一般定义:
    $$ H = -\sum_a \pi(a) \log \pi(a) = - \mathbb{E}_{a\sim \pi(a)} \log \pi(a)$$

SFT 的 Loss

  • SFT 的目标为最大化 Next Token 的对数概率和(本质是最大化所有 Token 的联合概率分布):
    $$ \mathcal{J}_\text{SFT}(\theta) = \max_\theta \mathbb{E}_{q,o\sim P_\text{SFT}(Q,o)}\left[\frac{1}{|o|} \sum_t \log \pi_\theta(o_t| x,o_{< t})\right]$$
  • SFT 下的 Loss 为:
    $$ L_\text{SFT} = -\frac{1}{|o|} \sum_t \log \pi_\theta(o_t| x,o_{< t}) $$

RM 的 Loss

  • 遵循 Bradley-Terry 模型,论文使用如下所述的奖励函数 \(r_{\psi}(x, y)\) 来制定偏好分布:
    $$
    \begin{aligned}
    p_{\psi}(y_{c} \succ y_{r}|x) & = \frac{\exp(r_{\psi}(x, y_{c}))}{\exp(r_{\psi}(x, y_{c})) + \exp(r_{\psi}(x, y_{r}))}, \\
    & = \sigma(r_{\psi}(x, y_{c}) - r_{\psi}(x, y_{r})),
    \end{aligned}
    $$
    • 其中 \(\sigma\) 是逻辑函数
    • 理解:这相当于是将正样本大于负样本的概率定义为一个 sigmoid 函数
  • 将该问题视为二分类任务,得到负对数似然损失函数(negative log-likelihood loss function):
    $$
    \mathcal{L}(r_{\psi}) = -\mathbb{E}_{(x, y) \sim \mathcal{D}_\text{rm} }[\log\sigma(r_{\psi}(x, y_{c}) - r_{\psi}(x, y_{r}))],
    $$
    • 理解:这里的损失函数本质上是相当于是二元交叉熵损失(Binary Cross-Entropy, BCE)函数,但因为已知有正负样本了,所以不需要分 label=1 和 label=0 来分别计算了
      $$
      L_\text{BCE} = -\frac{1}{N}\sum_{i=1}^N \big[y_i\log(\hat{y}_i) + (1-y_i)\log(1-\hat{y}_i)\big]
      $$
      • \(y_i\):真实标签(0 或 1),在 RM 中固定为 1(因为我们已知 Chosen 比 Rejected 更好)
      • \(\hat{y}_i\):模型预测概率 \(\sigma(r_{\psi}(x, y_{c}) - r_{\psi}(x, y_{r}))\)
      • 将 \(y_i=1\) 带入上面的 BCE 即可化简得到
        $$
        \begin{align}
        L_\text{BCE}
        &= -\frac{1}{N}\sum_{i=1}^N 1 \cdot \log(\hat{y}_i) \\
        &= -\frac{1}{N}\sum_{i=1}^N \log \sigma(r_{\psi}(x, y_{c}) - r_{\psi}(x, y_{r})) \\
        \end{align}
        $$
    • 其中数据集由表示为 \(D_\text{rm} = \{x^{(i)}, y_{c}^{(i)}, y_{r}^{(i)}\}_{i=1}^{N}\) 的 comparisons 组成
    • 在语言模型(LMs)领域,网络 \(r_{\psi}(x, y)\) 通常使用 SFT 模型 \(\pi^\text{SFT}(y|x)\) 进行初始化,并在它在最终的 Transformer 层上加入一个额外的线性层,以生成单个标量预测(singular scalar prediction)来表示奖励值

PPO

  • PPO 是最原始的 RLHF 方法
  • PPO 的替代目标函数定义为:
    $$
    \mathcal{L}^{CLIP}(\theta) = \hat{\mathbb{E} }_t \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]
    $$
    • \( \pi_\theta(a|s) \) 为当前策略,\( \pi_{\theta_{\text{old} } }(a|s) \) 为上一轮迭代的旧策略
    • \( r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old} } }(a_t|s_t)} \) 是概率比
    • \( \hat{A}_t \) 是时间步 \( t \) 的优势估计
    • \( \epsilon \) 是控制裁剪范围的超参数
  • 广义优势估计 (GAE) 是一种用于在 PPO 中更准确估计优势函数的技术
    • 对于长度为 \( T \) 的轨迹,时间步 \( t \) 的优势估计 \( \hat{A}_t \) 计算如下:
      $$
      \hat{A}_t = \sum_{l=0}^{T-t-1} (\gamma \lambda)^l \delta_{t+l}
      $$
      • \( \gamma \) 是折扣因子
      • \( \lambda \in [0,1] \) 是 GAE 参数
      • \( \delta_t = R(s_t, a_t) + \gamma V(s_{t+1}) - V(s_t) \) 是时序差分(Temporal-Difference, TD)误差
      • 这里 \( R(s_t, a_t) \) 是时间步 \( t \) 的奖励,\( V(s) \) 是价值函数
  • 注:在 RLHF 中通常设置折扣因子 \( \gamma = 1.0 \),为简化表示,论文后续章节将省略 \( \gamma \)
  • LLM 中,更具体的写法可以是:
    $$
    \mathcal{J}_{\textit{PPO}}(\theta)=\mathbb{E}_{q\sim P(Q),o\sim\pi_{\theta_{old}}(O|q)}\frac{1}{|o|}\sum_{t=1}^{|o|}\min\left[\frac{\pi_{\theta}(o_ {t}|q,o_{<t})}{\pi_{\theta_{old}}(o_{t}|q,o_{<t})}A_{t},\textrm{clip}\left( \frac{\pi_{\theta}(o_{t}|q,o_{<t})}{\pi_{\theta_{old}}(o_{t}|q,o_{<t})},1-\epsilon ,1+\epsilon\right)A_{t}\right],
    $$

CPPO(Continual Proximal Policy Optimization)

  • 原始论文:CPPO: Continual Learning for Reinforcement Learning with Human Feedback, ICLR 2024, Harbin Institute of Technology (Shenzhen):截止到 20250612,cited by 25
  • 其他容易误解论文:CPPO: Accelerating the Training of Group Relative Policy Optimization-Based Reasoning Models, arXiv 202503, Xiamen University:截止到 20250612,cited by 12
    • 不厚道,命名与别人相同,容易造成读者误解

REINFORCE

  • 不需要 Value Model,蒙特卡罗法评估奖励
  • 对 PPO 的简化

REINFORCE++

  • 原始论文:REINFORCE++: An Efficient RLHF Algorithm with Robustness to Both Prompt and Reward Models, 20250104-20251110, Jian Hu & Jason Klein Liu & Wei Shen
  • REINFORCE++ 方法介绍
  • 本质可以理解为 REINFORCE 方法(不是基于 Prompt 组的,有一个基于历史全局的基线)
  • 在 REINFORCE 的基础上,记录历史平均奖励作为基线,判断模型是否在进步(相比 GRPO,基线不是 Prompt 粒度的,而是历史)
  • 使用历史奖励的均值和方差做归一化,类似 Batch Normalization(论文认为 GRPO 的方法会出现 Prompt 粒度的有偏问题)
  • REINFORCE++ 方法出现在 ReMax, GRPO 和 RLOO 之后,对比如下:
  • 其他讨论:
    • REINFORCE++ 使用的是 k2 KL 散度估计而不是 k3,论文参考了博客 Rethinking KL Regularization in RLHF: From Value Estimation to Gradient Optimization 中的内容

ReMax

  • 参考链接:ReMax: A Simple, Effective, and Efficient Reinforcement Learning Method for Aligning Large Language Models, 2023, ICML 2024, 香港中文大学,南京大学
  • 本质是 REINFORCE 方法
  • 使用当前策略下每个状态下概率最高的动作对应的样本的奖励作为基线(每一步都贪心决策)
  • 注意:概率最高的动作会持续走到最后直到拿到一个完整的 rollout,再来计算奖励
  • 注意:与 GRPO 和 RLOO 不同,每次仅采样两个样本(其中一个是目标样本,另一个是贪心决策的样本)
  • ReMax 在 RLOO 之前一点点提出,算是并行的工作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # Algorithm 1: ReMax for Aligning LLMs
    1 Input: reward_model(rm), language_model(lm)
    2 for prompt in datasets:
    3 seq=lm.sample(prompt, greedy=False)
    4 seq_max=lm.sample(prompt, greedy=True)
    5 rew=rm(prompt, seq)−rm(prompt, seq_max)
    6 logp=lm.inference(prompt, seq)
    7 loss=−(logp.sum(dim=−1)*rew).mean()
    8 lm.minimize(loss)
    9 Output: language_model

GRPO(Group Relative Policy Optimization)

  • 每次生成一组样本,并对这组样本进行归一化
  • 具体公式:
    $$
    \begin{split}
    \mathcal{J}_{GRPO}(\theta)&=\mathbb{E}_{q\sim p(Q),\{\mathbf{o}_i\}_{i=1}^{G}\sim\pi_{\theta_{old}}(O|q)}\\
    &\frac{1}{G}\sum_{i=1}^{G}\frac{1}{|\mathbf{o}_i|}\sum_{t=1}^{|o_{t}|}\left\{\min\left[\frac{\pi_{\theta}(o_{i,t}|q,\mathbf{o}_{i,<t})}{\pi_{\theta_{old}}(o_{i,t}|q,\mathbf{o}_{i,<t})}\hat{A}_{i,t},\text{clip}\left(\frac{\pi_{\theta}(o_{i,t}|q,\mathbf{o}_{i,<t})}{\pi_{\theta_{old}}(o_{i,t}|q,\mathbf{o}_{i,<t})},1-\epsilon,1+\epsilon\right)\hat{A}_{i,t}\right]-\beta\text{D}_{\text{KL}}\left[\pi_{\theta}||\pi_{ref}|\right) \right\},
    \end{split}
    $$
    • 其中 \(\hat{A}_{i,t}\) 的计算方式为在同一个 Query 采样的多个 Response 内部做归一化:
      $$\hat{A}_{i,t}=\overline{r}_{i}=\frac{r_{i}-\text{mean}(\mathbf{r})}{\text{std}(\mathbf{r})}$$

RLOO(REINFORCE Leave-One-Out)

  • 参考链接:LLM RLHF 2024论文(四)RLOO - sanmuyang的文章 - 知乎
  • 类似于 GRPO(发表时间也类似),每次生成一组样本
  • 与 GRPO 的主要区别是:减去的是其他样本(不包含当前样本)的均值,且没有除以方差
  • RLOO 没有使用限制 KL 散度约束?

DPO(Direct Preference Optimization)

  • DPO 及其相关改进参见 NLP——LLM对齐微调-DPO 和 NLP——LLM对齐微调-DPO相关改进
  • DPO 目标:
    $$\mathcal{L}_{\text{DPO} }(\pi_{\theta};\pi_{\text{ref} })=-\mathbb{E}_{(x,y_{w},y_ {l})\sim\mathcal{D} }\bigg{[}\log\sigma\left(\beta\log\frac{\pi_{\theta}(y_{w} \mid x)}{\pi_{\text{ref} }(y_{w} \mid x)}-\beta\log\frac{\pi_{\theta}(y_{l} \mid x)}{\pi_{\text{ref} }(y_{l} \mid x)}\right)\bigg{]}. \tag{7}$$
  • DPO 梯度:关于参数 \(\theta\) 的梯度可以写成:
    $$\nabla_{\theta}\mathcal{L}_{\text{DPO} }(\pi_{\theta};\pi_{\text{ref } })=\ -\beta\mathbb{E}_{(x,y_{w},y_{l})\sim\mathcal{D} }\bigg{[}\underbrace{\sigma(\hat{r}_{\theta}(x,y_{l})-\hat{r}_{\theta}(x,y_{w}))}_{\text{higher weight when reward estimate is wrong} }\quad\bigg{[}\underbrace{\nabla_{\theta}\log\pi(y_{w} \mid x)}_{\text{increase likelihood of } y_{w}}-\underbrace{\nabla_{\theta}\log\pi (y_{l} \mid x)}_{\text{decrease likelihood of } y_{l} }\bigg{]}\bigg{]}$$
    • 其中 \(\hat{r}_{\theta}(x,y)=\beta\log\frac{\pi_{\theta}(y|x)}{\pi_{\text{ref} }(y|x)}\) 是由语言模型 \(\pi_{\theta}\) 和参考模型 \(\pi_{\text{ref} }\) 隐式定义的奖励(更多内容在第 5 节)
    • 直观地说:损失函数 \(\mathcal{L}_{\text{DPO} }\) 的梯度增加了优选补全 \(y_{w}\) 的似然,并降低了非优选补全 \(y_{l}\) 的似然
    • 样本的权重由隐式奖励模型 \(\hat{r}_{\theta}\) 对非优选补全评分高出多少来衡量,按 \(\beta\) 缩放,即隐式奖励模型对补全排序的错误程度,同时考虑了 KL 约束的强度
    • 论文的实验表明了这种加权的重要性,因为没有加权系数的朴素版本的方法会导致语言模型退化(附录表 3)

RAFT(Reward rAnked FineTuning)

  • RAFT,即 Reward rAnked FineTuning,出自 Raft: Reward ranked finetuning for generative foundation model alignment, 2023.
  • RAFT 是 RL-free 方法,其核心步骤包括:
    • 数据收集:可以利用正在训练的生成模型、预训练模型或它们的混合模型作为生成器,提升数据生成的多样性和质量
    • 数据排序:利用与目标需求对齐的分类器或者回归器,筛选出最符合人类需求的样本
    • 模型微调:利用筛选出的样本对模型进行微调,使训练后的模型与人类需求相匹配

RAHF(Representation Alignment from Human Feedback)

  • (RAHF)Aligning Large Language Models with Human Preferences through Representation Engineering, 2024, Fudan
  • RAHF(Representation Alignment from Human Feedback)的训练流程聚焦于通过表示工程实现大语言模型与人类偏好的对齐,整体方法描述如下

步骤一:指导LLM理解人类偏好

  • 单模型对比指令微调(RAHF-SCIT) (Single Large Language Model through Contrastive Instruction Tuning (SCIT))
    • 使用对比指令(如“生成受人类偏好的响应”和“生成不受人类偏好的响应”)对单个LLM进行微调
    • 训练目标:通过最小化损失函数,使模型在给定正偏好指令时提高生成偏好响应的概率,给定负偏好指令时降低该概率
    • 优势:通过同一模型学习偏好与非偏好的差异,避免特征空间不一致问题
  • 双模型监督训练(RAHF-Dual)
    • 分别微调两个LLM:
      • 偏好模型 :使用偏好响应数据进行监督训练,学习生成符合人类偏好的输出
      • 非偏好模型 :使用非偏好响应数据训练,学习生成不符合偏好的输出
    • 特点:通过不同模型分别捕捉偏好与非偏好的表征,但需注意双模型特征空间可能存在的偏差

步骤二:收集模型 Activity Pattern

  • 输入处理 :将查询-响应对与偏好/非偏好指令拼接,输入模型以获取中间层隐藏状态(即内部表征)。为确保长度一致,对指令和响应进行 padding 处理
  • 差异向量计算 :提取偏好刺激(\(A_{p^{+}, \pi, l}\))和非偏好刺激(\(A_{p^{-}, \pi, l}\))下的隐藏状态,计算差值 \(v_l = A_{p^{+}, \pi, l} - A_{p^{-}, \pi, l}\),该向量表征人类偏好相关的 Activity Pattern 差异

步骤三:构建最终对齐模型

  • LoRA适配器微调 :利用低秩适配器(LoRA)拟合差异向量 \(v_l\),通过均方误差(MSE)损失函数将差异向量融入模型表征:
    $$
    \mathcal{L}_{Align} = \left| A_{p, \pi_{LoRA}, l} - (A_{p, \pi_{base}, l} + \alpha v_l) \right|_2
    $$
    其中 \(\alpha\) 控制差异向量的干预强度,通过调整该超参数平衡模型原始能力与偏好对齐效果
  • 目标层选择 :优先选择模型中间层(如LLaMA2-7B的第10、20层)进行操作,因中间层更易捕捉与偏好相关的全局表征,避免顶层任务特异性或底层表征不完整的问题

SimpleRL

  • 原始论文:SimpleRL-Zoo: Investigating and Taming Zero Reinforcement Learning for Open Base Models in the Wild, HKUST & TikTok Meituan, 20250524 & 202508076
  • 没有提出新的方法,就是原始的 GRPO 方法,但是对 GRPO 方法进行了微调
  • 与 DAPO 类似,SimpleRL 将 GRPO 公式中的长度归一化挪到更外层的循环中去
  • SimpleRL 这篇论文提出了适用于多种开源基础模型的 Zero RL 训练方法
    • 注:Zero RL:指直接基于基础模型训练的方法,这种方法不依赖 SFT
    • SimpleRL 的贡献在于通过优化奖励设计、数据难度匹配等关键策略,实现 Zero RL 训练方法下推理能力提升
  • 具体设计:
    • 训练方法:直接从基础模型出发进行强化学习,不进行任何前置的 SFT,采用GRPO算法作为训练核心,仅依赖基于正确性的规则奖励和简单训练设置
    • 关键训练组件
      • 算法:采用移除目标函数中长度归一化的 GRPO 算法,优化计算效率,无需单独价值模型,直接通过组归一化奖励估计优势函数
      • 奖励函数:仅基于答案正确性设计,正确答案奖励+1,错误奖励0,摒弃严格格式奖励(如强制答案入盒),避免限制模型探索
      • 数据处理:将 GSM8K 和 MATH 数据集按难度分为 Easy(GSM8K+MATH lv.1)、Medium(MATH lv.1-4)、Hard(MATH lv.3-5)三类,每类含约 8000 个样本,根据模型能力匹配对应难度数据
      • 模型与 Prompt:覆盖 10 种不同家族和规模的模型(Llama3-8B、Mistral 系列、Qwen2.5 系列等),对指令跟随能力弱的模型采用简单提示(仅要求分步推理),能力强的模型采用复杂提示
  • 对 GRPO 目标函数作为微小修改(实际上这种改法与 DAPO 一致)
    $$
    \mathcal{J}_{\text{GRPO}}(\theta)=\underbrace{\frac{1}{\color{red}{\sum_{i=1}^{G}\left|o_{i}\right|}} \color{red}{\sum_{i=1}^{G} \sum_{t=1}^{\left|o_{i}\right|}} min \left[r_{i, t}(\theta) \hat{A}_{i}, clip\left(r_{i, t}(\theta) ; 1-\epsilon, 1+\epsilon\right) \hat{A}_{i}\right]}_{\text{Clipped policy update } }-\underbrace{\beta \mathbb{D}_{KL}\left[\pi_{\theta} | \pi_{ref }\right]}_{\text{KL penalty } }
    $$
    • 理解:这样可以消除长度归一化对模型响应长度的不当约束,更贴合 Zero RL 训练中“鼓励模型自由探索合理推理长度”的需求
  • 总结思考: SimpleRL 关键优化策略
    • 1)摒弃刚性格式奖励,优先保证响应可验证性,避免抑制模型探索
    • 2)严格匹配训练数据难度与模型固有能力,难度不匹配会导致训练崩溃或效果不佳
    • 3)调整探索相关超参数:采用较大采样量(N≥8)和合适温度(训练温度 1.0-1.2),稳定训练过程
    • 4)避免传统 SFT 冷启动:传统 SFT 会限制模型探索能力,降低RL阶段的推理行为涌现潜力

SEED-GRPO(Semantic Entropy EnhanceD GRPO)

  • SEED-GRPO: Semantic Entropy Enhanced GRPO for Uncertainty-Aware Policy Optimization, 20250518, Zhejiang University

DAPO

  • 在 GRPO 的基础上提出四个改进:
    • 提升上界
    • 动态过滤(全对或全错的)
    • 损失平均方式:Sequence 内部的 Token Loss 平均 -> 批次粒度的 Token Loss 平均
    • 长度惩罚([0;L_max - L_cache;L_max;+inf],在 [L_max - L_cache;L_max] 长度区间内,使用逐步增大惩罚,超过 L_max 部分,固定惩罚)

VAPO(Value-model-based Augmented Proximal Policy Optimization)

  • VAPO: Efficient and Reliable Reinforcement Learning for Advanced Reasoning Tasks, arXiv 202504, ByteDance Seed
  • 字节 Seed 团队的作品,是对 DAPO 的进一步改进
  • 在 DAPO 的基础上,增加了:
    • Value Pretraining
    • Decoupled-GAE,即 \(\lambda_\text{critic} = 1.0\),\(\lambda_\text{actor} = 0.95\)
    • 长度自适应的 GAE

VC-PPO(Value-Calibrated PPO)

  • (VC-PPO)What’s Behind PPO’s Collapse in Long-CoT? Value Optimization Holds the Secret, arXiv 20250303, ByteDance Seed
  • 核心贡献:
    • Pretrained value:开始 RL 前先预训练价值网络
    • Decoupled-GAE:计算 Advantage (for Actor 损失)时和 计算 Target Reward(for Critic 损失)时使用不同的 \(\lambda\)

ORZ(Open-Reasoner-Zero)

  • 原始论文:Open-Reasoner-Zero: An Open Source Approach to Scaling Up Reinforcement Learning on the Base Model, arXiv 20250401, StepFun & THU
  • 第一个开源的 LLM 上面向推理的 zero RL 实现(即从 Base Model 直接进入 RL)
  • 相关链接
    • GitHub: https://github.com/Open-Reasoner-Zero/Open-Reasoner-Zero
    • HuggingFace: https://huggingface.co/Open-Reasoner-Zero
  • 效果:
    • ORZ-32B 在 GPQA Diamond 基准上优于 DeepSeek-R1-Zero-Qwen-32B,训练步骤仅为其 1/30
    • ORZ-32B 在 AIME 2024 上获得 48.1分(同 Size 模型上,后来字节的 VAPO 做到了 60分)
    • ORZ-7B 做到了 17.9 分
  • 注:包含很多训练经验,值得一看

GVPO(Group Variance Policy Optimization)

  • 原始论文:GVPO: Group Variance Policy Optimization for Large Language Model Post-Training, arXiv 20250319, HKUST
  • 核心贡献:
    • GVPO 推到了一个 RL 约束优化问题的唯一最优解
    • 提出一种灵活的采样分布避免了 on-policy 和 重要性采样
  • 结果:用 Qwen-7B 模型为基线,在 AIME 2024 上,做到了 20.72分(注:比 ORZ 和 GRPO 等都高)

GPG(Group Policy Gradient)

  • 原始论文:GPG: A Simple and Strong Reinforcement Learning Baseline for Model Reasoning, arXiv 20250501, AMAP Alibaba:AMAP是高德地图的简称
  • 效果明显优于 GRPO
  • GPG 方法的特点:移除所有花里胡哨的组件(问题:效果真的好吗?)
  • GPG 方法的训练算法:
  • 各种方法的损失函数比较:
  • 补充趣事:GPG 对 Dr.GRPO 的批判:

    In addition to these methods to improve efficiency and stability, a very recent and concurrent work Dr.GRPO [31] studies the details of reward and loss normalization and states GRPO tends to generate more tokens. However, although it reveals the reward bias in the advantage function, we observe that its performance did not significantly outperform GRPO.


ORPO(Odds Ratio Preference Optimization)

  • 原始论文:ORPO: Monolithic Preference Optimization without Reference Model, arXiv 20240314, KAIST AI:截止到 20250616 日,cited by 244
    • 注:KAIST AI 是韩国科学技术院(KAIST)的一个机构
  • 核心思路:
    • 偏好对齐的同时考虑 SFT 损失

ECPO(Early Clipped GRPO)

  • 来源于快手的 OneREc OneRec Technical Report,用于 LLM4Rec 领域的偏好对齐
  • 具体来说,对于用户 \( u \),论文使用旧策略模型生成 \( G \) 个物品。每个物品与用户一起输入偏好奖励模型,得到 P-Score 作为奖励 \( r_i \)。优化目标如下:
    $$
    \mathcal{J}_{\text{ECPO}}(\theta) = \mathbb{E}_{u \sim P(U), \{o_i\}_{i=1}^G \sim \pi_{\theta_{old} } } \left[ \frac{1}{G} \sum_{i=1}^G \min \left( \frac{\pi_\theta(o_i|u)}{\color{red}{\pi’_{\theta_{old} }}(o_i|u)} A_i, \text{clip} \left( \frac{\pi_\theta(o_i|u)}{\color{red}{\pi’_{\theta_{old}} }(o_i|u)}, 1 - \epsilon, 1 + \epsilon \right) A_i \right) \right], \\
    A_i = \frac{r_i - \text{mean}(\{r_1, r_2, \ldots, r_G\})}{\text{std}(\{r_1, r_2, \ldots, r_G\})},\\
    \color{red}{\pi’_{\theta_{old} }(o_i|u) = \max \left( \frac{\text{sg}(\pi_\theta(o_i|u))}{1 + \epsilon + \delta}, \pi_{\theta_{old} }(o_i|u) \right), \quad \delta > 0,}
    $$
    • \(\text{sg}\) 表示停止梯度操作(stop gradient operation)
    • \(\delta\) 是一个大于 0 的超参数
  • ECPO 对 GRPO(Group Policy Relative Optimization)(2024) 进行了修改,使其训练过程更加稳定
    • 如图 6 所示,在原始 GRPO 中,允许负优势(negative advantages)的策略比率(\(\pi_\theta / \pi_{\theta_{old} }\))较大,这容易导致梯度爆炸
    • 因此,论文预先对具有较大比率的策略进行截断,以确保训练稳定性,同时仍允许相应的负优势生效
    • \(\delta\) 越大,可容忍的策略比率越大,意味着可容忍的梯度越大,这可以根据实际需求确定
  • 在 OneRec 中,论文将 \(\delta\) 设为 0.1,表示允许负优势的策略比率略微超过 \(1 + \epsilon\)

CISPO(Clipped IS-weight Policy Optimization)

  • 原始论文:MiniMax-M1: Scaling Test-Time Compute Efficiently with Lightning Attention, arXiv 20250616, MiniMax
  • 名字说明:Clipped IS-weight Policy Optimization 中 IS 表示 Importance Sampling
  • 效果:相对 DAPO,实现两倍加速
  • 核心思路:不再裁剪 token 更新(影响是否反传梯度),而是裁剪重要性采样权重
    • 注:思路和最近的 ECPO 有点相近
    • 理解:这种做法是不对的,相当于忽略了 PPO 本身的优势
  • CISPO 目标函数:
    $$
    \mathcal{J}_{\text{CISPO} }(\theta) = \mathbb{E}_{(q,a)\sim\mathcal{D},\{o_i\}_{i=1}^G\sim\pi_{\theta_{\text{old} } }(\cdot|q)} \left[\frac{1}{\sum_{i=1}^G |o_i|} \sum_{i=1}^G \sum_{t=1}^{|o_i|} \mathbf{sg}(\hat{r}_{i,t}(\theta))\hat{A}_{i,t}\log\pi_\theta(o_{i,t} \mid q,o_{i,<t})\right], \tag{4}
    $$
    • 其中 \(\hat{r}_{i,t}(\theta)\) 是裁剪后的重要性采样权重:
      $$
      \hat{r}_{i,t}(\theta) = \text{clip}\left(r_{i,t}(\theta),1-\epsilon_{low}^{IS},1+\epsilon_{high}^{IS}\right).
      $$
    • 优势计算与 GRPO 一致:
      $$
      \hat{A}_{i,t} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)},
      $$
      • \(R_i\) 是响应的奖励,每个问题采样 \(G\) 个响应 \(\{o_i\}_{i=1}^G\)

Efficient RL Scaling with CISPO

Background
  • 对于数据集 \(\mathcal{D}\) 中的问题 \(q\),论文将策略模型表示为参数化的 \(\pi_\theta\),生成的响应为 \(o\)。PPO(2017)采用以下目标函数来优化策略以最大化预期回报,并通过裁剪操作稳定训练:
    $$
    \mathcal{J}_{\text{PPO} }(\theta) = \mathbb{E}_{q\sim\mathcal{D},o_i\sim\pi_{\theta_{\text{old} } }(\cdot|q)} \left[\frac{1}{|o_i|}\sum_{t=1}^{|o_i|}\min\left(r_{i,t}(\theta)\hat{A}_{i,t}, \text{clip}(r_{i,t}(\theta),1-\epsilon,1+\epsilon)\hat{A}_{i,t}\right) - \beta D_{KL}(\pi_\theta||\pi_{\text{ref} })\right],
    $$
    • 其中 \(r_{i,t}(\theta) = \frac{\pi_\theta(o_{i,t}|q,o_{i,< t})} {\pi_{\theta_{\text{old} } } (o_{i,t}|q,o_{i, < t})}\) 是重要性采样权重(Importance Sampling, IS),用于在 Off-policy 更新时校正分布
  • PPO 需要一个单独的价值模型来计算优势 \(\hat{A}_{i,t}\),GRPO(2024)则通过将优势定义为响应组内相对奖励来消除价值模型:
    $$
    \hat{A}_{i,t} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)},
    $$
    • 其中 \(R_i\) 是响应的奖励,每个问题采样 \(G\) 个响应 \(\{o_i\}_{i=1}^G\)
    • 奖励可以来自基于规则的验证器(如数学问题求解)或奖励模型
  • 对比,回顾带有校正分布(重要性采样)的原始 REINFORCE 目标函数 :
    $$
    \mathcal{J}_{\text{REINFORCE} }(\theta) = \mathbb{E}_{(q,a)\sim\mathcal{D},o_t\sim\pi_{\theta_{\text{old} } }(\cdot|q)} \left[\frac{1}{|o_i|}\sum_{t=1}^{|o_i|} \mathbf{sg}(r_{i,t}(\theta))\hat{A}_{i,t}\log\pi_\theta(o_{i,t} \mid q,o_{i,<t})\right], \tag{3}
    $$
  • GISPO 的本质 是:基于组计算 Advantage 的(类似GRPO),带重要性采样的(可 Off-policy 更新的)REINFORCE 方法

GPPO(Gradient-Preserving Clipping Policy Optimization)

  • Klear-Reasoner: Advancing Reasoning Capability via Gradient-Preserving Clipping Policy Optimization, Klear, arXiv 20250812
  • Hugging Face地址:https://huggingface.co/Suu/Klear-Reasoner-8B
  • GitHub 地址:https://github.com/suu990901/KlearReasoner/tree/main
  • GPPO 方法用于解决传统强化学习(如PPO、GRPO)中的剪辑机制(Clipping)存在两个关键问题:
    • 高熵token梯度被抑制 :超出上阈值(\(1+\epsilon\))的高熵 token(对应关键探索行为)的梯度被直接丢弃,限制模型探索能力
    • 负样本收敛延迟 :低于下阈值(\(1-\epsilon\))的次优轨迹梯度被截断,导致模型难以从负样本中学习,收敛速度减慢
  • GPPO 不丢弃任何token的梯度,即使是超出剪辑范围的 token,其梯度也会被纳入反向传播计算图。通过有界且温和的梯度传播 ,平衡训练稳定性与有价值梯度信息的保留:
    • 对高熵token(超出上阈值),保留其梯度以增强探索;
    • 对次优轨迹(低于下阈值),保留其梯度以加速负样本学习
  • GPPO 的优势:
    • 增强探索能力 :保留高熵token的梯度,避免过早终止探索;
    • 加速负样本学习 :利用次优轨迹的梯度,减少重复采样,加快收敛;
    • 稳定训练 :通过有界梯度控制,避免梯度爆炸,维持训练稳定性
  • 实验表明,GPPO 在数学(AIME)和编程(LiveCodeBench)任务上的性能优于传统剪辑方法(如 GRPO w/ Clip-Higher)和并发方法(如 CISPO)
  • GPPO 损失函数(基于 GRPO 的 token-level 损失修改而来),公式如下:
    $$\mathcal{L}^{GPPO}(\theta)=\mathbb{E}_{x \sim \mathcal{D}}\left[\frac{1}{\sum_{j=1}^{M} T_{j}} \sum_{j=1}^{M} \sum_{t=1}^{T_{j}} min \left(\delta \tilde{A}^{(j)}, clip\left(\delta, \frac{1-\epsilon_{l}}{sg(\delta)} \delta, \frac{1+\epsilon_{h}}{sg(\delta)} \delta\right) \overline{A}^{(j)}\right)\right]$$
    • \(\delta = r_{t}^{(j)}(\theta)\) :token 级重要性采样比(当前策略与旧策略的概率比);
    • \(sg(\cdot)\) :停止梯度(stop-gradient)操作,确保 \(\frac{\delta}{sg(\delta)}\) 数值上恒为1,前向计算不变;
    • \(\epsilon_l, \epsilon_h\) :剪辑的下、上阈值(如 \(\epsilon_l=0.2, \epsilon_h=0.28\));
    • \(\tilde{A}^{(j)}\) :组相对优势(group-relative advantage),通过组内奖励标准化计算;
    • \(\sum_{j=1}^{M} T_j\) :所有token的总长度,用于归一化
  • GPPO 梯度表达式,(梯度计算保留所有 token 的贡献),公式如下:
    $$\nabla_{\theta} \mathcal{L}^{GPPO}(\theta) = \mathbb{E}_{x \sim \mathcal{D}}\left[\frac{1}{\sum_{j=1}^{M} T_{j}} \sum_{j=1}^{M} \sum_{t=1}^{T_{j}} \mathcal{F}_{j, t}(\theta) \cdot \phi_{\theta}\left(a_{j, t}, s_{j, t}\right) \cdot \tilde{A}^{(j)}\right]$$
    • 其中,\(\mathcal{F}_{j, t}(\theta)\)(梯度权重函数)定义为:
      $$\mathcal{F}_{j, t}(\theta) = \begin{cases}
      1-\epsilon_{l} & \text{if } \delta<1-\epsilon_{l} \text{ and } \tilde{A}^{(j)}<0, \\
      1+\epsilon_{h} & \text{if } \delta>1+\epsilon_{h} \text{ and } \tilde{A}^{(j)}>0, \\
      \delta & \text{otherwise}
      \end{cases}$$
    • \(\phi_{\theta}(a_{j,t}, s_{j,t})\) :策略网络输出的 logits 关于参数 \(\theta\) 的导数(减去基线项);
      • 当 \(\delta\) 超出剪辑范围时,\(\mathcal{F}_{j,t}(\theta)\) 被约束为 \(1-\epsilon_l\) 或 \(1+\epsilon_h\),确保梯度有界;
    • 当 \(\delta\) 在范围内时,直接使用 \(\delta\),保留原始梯度

Reflect-Retry-Reward

  • 原始论文:Reflect, Retry, Reward: Self-Improving LLMs via Reinforcement Learning, 20250530, Writer,Writer 是一家美国 AI 公司
  • Reflect-Retry-Reward 机制的基本框架:
  • 基本流程:
    • 第一次生成结果
      • 如果成功则不进行任何训练
      • 如果失败则生成 Self-reflection token,重新将带着 Self-reflection token 的任务继续输入模型
    • 第二次生成结果
      • 如果成功则进行训练,并对刚刚 Self-reflection token 增加赋予奖励?
      • 如果失败则不进行训练?

DFT(Dynamic Fine-Tuning)

  • 今天在损失函数上添加一个权重,将 SFT 的损失函数对齐 RLHF
    • 不能做到像 RL 一样探索,但是能尽量让 SFT 的损失函数贴近 RL
  • 注意:
    • SFT 的原始目标是最大化专家数据集的似然函数,故而其极大似然法推导出来的损失交(即叉熵损失)
    • 经过修改以后,SFT 的损失函数已经不太能说得上其含义了,目标是最大化专家数据对应的奖励?

iw-SFT(Importance Weighted Supervised Fine-tuning)

  • iw-SFT 是 Importance weighted supervised fine-tuning,详情见论文:Supervised Fine Tuning on Curated Data is Reinforcement Learning (and can be improved), 20250717
  • 待补充

CHORD(Controllable Harmonization of On-and Off-Policy Reinforcement Learning via Dynamic Weighting)

  • 原始论文:(CHORD)On-Policy RL Meets Off-Policy Experts: Harmonizing Supervised Fine-Tuning and Reinforcement Learning via Dynamic Weighting, arXiv 20250815, Alibaba Group
  • 解读博客:【千问大模型官方】先SFT后RL但是效果不佳?你可能没用好“离线专家数据”!
  • CHORD(Controllable Harmonization of On-and Off-Policy Reinforcement Learning via Dynamic Weighting)是一种结合了 RL 和 SFT 的方法,可以在 Token 粒度上识别重要性,从而调整学习损失函数或权重
  • CHORD 提出了思路相似的两种变体 CHORD-\(\mu\) 和 CHORD-\(\phi\),实验结果如下:

CHORD-\(\mu\)

  • CHORD-\(\mu\) 通过动态调整全局系数 \(\mu\) 来平衡 off-policy 专家数据(SFT 损失)和 on-policy 探索(GRPO 损失)的影响,其混合损失函数表达式为:
    $$
    \mathcal{L}_{\text{Hybrid}}(\theta) = (1-\mu) \mathcal{L}_{\text{GRPO}}(\theta) + \mu \mathcal{L}_{\text{SFT}}(\theta)
    $$
  • \(\mathcal{L}_{\text{GRPO}}(\theta)\) 是基于 GRPO 的 RL 损失函数,定义为:
    $$
    \mathcal{L}_{\text{GRPO}}(\theta) = -\frac{1}{\sum_{i=1}^{\hat{B}} \sum_{k=1}^{K} |\tau_{i,k}|} \sum_{i=1}^{\hat{B}} \sum_{k=1}^{K} \sum_{t=1}^{|\tau_{i,k}|} \min\left(r_{i,k,t}(\theta) A_{i,k}, \text{clip}(r_{i,k,t}(\theta), 1-\epsilon, 1+\epsilon) A_{i,k}\right)
    $$
    • 式中 \(r_{i,k,t}(\theta)\) 为 token-level 重要性采样比率,\(A_{i,k}\) 为优势值,\(\hat{B}\) 为批量提示数,\(K\) 为每个提示的候选响应数
  • \(\mathcal{L}_{\text{SFT}}(\theta)\) 是监督微调损失函数,定义为:
    $$
    \mathcal{L}_{\text{SFT}}(\theta) = -\frac{1}{\sum_{i=1}^{B} |y_i^*|} \sum_{i=1}^{B} \sum_{t=1}^{|y_i^*|} \log \pi_{\theta}(y_{i,t}^* | x_i, y_{i,< t}^*)
    $$
    • 式中 \(B\) 为批量大小,\(y_i^*\) 为专家响应序列,\(\pi_{\theta}\) 为模型策略
  • \(\mu \in [0,1]\) 是动态衰减的全局系数 ,初始值较高(侧重 SFT),随训练逐步降低(侧重 RL)

CHORD-\(\phi\)

  • CHORD-\(\phi\) 在 CHORD-\(\mu\) 的基础上引入 token-level 加权函数 \(\phi(\cdot)\),进一步细化对 off-policy 数据的控制,其损失函数表达式为:
    $$
    \mathcal{L}_{\text{Hybrid-}\phi}(\theta) = (1-\mu) \mathcal{L}_{\text{GRPO}}(\theta) + \mu \mathcal{L}_{\text{SFT-}\phi}(\theta)
    $$
  • \(\mathcal{L}_{\text{SFT-}\phi}(\theta)\) 是带 token 加权的 SFT 损失函数,定义为:
    $$
    \mathcal{L}_{\text{SFT-}\phi}(\theta) = -\mathbb{E}_{(x,y^*) \sim \mathcal{D}_{\text{SFT}}} \left[ \sum_{t=1}^{|y^*|} \phi(y_t^*; \pi_{\theta}) \cdot \log \pi_{\theta}(y_t^* | x, y_{<t}^*) \right]
    $$
  • 加权函数 \(\phi(y_t^*; \pi_{\theta})\) 基于模型对专家 token 的生成概率 \(p_t = \pi_{\theta}(y_t^* | x, y_{< t}^*)\) 定义为:
    $$
    \phi(y_t^*; \pi_{\theta}) = p_t (1 - p_t)
    $$
    • 该函数呈抛物线形,在 \(p_t=0.5\) 时权重最大,对高概率(\(p_t \to 1\))和低概率(\(p_t \to 0\))的 token 均降权,平衡探索与稳定性

BAPO(Balanced Policy Optimization with Adaptive Clipping)

  • 原始论文:BAPO: Stabilizing Off-Policy Reinforcement Learning for LLMsvia Balanced Policy Optimization with Adaptive Clipping, 20251021, Fudan
  • GitHub 链接:github.com/WooooDyy/BAPO
  • BAPO 用于解决 Off-policy RL 训练中 “优化失衡(imbalance in optimization)” 和 “熵坍缩” 两大问题,通过动态调整裁剪边界实现正 / 负样本贡献平衡与熵保留,最终提升训练稳定性、数据效率与模型性能
    • 优化失衡 :负优势样本(Advantage < 0)在策略梯度中占主导,抑制有效行为且易引发梯度爆炸;
    • 熵坍缩 :PPO 类方法的固定对称裁剪机制会系统性阻断“熵增更新”(排除低概率正样本、过度惩罚低概率负样本),导致策略过度利用(Exploitation)而丧失探索能力(Exploration)
  • BAPO 上述问题,提出动态非对称裁剪机制 ,核心目标是:
    • 平衡正/负样本对损失的贡献;
    • 保留低概率正样本以维持熵,过滤过度负样本以避免梯度爆炸;
    • 无需复杂手动调参,适配不同离线场景(样本重放、部分轨迹生成、不同数据陈旧度)
  • BAPO 核心优势
    • 稳定性:在不同数据陈旧度(2×、4×)、部分轨迹生成场景下,训练奖励持续上升,熵保持稳定(无坍缩),梯度范数可控;
    • 通用性:适配 DeepSeek-R1、Llama3.2 等不同底座模型,无需针对模型规模重新调参
    • 注:论文中看起来得分似乎没有比 GRPO 高太多

BAPO 目标函数(动态裁剪改进)

  • BAPO 保留 PPO 的“最小化裁剪项”结构,但将固定对称边界替换为动态非对称边界(\(c_{low}\) 为下边界,\(c_{high}\) 为上边界),目标函数为:
    $$
    J^{BAPO}(\theta) = \mathbb{E}_{y \sim \pi_{\theta_{rollout} }(\cdot | x)} \sum_{t=1}^{T} min\left(r_t \cdot A_t, clip\left(r_t, c_{low}, c_{high}\right) \cdot A_t\right)
    $$
    • 其中,\(c_{low}\) 和 \(c_{high}\) 不再是固定值,而是通过每批次数据动态调整 ,核心约束是“正样本对策略梯度损失的贡献达到目标阈值 \(\rho_0\)”

动态裁剪边界调整规则

  • BAPO 每轮训练(Step)中,通过迭代调整 \(c_{low}\) 和 \(c_{high}\),满足“正样本贡献目标 \(\rho_0\)”,具体规则如下:

  • (1)调整目标约束

    • 设 \(\rho\) 为当前批次中正样本对策略梯度损失的实际贡献占比,需满足:
      $$
      \rho \geq \rho_0
      $$
      • 其中 \(\rho_0\) 为预设目标(实验中设为 0.4),确保正样本不被负样本压制
  • (2)边界调整范围与步长

    • 下边界 \(c_{low}\):取值范围 \([a^-, b^-]\)(实验中设为 [0.6, 0.9]),调整步长 \(\delta_2\)(实验中设为 0.02);
    • 上边界 \(c_{high}\):取值范围 \([a^+, b^+]\)(实验中设为 [1.2, 3.0]),调整步长 \(\delta_1\)(实验中设为 0.05)
  • (3)迭代调整逻辑

    • 1)初始化:\(c_{low} = a^-\),\(c_{high} = a^+\);
    • 2)若 \(\rho < \rho_0\) 且 \(c_{low} + \delta_2 \leq b^-\):
      • 优先提升 \(c_{high}\)(若 \(c_{high} + \delta_1 \leq b^+\)),纳入更多低概率正样本(\(r_t\) 较大的正样本);
      • 若 \(c_{high}\) 已达上限,则提升 \(c_{low}\),过滤更多过度负样本(\(r_t\) 过小的负样本);
    • 3)重复步骤 2,直至 \(\rho \geq \rho_0\) 或边界达上限

一些关于熵的理论基础讨论

  • BAPO 通过纳入低概率正样本维持熵,其理论基础是“熵-裁剪规则”(Entropy-Clip Rule):策略熵的变化由未裁剪样本的“对数概率与优势值的协方差”决定,公式推导如下(详细证明见附录 B):
    $$
    \Delta \mathcal{H}(\pi_{\theta} | x, y_{ < t}) \approx -\eta \cdot Cov_{y_t \sim \pi_{\theta} } \left( log \pi_{\theta}(y_t), A_t \cdot \mathcal{X}(y_t) \right) + C
    $$
  • 其中:
    • \(\Delta \mathcal{H}\):策略熵的变化量;
    • \(\eta\):学习率;
    • \(\mathcal{X}(y_t)\):指示函数,\(\mathcal{X}(y_t)=1\) 表示样本未被裁剪,\(\mathcal{X}(y_t)=0\) 表示被裁剪;
    • \(C\):与 \(y_t\) 无关的常数
  • 关键结论:
    • 低概率正样本(\(\pi_{\theta}(y_t) \to 0\),\(A_t > 0\))未被裁剪时,会增大协方差,进而提升熵;
    • BAPO 动态提升 \(c_{high}\) 可纳入更多此类样本,避免熵坍缩

BAPO 训练流程:Algorithm 1

  • BAPO 每轮训练包含“样本生成-动态裁剪-策略更新”三步,具体流程如下:
  • 1)初始化输入:
    • 初始 LLM 策略 \(\pi_{\theta}\)、训练数据集 \(\mathcal{D}\)、奖励函数 \(R\)、数据陈旧度上限 \(E\);
    • 裁剪边界范围 \([a^-, b^-]\)(\(c_{low}\))和 \([a^+, b^+]\)(\(c_{high}\))、步长 \(\delta_1/\delta_2\)、正样本贡献阈值 \(\rho_0\)
  • 2)样本生成与过滤(每轮 Step s):
    • 更新行为策略:\(\pi_{\theta_{rollout} } \leftarrow \pi_{\theta}\);
    • 从 \(\mathcal{D}\) 采样批次数据 \(\mathcal{D}_s\),基于 \(\pi_{\theta_{rollout} }\) 生成 G 条响应 \(y_i\);
    • 计算每条响应的奖励(基于 \(R\))和优势值 \(A_t\)
  • 3)动态调整裁剪边界(适配不同陈旧度):
    • 初始化 \(c_{low} = a^-\),\(c_{high} = a^+\);
    • 循环:若 \(\rho < \rho_0\) 且 \(c_{low} + \delta_2 \leq b^-\):
      • 若 \(c_{high} + \delta_1 \leq b^+\),则 \(c_{high} \leftarrow c_{high} + \delta_1\);
      • 否则,\(c_{low} \leftarrow c_{low} + \delta_2\)
  • 4)策略更新:
    • 通过最大化 \(J^{BAPO}(\theta)\) 更新 \(\pi_{\theta}\),完成一轮训练

Trianing-free GRPO

  • 原始论文:Training-Free Group Relative Policy Optimization, 20251009, Tencent Youtu-Agent Team
  • 亮点:不修改模型参数,仅通过改进上下文来提升模型推理能力
  • Training-free GRPO 保留了传统 GRPO 的“多轮学习”框架,但将“参数更新”替换为“经验知识迭代优化”,核心流程可分为 4 步:
  • 1)初始化:经验库与基础配置
    • 初始化外部经验库 :存储领域相关的“语义优势知识”,初始为空或包含少量基础经验;
    • 固定LLM参数:使用冻结的大模型(如 DeepSeek-V3.1-Terminus )作为基础策略,避免参数更新;
    • 配置训练参数:仅需少量训练样本(如 100 个)、3-5轮迭代(epoch)、每组生成 5 个输出(group size=5)
  • 2)Rollout 与奖励计算(复刻传统 GRPO):对每个查询(query)执行并行输出生成 :
    • 基于当前经验库 \(\varepsilon\),让 LLM 生成一组输出(rollout,如 5 个不同推理轨迹),即策略 \(\pi_\theta(o_i | q, \varepsilon)\);
    • 使用奖励模型(R)对每个输出 \(o_i\) 打分,得到 scalar reward \(r_i = R(q, o_i)\)(如数学推理的“答案正确性”、网页搜索的“任务完成率”)
  • 3)群体语义优势计算(核心创新):传统 GRPO 通过数值优势( \(\hat{A}_i = \frac{r_i - mean(r)}{std(r)}\))指导参数更新,而Training-free GRPO 替换为语义优势(自然语言形式的经验知识),具体步骤:
    • 轨迹总结 :用同一LLM对每个输出 \(o_i\) 生成结构化总结 \(s_i\),包含“推理步骤、工具使用、错误点(若有)”;
    • 语义优势提炼 :基于总结 \(\{s_1,…,s_G\}\) 和当前经验库,让 LLM 分析“成功/失败原因”,提炼通用经验(如“几何题需验证解是否在边界内”),形成语义优势 \(A_{text}\);
    • 筛选有效群体 :仅对“存在明显优劣差异”的群体(即 \(std(r) \neq 0\))提炼语义优势,避免无意义经验
  • 4)经验库优化(无参数更新的“策略优化”):通过语义优势 \(A_{text}\) 迭代更新经验库,LLM 生成 4 类操作指令:
    • Add(添加) :将新提炼的有效经验直接加入经验库;
    • Delete(删除) :移除经验库中过时或低质量的经验;
    • Modify(修改) :基于新经验优化现有经验的通用性(如扩展“几何题验证”到“代数题验证”);
    • Keep(保留) :经验库无需调整时维持现状
  • 更新后的经验库会作为“token 先验”注入下一轮 LLM 调用,引导模型输出向高奖励方向偏移,实现“参数冻结下的策略优化”

SRL(Supervised Reinforcement Learning)

  • 原始论文:Supervised Reinforcement Learning: From Expert Trajectories to Step-wise Reasoning, 20251029, Google Cloud AI Research, UCLA
  • SRL 是一种针对复杂多步推理任务的 LLM 训练框架,核心是将问题拆解为序列决策过程,通过分步专家动作引导和密集奖励信号实现高效学习
    • 用于弥补SFT(刚性逐 token 模仿易过拟合)和 RLVR(依赖最终结果奖励、稀疏信号难学难问题)的缺陷
    • 核心思路是把专家解决方案分解为一系列“逻辑动作”,模型先生成内部推理独白,再输出每步动作,基于与专家动作的相似度获得分步奖励

整体流程概述

  • 整体流程图
  • 动作化问题构建
    • 将专家(优秀的大模型)轨迹 \( \mathbf{y} \) 拆解为步骤动作序列 \( \mathbf{y} = \{\mathbf{y}_{\text{step}_n}\}_{n=1}^N \),每个动作代表一个有意义的决策步骤(如数学中的代数运算、软件任务中的命令执行)
    • 构建分步训练数据:从单个专家解决方案生成 \( N-1 \) 个部分轨迹,输入提示 \( x_{\text{step}_k} = [x, \mathbf{y}_{\text{step}_1}, …, \mathbf{y}_{\text{step}_{k-1} }] \),目标是预测下一步动作 \( \mathbf{y}_{\text{step}_k} \)
  • 分步推理与奖励计算
    • 模型生成格式:
      $$ \mathbf{y}’ \sim p_{\theta}(\cdot | x_{\text{step}_k}) = [\mathbf{y}_{think}’, \mathbf{y}_{\text{step}_k}’] $$
      • 其中 \( \mathbf{y}_{think}’ \) 是内部推理独白(用特定标签封装),\( \mathbf{y}_{\text{step}_k}’ \) 是预测动作
    • 序列相似度奖励公式:
      $$ R(\mathbf{y}_{\text{step}_k}’, \mathbf{y}_{\text{step}_k}) = \frac{2 \sum_{(i,j,n) \in \text{MatchingBlocks}} n}{|S_1| + |S_2|} $$
      • \( S_1 \) 为模型预测动作序列,\( S_2 \) 为专家动作序列
      • \( \text{MatchingBlocks} \) 是两序列中非重叠匹配块的集合,\( n \) 为每个匹配块的长度
      • 若输出格式错误,奖励为 -1,最终奖励范围 \( r \in [0,1] \cup \{-1\} \)
  • 动态采样策略
    • 过滤奖励方差接近零的样本,保留标准偏差超过阈值 \( \epsilon \) 的样本:
      $$ \sqrt{\frac{\sum_{i=1}^G (r(o_i, \mathbf{y}) - \bar{r})^2}{G} } > \epsilon $$
      • \( G \) 为生成的轨迹数量,\( r(o_i, \mathbf{y}) \) 是第 \( i \) 条轨迹的奖励,\( \bar{r} \) 为样本平均奖励
  • 采用 GRPO 目标函数优化策略,仅基于逻辑动作计算奖励,不约束内部推理独白,兼顾动作一致性与推理灵活性

核心优势

  • 密集奖励:即使所有轨迹均错误,仍能通过分步动作相似度提供有效学习信号
  • 灵活推理:避免SFT的刚性模仿,允许模型发展自身推理风格
  • 跨域通用:在数学推理和软件工程代理任务中均表现优异

性能表现

  • 数学推理任务:在 AMC23、AIME24 等竞赛级基准上,SRL 平均性能超 SFT 和 RLVR,SRL+RLVR pipeline 实现最优(平均28.3%)
  • 软件工程任务:在 SWE-Bench 上,Oracle 设置下 resolve rate 达 14.8%,较 SFT 基线提升 74%;端到端设置下性能翻倍

AWPO

  • 原始论文:AWPO: Enhancing Tool-Use of Large Language Models through Explicit Integration of Reasoning Rewards
  • AWPO(Advantage-Weighted Policy Optimization)是用于工具集成方向的 LLM,其核心思想是 通过显式地集成推理奖励(reasoning rewards)来提升模型在复杂任务中的推理和工具调用能力 ,同时避免与基于结果的奖励(outcome rewards)发生冲突
  • 背景:
    • 现有的基于 RL 的工具使用 LLM 训练方法通常仅依赖 可验证的结果奖励(如工具调用的格式正确性、执行结果匹配度),而 忽视了推理过程的质量(如逻辑连贯性、步骤合理性、工具选择恰当性)
    • 若直接简单混合推理奖励与结果奖励可能导致:优化目标冲突、 训练不稳定、 性能提升有限 等问题
  • AWPO 建立在 策略改进上界理论 之上,

核心:奖励设计与优势计算

  • 结果奖励 \(R^{\text{out} }\) :基于规则计算,包括格式正确性(精确匹配)和执行正确性(工具名、参数名、参数值的相似度)
  • 推理奖励 \(R^{\text{reasoning} }\) :由 LLM-as-a-Judge 模型评估生成推理链的逻辑连贯性、工具选择合理性、参数设置准确性等,得分范围 \([0, 1]\)
  • 混合奖励 :
    $$
    R^{\text{mix} } = R^{\text{out} } + R^{\text{reasoning} }
    $$
  • 优势计算 :分别计算基于结果奖励和混合奖励的归一化优势:
    $$
    A^{\text{out} }_{g,j} = \frac{R^{\text{out} }_{g,j} - \bar{R}^{\text{out} }_{g} }{\widehat{\sigma}^{\text{out} }_{g} + \epsilon}, \quad
    A^{\text{mix} }_{g,j} = \frac{R^{\text{mix} }_{g,j} - \bar{R}^{\text{mix} }_{g} }{\widehat{\sigma}^{\text{mix} }_{g} + \epsilon}
    $$
  • 最终加权优势 :结合门控权重与难度权重:
    $$
    A^{\text{hyper} }_{g,j} := d_{g}\left[ (1 - w_{g}^{\text{mix} }) A^{\text{out} }_{g,j} + w_{g}^{\text{mix} } A^{\text{mix} }_{g,j} \right]
    $$

创新1:方差感知门控,Variance-Aware Gating

  • 用于自适应调节推理奖励的权重,避免在结果奖励方差不足时引入噪声
  • 对于每组样本,计算混合奖励与结果奖励的标准差比值:
    $$
    r_{g} := \frac{\widehat{\sigma}_{g}^{\text{mix} } }{\widehat{\sigma}_{g}^{\text{out} }+\widehat{\sigma}_{g}^{\text{mix} }+\varepsilon_{\text{std} } }
    $$
  • 最终得到 门控权重 :
    $$
    w_{g}^{\text{mix} } := \mathbf{1}(\bar{R}_{g}^{\text{out} } < R_{\text{out} }^{\text{max} }) \cdot \mathbf{1}(r_{g} < \varepsilon_{\text{mix} }) \cdot r_{g}
    $$
    • 仅当结果奖励未饱和且混合奖励方差相对可控时,才引入推理奖励信号

创新2:Difficulty-Aware Weighting(难度感知加权)

  • 优先从中等难度样本组中学习,避免过于简单或过于困难的样本主导优化过程
  • 根据结果奖励的组内均值设定权重:
    $$
    d_{g} := \alpha_{\text{base} } + (\alpha_{\text{prio} } - \alpha_{\text{base} }) \cdot \mathbf{1}(\tau_{\text{low} } < \bar{R}_{g}^{\text{out} } < \tau_{\text{high} })
    $$
    • 中等难度区间 \((\tau_{\text{low} }, \tau_{\text{high} })\) 内的样本获得更高权重 \(\alpha_{\text{prio} }\)

创新3:Dynamic Clipping

  • 根据混合信号依赖程度动态调整 PPO 裁剪范围,在高方差信号下收紧信任域以控制噪声风险
  • 裁剪半径随批次平均混合权重自适应调整:
    $$
    \varepsilon := \varepsilon_{\min} + (1 - \bar{w}_{\mathcal{B} })(\varepsilon_{\max} - \varepsilon_{\min})
    $$
    • 当模型更多依赖高方差的推理奖励时(\(\bar{w}_{\mathcal{B} }\) 大),裁剪范围收紧,防止梯度更新过大

Self-Rewarding

  • 原始论文:Self-Rewarding Language Models, ICML 2024, Meta
  • Self-rewarding 是用模型自身替代独立外部奖励模型(RM),以自评估生成响应并提供奖励信号、驱动迭代对齐的范式,核心是单模型兼具生成(Actor)与评估(Judge)能力,降低对人类偏好标注的依赖
  • 论文贡献:
    • 提出了一体化框架,让模型同时具备指令生成、响应生成与自我评估能力,无需分离的奖励模型
    • 验证了迭代式自奖励训练的可行性,实现模型在两大核心能力上的协同提升
    • 为突破人类反馈瓶颈、实现模型持续自我改进提供了新路径
  • 方法流程:
    • 初始化:SFT
    • 自指令生成(Self-Instruction Creation):
      • 为每个指令生成多个候选响应(注:论文中似乎 Instruction 也是模型自己生成的)
      • 模型通过 “LLM-as-a-Judge” 提示自我评估候选响应,给出 0-5 分评分(基于相关性、完整性、实用性等5个维度)
    • 模型训练:从生成的候选响应中筛选出最高分(获胜者)和最低分(失败者)组成偏好对,通过直接偏好优化(DPO)训练下一轮模型
    • 迭代优化:重复上述步骤

DLER

  • 原始论文:DLER: Doing Length pEnalty Right - Incentivizing More Intelligence per Token via Reinforcement Learning, 20251016, NVIDIA
  • DLER(Doing Length pEnalty Right)是一种通过 RL 优化推理语言模型效率的训练方案,核心目标是在不损失准确率的前提下最大化“每 token 智能度”(准确率与响应长度的比值),解决现有长链推理模型输出冗长、延迟高的问题
  • DLER 的核心创新在于:无需复杂的长度惩罚设计,通过优化 RL 训练过程即可实现最优的准确率-效率权衡
    • 长度缩减:相较于原始模型(如 DeepSeek-R1-7B),DLER 将响应长度削减 69%-77%,DA-DLER 进一步降至 80%;
    • 准确率提升:在 MATH、AIME-24 等 5 个推理基准上,DLER 不仅恢复原始模型准确率,还实现 1%-3% 的提升;
    • 推理效率:并行推理时,DLER-7B 生成多轮响应的 latency 降低6 2%,且准确率提升 28%;
    • 泛化性:兼容多种长度惩罚函数(如 Cosine、Laser),且简单截断惩罚的效果优于复杂惩罚,同时训练成本更低
  • 现有基于RL的长度优化方法常采用复杂长度惩罚函数,但存在三大关键问题:
    • 1)优势估计偏差:GRPO 的分组奖励归一化在截断惩罚下产生显著奖励噪声,导致优势估计偏差
    • 2)熵崩溃:重要性采样比率裁剪会过滤掉低概率、高熵的推理过渡 token,限制推理路径探索
    • 3)奖励信号稀疏:大量训练样本因响应超截断长度被分配零奖励,导致训练信号失衡
  • DLER 整合四大关键技术,针对性解决上述挑战:
    • 1)批次级奖励归一化(Batch-wise Reward Normalization) :将GRPO的分组级优势归一化改为批次级归一化,缓解奖励噪声导致的偏差,优势计算方式为:
      $$A_{i, t}^{norm }=\frac{A_{i, t}-mean_{batch}\left(A_{i, t}\right)}{std_{batch}\left(A_{i, t}\right)}$$
      • 其中 \(A_{i, t}=R_{i}’-mean(\{R_{i}’\}_{i=1}^{G})\),\(R_i’\) 为包含正确性奖励与长度惩罚的总奖励
    • 2)更高裁剪阈值(Higher Clipping Threshold) :解耦GRPO中上下裁剪阈值,提高上阈值(\(\epsilon_{high}\)),保留高熵探索性token的梯度更新,避免熵崩溃
    • 3)动态采样(Dynamic Sampling) :过滤所有rollout均为零奖励(过难样本)或全为正奖励(过易样本)的训练样本,重新采样至目标批次大小,构建均衡的训练信号
    • 4)简单截断惩罚(Simple Truncation Penalty) :采用最简洁的长度惩罚机制——对超过固定长度限制的响应分配零奖励,避免复杂惩罚函数带来的训练不稳定

DLER 扩展变体

  • 难度感知DLER(DA-DLER) :根据问题难度动态调整截断长度,模型已可靠解答的简单问题进一步缩短截断长度,复杂问题保留更长token预算,额外降低11%-15%的响应长度
  • 更新选择性权重融合(Update-selective Weight Merging) :针对公开训练数据质量不足导致的准确率下降,融合原始基线模型与DLER训练模型的权重(保留Top25%最大参数更新量并缩放),在恢复基线准确率的同时保持47%的长度缩减

GSPO(包含 GSPO 和 GSPO-Token 两个版本)

GSPO: Group Sequence Policy Optimization

  • 原版的的 序列组策略优化(Group Sequence Policy Optimization, GSPO)算法采用以下 Sequence-level 的优化目标:
    $$
    \mathcal{J}_{\text{GSPO} }(\theta) = \mathbb{E}_{x\sim\mathcal{D},\{y_i\}_{i=1}^G \sim \pi_{\theta_{\text{old} } }(\cdot|x)} \left[\frac{1}{G} \sum_{i=1}^G \min \left(s_i(\theta)\widehat{A}_i, \text{clip}(s_i(\theta), 1-\varepsilon, 1+\varepsilon) \widehat{A}_i\right)\right], \tag{5}
    $$
    • 其中,论文采用基于组的优势估计:
      $$
      \widehat{A}_i = \frac{r(x,y_i) - \text{mean}(\{r(x,y_i)\}_{i=1}^G)}{\text{std}(\{r(x,y_i)\}_{i=1}^G)}, \tag{6}
      $$
    • 并基于序列似然(2023)定义重要性比率 \(s_i(\theta)\):
      $$
      s_i(\theta) = \left(\frac{\pi_{\theta}(y_i|x)}{\pi_{\theta_{\text{old} } }(y_i|x)}\right)^{\frac{1}{|y_i|} } = \exp\left(\frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \log \frac{\pi_{\theta}(y_{i,t}|x,y_{i,<t})}{\pi_{\theta_{\text{old} } }(y_{i,t}|x,y_{i,<t})}\right). \tag{7}
      $$
      • 理解:这里是相当于有点对所有的 Token 的比例对数求平均,再求指数的意思,核心是将 Token 粒度的重要性比例替换成经过 “几何平均(Geometric Mean)“ 后的 Sequence 粒度的(同一个 Sequence 所有 Token 共享的)重要性比例
        • 补充:几何平均(Geometric Mean,GM)的定义:
          $$ \exp\left(\frac{1}{|x|} \sum_{t=1}^{|x|} \log x_t\right) = (\prod_{t=1}^{|x|} x_t)^{\frac{1}{|x|}}$$

GSPO-token:A Token-level Objective Variant

  • 在多轮强化学习等场景中,论文可能希望比 Sequence-level 更细粒度地调整优势
  • 为此,论文引入了 GSPO 的 Token-level 目标变体,即 GSPO-token ,以实现 Token-level 的优势定制:
    $$
    \mathcal{J}_{\text{GSPO-token} }(\theta) = \mathbb{E}_{x\sim\mathcal{D},\{y_i\}_{i=1}^G \sim \pi_{\theta_{\text{old} } }(\cdot|x)} \left[\frac{1}{G} \sum_{i=1}^G \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \min \left(s_{i,t}(\theta)\widehat{A}_{i,t}, \text{clip}(s_{i,t}(\theta), 1-\varepsilon, 1+\varepsilon) \widehat{A}_{i,t}\right)\right], \tag{13}
    $$
    • 其中
      $$
      s_{i,t}(\theta) = \text{sg}[s_i(\theta)] \cdot \frac{\pi_{\theta}(y_{i,t}|x,y_{i,<t})}{\text{sg}[\pi_{\theta}(y_{i,t}|x,y_{i,<t})]},
      $$
    • \(\text{sg}[\cdot]\) 表示仅取值但停止梯度,对应于 PyTorch 中的 detach 操作。GSPO-token 的梯度可以推导为:
      $$
      \begin{align}
      \nabla_{\theta} \mathcal{J}_{\text{GSPO-token} }(\theta) &= \nabla_{\theta} \mathbb{E}_{x\sim\mathcal{D},\{y_i\}_{i=1}^G \sim \pi_{\theta_{\text{old} } }(\cdot|x)} \left[\frac{1}{G} \sum_{i=1}^G \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} s_{i,t}(\theta)\widehat{A}_{i,t}\right] \\
      &= \mathbb{E}_{x\sim\mathcal{D},\{y_i\}_{i=1}^G \sim \pi_{\theta_{\text{old} } }(\cdot|x)} \left[\frac{1}{G} \sum_{i=1}^G s_i(\theta) \cdot \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \widehat{A}_{i,t} \frac{\nabla_{\theta} \pi_{\theta}(y_{i,t}|x,y_{i,<t})}{\pi_{\theta}(y_{i,t}|x,y_{i,<t})}\right] \\
      &= \mathbb{E}_{x\sim\mathcal{D},\{y_i\}_{i=1}^G \sim \pi_{\theta_{\text{old} } }(\cdot|x)} \left[\frac{1}{G} \sum_{i=1}^G \left(\frac{\pi_{\theta}(y_i|x)}{\pi_{\theta_{\text{old} } }(y_i|x)}\right)^{\frac{1}{|y_i|} } \cdot \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \color{red}{\widehat{A}_{i,t}} \nabla_{\theta} \log \pi_{\theta}(y_{i,t}|x,y_{i,<t})\right]. \tag{15-17}
      \end{align}
      $$
      • GSPO-token 与原始 GSPO 的主要区别在于:
        • GSPO 的目标函数中使用的优势函数是 Sequence-level 的 \(\widehat{A}_{i}\)
        • GSPO-token 的目标函数中使用的优势函数是 Token-level 的 \(\color{red}{\widehat{A}_{i,t}}\),这允许同一个序列中,不同的 Token 使用不同的值
  • 当 Response \(y_i\) 中所有 token 的优势设置为相同值(即 \(\widehat{A}_{i,t} = \widehat{A}_i\))时,GSPO-token 和 GSPO 在优化目标、剪裁条件和理论梯度上是数值相同的

OPSD(On-Policy Self-Distillation)

  • 原始论文:(OPSD)Self-Distilled Reasoner: On-Policy Self-Distillation for Large Language Models, 20260126 - 20260305, UCLA & Meta
  • OPSD 损失函数定义如下:
    $$
    \mathcal{L}_{\mathrm{OPSD} }(\theta) = \mathbb{E}_{(x,y^*)\sim \mathcal{S} } \mathbb{E}_{\hat{y} \sim p_S(\cdot | x)} \left[ \frac{1}{|\hat{y}|} \sum_{n=1}^{|\hat{y}|} D\big(p_T | p_S\big) \right]
    $$
    • 用 hint 增强的 Prompt 得到教师模型

RL——DDPO

本文介绍DDPO(Denoising Diffusion Policy Optimization, DDPO)方法

  • 参考链接:
    • 原始论文:TRAINING DIFFUSION MODELS WITH REINFORCEMENT LEARNING, UC Berkeley & MIT, 2024
    • 论文解读:论文阅读:Training Diffusion Models with Reinforcement Learning
    • 源码:github.com/kvablack/ddpo-pytorch/

基本思路

  • 目标:解决 Diffusion 图片生成时无法捕捉“人类审美”反馈的问题,也可以用于多模态对齐
    • 个人理解也可以用于生成最大化某个指标的图片,比如最大化“CTR”的图片?
  • 基本思路:将 Diffusion 的每一个生成步骤视为 MDP 过程,从而通过 RL 来引导生成过程
  • MDP 建模思路: \(\epsilon_\theta(x_t, c, t)\) 作为策略网络,其他MDP的详细信息定义如下
    • \(s_t \triangleq (c, t, x_t)\)
    • \(a_t \triangleq x_{t-1}\)
    • \(\pi(a_t|s_t) \triangleq p_\theta(x_{t-1}|x_t, c)\),这里实际上可以写为 \(p_\theta(x_{t-1}|x_t, c, t)\) 更准确
    • \(P(s_{t+1}|s_t, a_t) \triangleq (\delta_c, \delta_{t-1}, \delta_{x_{t-1}}) \),这里 \(\delta_y\) 是狄拉克 \(\delta\) 分布函数(e Dirac delta distributin),表示在指定值时概率无穷大,其他位置概率为0的概率分布
      • 问题:这里概率本质应该是一个标量数字,不能用三维数字来表达吧?文章这里应该是想表达联合概率分布的含义
    • \(\rho_0(s_0) = \triangleq (p(c), \delta_T, \mathcal{N}(0,\mathbf(I)))\),表示初始状态出现的概率,这里也是联合概率分布
    • \(R(s_t, a_t) = \triangleq
      \begin{cases}
      r(x_0, c)& \text{if}\ t = 0\\
      0& \text{otherwise}
      \end{cases}\),表示仅在最后时刻有reward,其他时刻没有reward反馈

RL——Decision-Transformer

Decision-Transformer,简称DT,使用序列预估的思想去实现决策问题

  • 参考链接:
    • 原始论文:Decision Transformer: Reinforcement Learning via Sequence Modeling, UC Berkeley, NuerIPS 2021
    • 源码:github.com/kzl/decision-transformer

HER 技术

  • 在 Decision Transformer 之前,HER(Hindsight Experience Replay)方法已经有这种事后的思想,HER 过将想要达到的目标状态添加到策略网络的输入端,实现在给定目标的情况下,进行决策

Decision Transformer

returns-to-go轨迹变换

  • 原始的轨迹: \( \tau = (s_1,a_1,r_1,s_2,a_2,r_2,\cdots,s_T,a_T,r_T) \)
  • returns-to-go对应的轨迹: \( \tau = (\hat{R}_1,s_1,a_1,\hat{R}_2,s_2,a_2,\cdots,\hat{R}_T,s_T,a_T) \)
    • \(\hat{R}_t = \sum_{t’=t}^T r_{t’}\) 被称为return-to-go(与state、action等一样的粒度),表示复数或者泛指时,也是用returns-to-go
    • 注意, \(\hat{R}_t\) 没有使用discount ratio,是无折扣的奖励,方便后续实现中减去已获得的奖励实现目标值变换

建模方式

  • 整体架构
  • demo

伪代码

  • 在原始 Transformer 的基础上,DT 算法的实现非常简单,DT 算法的整体伪代码如下(连续版本):

    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
    # R, s, a, t: returns -to -go, states, actions, or timesteps
    # K: context length (length of each input to DecisionTransformer)
    # transformer: transformer with causal masking (GPT)
    # embed_s, embed_a, embed_R: linear embedding layers
    # embed_t: learned episode positional embedding
    # pred_a: linear action prediction layer

    # main model
    def DecisionTransformer(R, s, a, t):
    # compute embeddings for tokens
    pos_embedding = embed_t(t) # per -timestep (note: not per -token)
    a_embedding = embed_a(a) + pos_embedding
    s_embedding = embed_s(s) + pos_embedding
    R_embedding = embed_R(R) + pos_embedding
    # interleave tokens as (R_1, s_1, a_1, ..., R_K, s_K)
    input_embeds = stack(R_embedding, s_embedding, a_embedding)
    # use transformer to get hidden states
    hidden_states = transformer(input_embeds=input_embeds)
    # select hidden states for action prediction tokens
    a_hidden = unstack(hidden_states).actions
    # predict action
    return pred_a(a_hidden)


    # training loop
    for (R, s, a, t) in dataloader: # dims: (batch_size, K, dim)
    a_preds = DecisionTransformer(R, s, a, t)
    loss = mean((a_preds - a) ** 2) # L2 loss for continuous actions
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    # evaluation loop
    target_return = 1 # for instance, expert -level return
    R, s, a, t, done = [target_return], [env.reset()], [], [1], False
    while not done: # autoregressive generation/sampling
    # sample next action
    action = DecisionTransformer(R, s, a, t)[-1] # for cts actions
    new_s, r, done, _ = env.step(action)
    # append new tokens to sequence
    R = R + [R[-1] - r] # decrement returns -to -go with reward
    s, a, t = s + [new_s], a + [action], t + [len(R)]
    R, s, a, t = R[-K:], s[-K:], a[-K:], t[-K:] # only keep context length of K
  • 伪代码讲解:

    • token:包含三种模态的token,分别为return-to-go、state和action
    • 位置编码:虽然是三个token,但是同一个时间片的return-to-go、state和action,对应的位置编码相同
    • 模型的输入:过去 \(K-1\) 个时间片的(return-to-go,state,action)完整信息和当前时间片的(return-to-go,state),共 \((K-1)*3+2\) 个tokens
    • 输出:仅在输入是state token对应的位置上,输出action token为决策目标
    • 损失函数:伪代码中使用的是MSE损失函数(对应连续动作场景),实际上对于离散动作场景, 可以使用交叉熵损失函数(策略网络输出Softmax后的多个头)
    • 训练时:
      • 每个样本仅保留最近的 \(K\) 个步骤,模型输入是 \(K*\) 个样本
      • 时间步 \(t\) 是一直累计的,与 \(K\) 无关
    • 推断时的自回归:
      • 初始化时先指定初始状态 \(s_0\) 和最终目标target_return \(R_0\)
      • 通过DT模型决策得到动作 \(a_t\)
      • 与环境交互执行动作 \(a_t\) 并得到reward \(r_{t}\),并跳转到状态 \(s_{t+1}\)
      • 通过reward \(r_t\) 计算下个时间步的return-to-go \(R_{t+1} = R_t - r_t\)
      • 将 \((a_t, s_{t+1}, R_{t+1})\) 分别加入到各自token列表中
      • 截断到 \(K\) 个时间步,注意动作是保留 \(K-1\) 个,不足 \(K\) 个时间步时,会自动paddding(直接调用Transformer)即可,此时也需要保证模型输入action比return-to-go和state少一个

预测时如何指定Reward目标?

  • 可以使用离线采样样本中Reward最大值作为目标,论文原始表述如下

  • 个人理解:这个目标不一定要是最优目标,也不需要与离线目标完全相等,但是比较难设置:

    • 如果太小,但是生成的不一定是最优的路径
    • 如果太大,理论上,可以生成最优解,但是因为模型没有见过该目标值(模型做不到,因为训练时也收集不到这样的样本),可能会发生意想不到情况

实验结果

  • Atari上收集的实验数据集训练的实验结果见如下图:

    • 在部分场景上,CQL效果更好
  • D4RL以及OpenAI-Gym上收集的数据集上的实验结果如下图(注意,补充了一些):

    • 补充了一些D4RL中没有的数据集(Medium是指直接用Medium Agent与环境交互生成的样本;Medium-Replay是指训练一个Medium Agent时收集的Replay Buffer;Medium-Expert是Medium Agent和Expert Agent两种策略收集到的数据集的混合)
1…171819…66
Joe Zhou

Joe Zhou

Stay Hungry. Stay Foolish.

659 posts
53 tags
GitHub E-Mail
© 2026 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4