源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py
  1. /
  2. /
  3. /

/

finetuning_args.py

@dataclass
class RLHFArguments:
    r"""
    Arguments pertaining to the PPO and DPO training.
    """

    dpo_beta: float = field(
        default=0.1,
        metadata={"help": "The beta parameter for the DPO loss."},
    )
    dpo_loss: Literal["sigmoid", "hinge", "ipo", "kto_pair"] = field(
        default="sigmoid",
        metadata={"help": "The type of DPO loss to use."},
    )
    dpo_label_smoothing: float = field(
        default=0.0,
        metadata={"help": "The robust DPO label smoothing parameter in cDPO that should be between 0 and 0.5."},
    )
    dpo_ftx: float = field(
        default=0.0,
        metadata={"help": "The supervised fine-tuning loss coefficient in DPO training."},
    )
    kto_beta: float = field(
        default=0.1,
        metadata={"help": "The beta parameter for the KTO loss."},
    )
    kto_chosen_weight: float = field(
        default=1.0,
        metadata={"help": "The weight factor of the desirable losses in KTO training."},
    )
    kto_rejected_weight: float = field(
        default=1.0,
        metadata={"help": "The weight factor of the undesirable losses in KTO training."},
    )
    kto_ftx: float = field(
        default=0.0,
        metadata={"help": "The supervised fine-tuning loss coefficient in KTO training."},
    )
    orpo_beta: float = field(
        default=0.1,
        metadata={"help": "The beta (lambda) parameter in the ORPO loss representing the weight of the SFT loss."},
    )
    ppo_buffer_size: int = field(
        default=1,
        metadata={"help": "The number of mini-batches to make experience buffer in a PPO optimization step."},
    )
    ppo_epochs: int = field(
        default=4,
        metadata={"help": "The number of epochs to perform in a PPO optimization step."},
    )
    ppo_score_norm: bool = field(
        default=False,
        metadata={"help": "Use score normalization in PPO training."},
    )
    ppo_target: float = field(
        default=6.0,
        metadata={"help": "Target KL value for adaptive KL control in PPO training."},
    )
    ppo_whiten_rewards: bool = field(
        default=False,
        metadata={"help": "Whiten the rewards before compute advantages in PPO training."},
    )

用 Python 的 dataclass 模块定义了一个名为 RLHFArguments 的类,该类用于存储与PPO(Proximal Policy Optimization,近端策略优化)和DPO(可能是自定义的策略优化技术,文档中未明确解释)训练相关的参数。

python

复制

@dataclass

这一行是一个装饰器,用于自动为类生成一些特殊方法,如 __init__()__repr__()__eq__()。这使得定义类更简洁,主要用于存储数据。

python

复制

class RLHFArguments:

定义了一个名为 RLHFArguments 的类。

python

复制

    r"""
    Arguments pertaining to the PPO and DPO training.
    """

这是一个多行字符串,作为文档字符串,描述了这个类的用途:存储与 PPO 和 DPO 训练相关的参数。

接下来的每一块代码都定义了一个类的字段,每个字段都通过 field() 函数设置了一些元数据和默认值。

python

复制

    dpo_beta: float = field(
        default=0.1,
        metadata={"help": "The beta parameter for the DPO loss."},
    )

定义了一个名为 dpo_beta 的浮点数字段,其默认值为 0.1。字段的元数据中包含了一个帮助字符串,解释了这个参数是 DPO 损失中的 beta 参数。

python

复制

    dpo_loss: Literal["sigmoid", "hinge", "ipo", "kto_pair"] = field(
        default="sigmoid",
        metadata={"help": "The type of DPO loss to use."},
    )

定义了一个名为 dpo_loss 的字段,类型为 Literal,限定了可能的值为 "sigmoid"、"hinge"、"ipo" 或 "kto_pair"。默认值为 "sigmoid",元数据提供了关于此参数的帮助说明。

python

复制

    dpo_label_smoothing: float = field(
        default=0.0,
        metadata={"help": "The robust DPO label smoothing parameter in cDPO that should be between 0 and 0.5."},
    )

定义了一个浮点数字段 dpo_label_smoothing,默认值为 0.0,元数据中的帮助字符串解释了这是一个标签平滑参数,用于 DPO 中的 robust label smoothing。

python

复制

    dpo_ftx: float = field(
        default=0.0,
        metadata={"help": "The supervised fine-tuning loss coefficient in DPO training."},
    )

定义了一个名为 dpo_ftx 的浮点数字段,代表在 DPO 训练中使用的监督性微调损失系数,其默认值为 0.0

python

复制

    kto_beta: float = field(
        default=0.1,
        metadata={"help": "The beta parameter for the KTO loss."},
    )

定义了一个名为 kto_beta 的浮点数字段,其默认值和帮助信息与 dpo_beta 类似,但用于 KTO 损失。

python

复制

    kto_chosen_weight: float = field(
        default=1.0,
        metadata={"help": "The weight factor of the desirable losses in KTO training."},
    )

定义了一个名为 kto_chosen_weight 的浮点数字段,其默认值为 1.0,用于指定在 KTO 训练中所需损失的权重因子。

python

复制

    kto_rejected_weight: float = field(
        default=1.0,
        metadata={"help": "The weight factor of the undesirable losses in KTO training."},
    )

类似于 kto_chosen_weight,但用于不期望的损失。

python

复制

    kto_ftx: float = field(
        default=0.0,
        metadata={"help": "The supervised fine-tuning loss coefficient in KTO training."},
    )

dpo_ftx 类似,但用于 KTO 训练中的监督性微调损失系数。

python

复制

    orpo_beta: float = field(
        default=0.1,
        metadata={"help": "The beta (lambda) parameter in the ORPO loss representing the weight of the SFT loss."},
    )

定义了一个名为 orpo_beta 的浮点数字段,用于 ORPO 损失中的 beta (或 lambda) 参数,表示 SFT 损失的权重。

python

复制

    ppo_buffer_size: int = field(
        default=1,
        metadata={"help": "The number of mini-batches to make experience buffer in a PPO optimization step."},
    )

定义了一个名为 ppo_buffer_size 的整数字段,用于指定在 PPO 优化步骤中生成经验缓冲区的迷你批次数量。

python

复制

    ppo_epochs: int = field(
        default=4,
        metadata={"help": "The number of epochs to perform in a PPO optimization step."},
    )

定义了一个名为 ppo_epochs 的整数字段,用于指定在 PPO 优化步骤中要执行的 epoch 数量。

python

复制

    ppo_score_norm: bool = field(
        default=False,
        metadata={"help": "Use score normalization in PPO training."},
    )

定义了一个名为 ppo_score_norm 的布尔字段,其默认值为 False,用于指定是否在 PPO 训练中使用分数归一化。

python

复制

    ppo_target: float = field(
        default=6.0,
        metadata={"help": "Target KL value for adaptive KL control in PPO training."},
    )

定义了一个名为 ppo_target 的浮点数字段,其默认值为 6.0,用于指定 PPO 训练中自适应 KL 控制的目标 KL 值。

python

复制

    ppo_whiten_rewards: bool = field(
        default=False,
        metadata={"help": "Whiten the rewards before compute advantages in PPO training."},
    )

定义了一个名为 ppo_whiten_rewards 的布尔字段,其默认值为 False,用于指定是否在 PPO 训练中计算优势之前对奖励进行白化处理。

这个类通过定义这些字段,为 PPO 和 DPO 训练提供了一个结构化的方式来存储和访问所需的配置参数。

ref_model: Optional[str] = field(
        default=None,
        metadata={"help": "Path to the reference model used for the PPO or DPO training."},
    )
    ref_model_adapters: Optional[str] = field(
        default=None,
        metadata={"help": "Path to the adapters of the reference model."},
    )
    ref_model_quantization_bit: Optional[int] = field(
        default=None,
        metadata={"help": "The number of bits to quantize the reference model."},
    )
    reward_model: Optional[str] = field(
        default=None,
        metadata={"help": "Path to the reward model used for the PPO training."},
    )
    reward_model_adapters: Optional[str] = field(
        default=None,
        metadata={"help": "Path to the adapters of the reward model."},
    )
    reward_model_quantization_bit: Optional[int] = field(
        default=None,
        metadata={"help": "The number of bits to quantize the reward model."},
    )
    reward_model_type: Literal["lora", "full", "api"] = field(
        default="lora",
        metadata={"help": "The type of the reward model in PPO training. Lora model only supports lora training."},
    )

继续使用 Python 的 dataclass 来定义 RLHFArguments 类的属性,专注于模型相关的配置:

python

复制

ref_model: Optional[str] = field(
        default=None,
        metadata={"help": "Path to the reference model used for the PPO or DPO training."},
    )

定义了一个名为 ref_model 的字段,其类型为 Optional[str],意味着它可以是一个字符串或者 None。默认值是 None。元数据中的帮助信息说明这个字段是用来存放在 PPO 或 DPO 训练中使用的参考模型的路径。

python

复制

ref_model_adapters: Optional[str] = field(
        default=None,
        metadata={"help": "Path to the adapters of the reference model."},
    )

定义了一个名为 ref_model_adapters 的字段,类型也是 Optional[str],默认值是 None。元数据中的帮助信息说明这个字段是用来存放参考模型适配器的路径。

python

复制

ref_model_quantization_bit: Optional[int] = field(
        default=None,
        metadata={"help": "The number of bits to quantize the reference model."},
    )

定义了一个名为 ref_model_quantization_bit 的字段,其类型为 Optional[int],意味着它可以是一个整数或者 None。默认值是 None。元数据中的帮助信息说明这个字段是用来指定参考模型量化的位数。

python

复制

reward_model: Optional[str] = field(
        default=None,
        metadata={"help": "Path to the reward model used for the PPO training."},
    )

定义了一个名为 reward_model 的字段,类型为 Optional[str],默认值是 None。元数据中的帮助信息说明这个字段是用来存放在 PPO 训练中使用的奖励模型的路径。

python

复制

reward_model_adapters: Optional[str] = field(
        default=None,
        metadata={"help": "Path to the adapters of the reward model."},
    )

定义了一个名为 reward_model_adapters 的字段,类型也是 Optional[str],默认值是 None。元数据中的帮助信息说明这个字段是用来存放奖励模型适配器的路径。

python

复制

reward_model_quantization_bit: Optional[int] = field(
        default=None,
        metadata={"help": "The number of bits to quantize the reward model."},
    )

定义了一个名为 reward_model_quantization_bit 的字段,其类型为 Optional[int],默认值是 None。元数据中的帮助信息说明这个字段是用来指定奖励模型量化的位数。

python

复制

reward_model_type: Literal["lora", "full", "api"] = field(
        default="lora",
        metadata={"help": "The type of the reward model in PPO training. Lora model only supports lora training."},
    )

定义了一个名为 reward_model_type 的字段,其类型为 Literal,限定了可能的值为 "lora"、"full" 或 "api"。默认值是 "lora"。元数据中的帮助信息说明这个字段是用来指定 PPO 训练中使用的奖励模型的类型,并注明 "lora" 类型模型仅支持 lora 训练。

以上代码通过为模型配置提供详细字段,帮助确保模型训练的灵活性和可配置性。

大模型中 奖励模型的训练流程

大模型中奖励模型的训练流程是一个关键步骤,它使得模型能够根据人类的偏好来优化其行为。以下是奖励模型训练流程的一般步骤20212228:

数据预处理:首先对原始数据集进行清洗、分词、编码等预处理操作,以便模型能够理解和处理。

标注数据:收集人类对模型输出的反馈。这些反馈可以是直接的评分,或者更常见的是,对输出的排序或比较。例如,给定两个模型的输出,人类评估者需要决定哪个输出更符合他们的期望。

构建奖励模型:设计一个神经网络作为奖励模型,该模型能够接受模型输出和相关的上下文信息,并输出一个奖励信号(通常是标量值)。

训练奖励模型:使用标注好的数据来训练奖励模型。这个过程中,模型学习如何根据人类的偏好来评估输出的好坏。训练通常采用pairwise loss或者ranking loss,鼓励模型给更好的输出更高的分数。

优化算法:奖励模型的训练通常涉及到梯度下降等优化算法,以最小化预测分数与实际人类评价之间的误差。

模型评估:在训练过程中,持续评估奖励模型的性能,确保它能够准确地反映人类的偏好。

迭代改进:根据评估结果,可能需要回到步骤3或4,对奖励模型的结构或训练数据进行调整,以改进模型的性能。

集成到强化学习:一旦奖励模型训练完成,就可以将其集成到强化学习框架中,用来指导大模型通过强化学习进行微调。

强化学习微调:在强化学习阶段,大模型(actor model)根据奖励模型提供的反馈进行微调,以生成更符合人类偏好的输出。

监控和调整:在强化学习过程中,持续监控模型的行为和输出,必要时进行调整,以确保模型的输出符合预期。

通过这个流程,奖励模型能够为大模型提供一种量化的方式来评估和优化其输出,使其更加符合人类的偏好和期望。20 强调了在强化学习阶段,用到的奖励模型和价值模型(critic model)都使用同一个模型初始化,因此在训练奖励模型的过程中,也是在训练价值模型。奖励模型的输入是prompt+answer的形式,目的是让模型学会对prompt+answer进行打分。21 提到了奖励模型训练的必要性,尤其是在SFT后,需要奖励模型来告知模型什么是「好的数据」,什么是「不好的数据」。22 详细描述了奖励模型训练的过程,包括如何将人类偏好转化为数字奖励信号,以及如何使用比较评分系统来构建奖励模型。28 解释了奖励模型训练数据是人工对问题的每个答案进行排名,然后利用这些排序结果来训练奖励模型。

PPO(Proximal Policy Optimization)和DPO(Direct Preference Optimization)直接兴趣优化,都是用于强化学习中的优化算法,但它们在目标、方法和实现上存在一些关键的区别:

目标差异

  • PPO:旨在优化策略以最大化期望回报。它通过策略梯度方法来更新策略,同时限制更新步长,以保持新策略不会偏离旧策略太远3233。
  • DPO:专注于直接优化模型输出,使其更符合人类的偏好,而不是间接通过奖励信号来调整策略。它通过将强化学习的两个步骤合并为一个,简化了训练过程3839。

方法差异

  • PPO:使用剪辑的目标函数来限制策略更新的步长,避免大幅度更新带来的风险。它通过迭代更新和调整超参数来优化策略3233。
  • DPO:通过分析从奖励函数到最优策略的映射,将奖励函数上的偏好损失函数转换为策略上的损失函数,从而简化了优化过程3839。

实现差异

  • PPO:实现相对复杂,涉及到多个超参数的调整,如剪辑参数(epsilon)和惩罚系数(beta),并且需要细致的调参来保证算法的稳定性和效率3233。
  • DPO:提供了一种更简单直接的实现方式,它使用二元交叉熵目标来优化策略,无需在训练期间显式学习奖励函数或从策略中采样3839。

训练稳定性

  • PPO:虽然稳定性较强,但仍需仔细调整超参数和可能需要多次迭代来达到理想性能3233。
  • DPO:由于其简化的训练过程,可能在某些情况下提供更稳定的训练体验,且超参数调整相对简单3839。

适用性

  • PPO:已被证明在多种任务和复杂环境中表现优异,具有广泛的适用性,特别是在需要复杂策略和高样本效率的场景中3233。
  • DPO:作为一种较新的算法,可能在特定类型的任务中表现更好,尤其是那些需要模型输出与人类偏好紧密对齐的场景3839。

计算效率

  • PPO:虽然计算效率较高,但仍然需要处理强化学习中的一些常见问题,如探索与利用的平衡3233。
  • DPO:通过直接优化偏好,可能在某些情况下提供更高的计算效率,因为它避免了强化学习中的一些复杂性3839。

总的来说,PPO是一种成熟的、广泛使用的强化学习算法,而DPO是一种新兴的方法,它试图通过简化训练过程来提高效率和简化超参数调整。选择哪种算法取决于具体的应用场景、任务需求以及对计算资源和训练时间的考量。

复制再试一次分享

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py
www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

原理

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

传统的 RLHF 步骤一般是:训练一个 reward model 对 prompt 的 response 进行打分,训练完之后借助 PPO 算法,使得 SFT 的模型和人类偏好对齐,这个过程我们需要初始化四个基本结构一致的 transformer 模型。DPO 算法,提供了一种更为简单的 loss function,而这个就是 DPO 的核心思想:针对奖励函数的 loss 函数被转换成针对策略的 loss 函数,而针对策略的 loss 函数又暗含对奖励的表示,即人类偏好的回答会暗含一个更高的奖励。

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

�DPO(��;�ref)=−�(�,��,��)∼�[log⁡�(�log⁡��(��∣�)�ref(��∣�)−�log⁡��(��∣�)�ref(��∣�))]

这个函数唯一的超参数是 � ,决定了不同策略获得的奖励之间的 margin。

可以看出 DPO 虽然说是没有用强化学习,但是还是有强化学习的影子,相当于 beta 是一个固定的 reward,通过人类偏好数据,可以让这个奖励的期望最大化,本至上来说 dpo 算法也算是一种策略迭代。

对 loss function 求导,可得如下表达式:

∇��DPO(��;�ref)=−��(�,��,��)∼�[�(�^�(�,��)−�^�(�,��))⏟higher weight when reward estimate is wrong [∇�log⁡�(��∣�)⏟increase likelihood of ��−∇�log⁡�(��∣�)⏟decrease likelihood of ��]]

其中 �^�(�,�)=�log⁡��(�∣�)�ref (�∣�) 。

不得不佩服作者构思的巧妙,通过求导,作者捕捉到了”暗含“的 reward —— �(�^�(�,��)−�^�(�,��)) ,作者在论文里说到,当我们在让 loss 降低的过程中,这个 reward 也会变小(可以用来做 rejected_rewards)。因而在下面的代码实现里,chosen_rewards 和 rejected_rewards 就来自这个想法。

TRPO(Trust Region Policy Optimization)是一种策略优化算法,旨在提高策略梯度方法的稳定性和效率。TRPO通过限制每次策略更新的步长,确保在优化过程中不会导致策略发生剧烈变化,从而避免破坏策略的稳定性。

主要思想

TRPO的核心思想是通过定义信赖域(Trust Region)来限制每一步策略更新的变化幅度,从而保证策略更新的稳定性和收敛性。

目标函数

TRPO的目标是最大化累积回报,但同时限制策略更新的变化幅度。具体来说,TRPO解决以下优化问题:

max⁡���∼��old,�∼�old[��(�∣�)�old(�∣�)�^(�,�)]θmax​Es∼ρπold​​,a∼πold​​[πold​(a∣s)πθ​(a∣s)​A^(s,a)]

同时满足以下信赖域约束:

subject to ��∼��old[���(�old(⋅∣�)∥��(⋅∣�))]≤�subject to Es∼ρπold​​​[DKL​(πold​(⋅∣s)∥πθ​(⋅∣s))]≤δ

其中:

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

πold​ 是旧策略。

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

πθ​ 是新策略,参数为 θ。

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

A^(s,a) 是优势函数。

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

DKL​ 是KL散度,衡量两个分布之间的差异。

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

δ 是一个小常数,表示允许的策略变化幅度。

www.zeeklog.com  - 源码解析RHLF:LLaMA-Factory/src/llamafactory/hparams/finetuning_args.py

关键步骤

估计优势函数

  • 使用优势函数 A^(s,a) 来估计当前策略相对于基准策略的优越性。

构建目标函数

  • 使用策略梯度方法构建目标函数,最大化优势函数的期望值。

约束优化

  • 通过引入KL散度约束,限制每次策略更新的变化幅度,确保策略更新在信赖域内进行。

解决优化问题

  • 使用共轭梯度法(Conjugate Gradient Method)等优化技术,在保持约束的同时,最大化目标函数。

优点

稳定性高

  • 通过限制策略更新的步长,避免了策略更新过大导致的不稳定性。

收敛性好

  • 带有理论保证,能够逐步提升策略的性能,最终收敛到最优策略。

适用性广

  • 适用于离散和连续动作空间,能够处理复杂的强化学习任务。

缺点

实现复杂

  • 由于引入了KL散度约束和共轭梯度法,算法实现相对复杂。

计算开销大

  • 需要计算优势函数和KL散度,优化过程中的计算开销较大。

与PPO的比较

TRPO和PPO都是为了提高策略梯度方法的稳定性,但它们的实现方式和复杂度有所不同:

策略更新的方式

  • TRPO:通过KL散度约束来限制策略更新的变化幅度,确保在信赖域内进行优化。
  • PPO:通过引入剪辑机制,限制策略更新的概率比 ��(�)rt​(θ) 在一定范围内,从而提高稳定性。

实现复杂度

  • TRPO:实现复杂度较高,需要计算KL散度和使用共轭梯度法进行优化。
  • PPO:实现相对简单,计算效率较高,适用于大规模应用。

计算开销

  • TRPO:计算开销较大,适用于较小规模或特定任务。
  • PPO:计算效率较高,适用于大规模和复杂环境。

小结

TRPO通过引入信赖域约束,确保每次策略更新的变化幅度在可控范围内,从而提高策略优化的稳定性和收敛性。尽管实现复杂度和计算开销较大,但其理论保证和性能提升使其在许多复杂强化学习任务中表现优异。相比之下

Read more

深入理解 Proxy 和 Object.defineProperty

在JavaScript中,对象是一种核心的数据结构,而对对象的操作也是开发中经常遇到的任务。在这个过程中,我们经常会使用到两个重要的特性:Proxy和Object.defineProperty。这两者都允许我们在对象上进行拦截和自定义操作,但它们在实现方式、应用场景和灵活性等方面存在一些显著的区别。本文将深入比较Proxy和Object.defineProperty,包括它们的基本概念、使用示例以及适用场景,以帮助读者更好地理解和运用这两个特性。 1. Object.defineProperty 1.1 基本概念 Object.defineProperty 是 ECMAScript 5 引入的一个方法,用于直接在对象上定义新属性或修改已有属性。它的基本语法如下: javascript 代码解读复制代码Object.defineProperty(obj, prop, descriptor); 其中,obj是目标对象,prop是要定义或修改的属性名,descriptor是一个描述符对象,用于定义属性的特性。 1.2 使用示例 javascript 代码解读复制代码//

By Ne0inhk

Proxy 和 Object.defineProperty 的区别

Proxy 和 Object.defineProperty 是 JavaScript 中两个不同的特性,它们的作用也不完全相同。 Object.defineProperty 允许你在一个对象上定义一个新属性或者修改一个已有属性。通过这个方法你可以精确地定义属性的特征,比如它是否可写、可枚举、可配置等。该方法的使用场景通常是需要在一个对象上创建一个属性,然后控制这个属性的行为。 Proxy 也可以用来代理一个对象,但是相比于 Object.defineProperty,它提供了更加强大的功能。使用 Proxy 可以截获并重定义对象的基本操作,比如访问属性、赋值、函数调用等等。在这些操作被执行之前,可以通过拦截器函数对这些操作进行拦截和修改。因此,通过 Proxy,你可以完全重写一个对象的默认行为。该方法的使用场景通常是需要对一个对象的行为进行定制化,或者需要在对象上添加额外的功能。 对比 以下是 Proxy 和 Object.defineProperty 的一些区别对比: 方面ProxyObject.defineProperty语法使用 new Proxy(target,

By Ne0inhk