Hugging Face Hub
- Models, Spaces, and Datasets are hosted on the Hugging Face Hub as Git repositories
- Do you have files larger than 10MB? Those files should be tracked with
git-lfs, which you can initialize with:git lfs install - Note that if your files are larger than 5GB you’ll also need to run:
hf lfs-enable-largefiles . - Pull Request 之所以叫这个名字,是因为它准确地描述了请求者(你)和被请求者(项目维护者)之间的动作和方向。
- PR = “我改好了,请你把我这边的修改拉(pull)进你的主分支吧。”这个 “pull” 并不是指你自己去拉,而是 请求项目维护者去拉你的代码。
- Templates
transformers
- quickstart
- Auto Classes
- Instantiating one of AutoConfig, AutoModel, and AutoTokenizer will directly create a class of the relevant architecture.
- transformers库中 AutoImageProcessor 实例话出来的processor 的作用都有哪些
- 一旦你通过
AutoImageProcessor.from_pretrained()实例化了一个处理器,这个 processor 实例 就成为了一个强大的工具,专门用于准备图像数据,使其能够被特定的预训练视觉模型使用。它的主要作用可以概括为以下几点: - 图像标准化 (Normalization)
- 图像尺寸调整 (Resizing) 和裁剪 (Cropping)
- 通道格式转换 (Channel Format Conversion)
- 图像到张量转换 (Image to Tensor Conversion)
- 批处理 (Batching)
- 数据增强 (Data Augmentation)
- 一旦你通过
- transoformer库中 TFAutoModel和AutoModel的区别是什么
AutoModel:用于加载 PyTorch 框架下的模型。TFAutoModel:用于加载 TensorFlow 2.0 框架下的模型。FlaxAutoModel: 用于加载 Flax(基于 JAX 的框架)下的模型。
- AutoModel 类的后缀
- LM
- CausalLM
- MaskedLM
- MaskedGeneration
- sequenceClassification
- TokenClassification
- NextSentencePrediction
- MultipleChoice
- Seq2SeqLM
- QuestionAnswering
- LM
- Backbone
- A backbone is a model used for feature extraction for higher level computer vision tasks such as object detection and image classification.
- Data Collator
- Data collators are objects that will form a batch by using a list of dataset elements as input. These elements are of the same type as the elements of
train_datasetoreval_dataset. - 我理解 Data Collator 就是把多个列放在一起,就是collator的字面意思,但其实内部还是会有一些具体的数据处理逻辑,比如padding、数据扩增等
- Data collators are objects that will form a batch by using a list of dataset elements as input. These elements are of the same type as the elements of
- pipeline
from transformers import pipelinegenerator = pipeline(model="openai-community/gpt2")generator("I can't believe you did such a ", do_sample=False)[{'generated_text': "I can't believe you did such a icky thing to me. I'm so sorry. I'm so sorry. I'm so sorry. I'm so sorry. I'm so sorry. I'm so sorry. I'm so sorry. I"}]
- chat with models
- Templates
Diffusers
- LoRA
- Add a LoRA to a pipeline with the load_lora_weights() method. Some LoRA’s require a special word to trigger it, such as
Realism, in the example below. Check a LoRA’s model card to see if it requires a trigger word. - LoRA文件就是一种插件
- Add a LoRA to a pipeline with the load_lora_weights() method. Some LoRA’s require a special word to trigger it, such as
Sentence Transformers
sentence embedding demo
from sentence_transformers import SentenceTransformer
# 1. Load a pretrained Sentence Transformer model
model = SentenceTransformer("all-MiniLM-L6-v2")
# The sentences to encode
sentences = [
"The weather is lovely today.",
"It's so sunny outside!",
"He drove to the stadium.",
]
# 2. Calculate embeddings by calling model.encode()
embeddings = model.encode(sentences)
print(embeddings.shape)
# [3, 384]
# 3. Calculate the embedding similarities
similarities = model.similarity(embeddings, embeddings)
print(similarities)
# tensor([[1.0000, 0.6660, 0.1046],
# [0.6660, 1.0000, 0.1411],
# [0.1046, 0.1411, 1.0000]])
trl
- Quickstart 简单的helloworld程序。
- evaluation: The important thing is that this process should yield a scalar value for each query/response pair.
- 这里的例子代码应该已经过时了,PPOTrainer现在没有step这个方法。
- Dataset formats and types
- How-to guides
- customozing the training
- Memory efficient fine-tuning by sharing layers
- customozing the training
PPO
- PPO策略中的一些基础知识
- 深度学习硬件配置中的概念 device rank world_size node 分别是指什么
- 这四个概念的关系可以概括为:node 是物理机器。 一个 node 可以包含多个 device,device通常是指GPU。 每个 device 通常由一个独立的 rank 进程来控制。 所有 rank 进程的总数就是 world_size。
- PPO示例
- trl/examples/scripts/ppo/ppo.py at v0.21.0 · huggingface/trl
- model=policy, ref_model=ref_policy, reward_model=reward_model, value_model=value_model,、
- reward_model其实是每一小步决策的即时的、直接奖励,value_model对每一小步决策的全局性、长期性后果进行预测。
- advantage 我直观理解是因为Q(s,a) 这个state的随机选择导致后续的所有reward都比较异常,所以要消除一部分state的随机性,所以减去state的value;如果训练数据中的Q(s,a)中的s足够多,那么就可以直接使用Q,但因为不够多有了随机性,所以Q代表的return分布就偏离了所谓的全局性的Q的分布,所以通过减去这个s的随机性从而拉回正常分布。
- Mixin 类本身不能独立实例化,它不是用来创建对象的。相反,它像一个“功能包”或“能力插件”,专门设计用来被其他类继承。当一个类继承了一个或多个 Mixin 类时,它就自动获得了这些 Mixin 类所定义的所有方法和属性。
- PPO中的数据处理
transformers.tokenization_utils_base.PreTrainedTokenizerBase.batch_encode_plus方法中包含了 tokenizer中的padding处理。- PPO官方例子中中的代码,
- 为什么 padding_side 和后面的padding=false ,这两者不冲突么
- 代码中两个地方使用了tokenizer,
PPOTrainer( args=training_args, processing_class=tokenizer,...)这里的tokenizer定义中是带有padding=“left"的。- 这里的tokenizer 在PPOTrainer中是作为 processing_class 的作用。
- 在PPOTrainer中 会根据 processing_class 生成一个 DataCollatorWithPadding 类用于数据进行处理,而该类的计算逻辑是使用tokenizer对数据仅仅进行padding操作(这里的padding是右对齐),这里不包括encoding操作。因为训练数据已经根据第二条中的 prepare_dataset进行了encoding。
- prepare_dataset 中的 tokenize函数。
- 该函数的输入是一条数据,所以不需要进行padding。
- 总结: 即encodding和padding是分在两个地方进行处理的,分别是PPOTrainer实例化之前 和 PPOTrainer的train方法内部 分别进行encoding和padding。
- 至于为什么这样做,从代码中没有看出原因。可能是为了更细粒度的控制训练和评估各自场景下的逻辑。
- 代码中两个地方使用了tokenizer,
- 为什么 padding_side 和后面的padding=false ,这两者不冲突么
trl.trainer.utils.selective_log_softmax- 其中实现针对FP32 FP64 采用了高效的 logsumexp方法,针对其他的格式采用了低效的方法,因为低精度格式计算容易出现问题。
logsumexp的标准定义是: $$\log \sum_{i} \exp(x_i)$$,如果直接按这个公式实现,当 $x_i$ 的值很大时,$\exp(x_i)$ 可能会导致数值上溢(overflow),超出浮点数的表示范围,结果变为无穷大。- 工程上的稳定实现方法
1. 为了避免上溢,
logsumexp在大多数深度学习框架(如 PyTorch、TensorFlow)中都有一个经过优化的、数值稳定的实现。核心思想是利用对数函数的性质,将指数运算中的大数相加问题,转换为对数运算中的小范围数相加问题。 2. 具体方法如下:- 找到最大值:首先,找到输入向量 $x$ 中的最大值 $x_{max}$。
- 转换公式:利用 $e^{a+b} = e^a e^b$ 的性质,将原公式进行等价转换: $$\log \sum_{i} \exp(x_i) = \log \left( \exp(x_{max}) \sum_{i} \exp(x_i - x_{max}) \right)$$
- 拆分对数:利用 $\log(ab) = \log(a) + \log(b)$ 的性质,进一步拆分: $$= \log(\exp(x_{max})) + \log \left( \sum_{i} \exp(x_i - x_{max}) \right)$$ $$= x_{max} + \log \left( \sum_{i} \exp(x_i - x_{max}) \right)$$
- 为什么这个方法更稳定?
- 防止上溢:在转换后的公式中,$x_i - x_{max}$ 的值都是负数或者零。这意味着 $\exp(x_i - x_{max})$ 的值都在 $(0, 1]$ 范围内。这样,即使 $x_i$ 非常大,指数运算的结果也不会上溢。
- 保持精度:虽然 $x_i - x_{max}$ 的值是负数,但它们之间的相对大小关系保持不变,这保证了计算结果的精确度。
- 计算效率:这个稳定的实现只需要额外进行一次
max运算和一次加法运算,对整体计算效率影响很小。 - 例如,在 PyTorch 中,
torch.logsumexp函数就是以这种方式实现的。当你使用它时,框架会自动处理这些数值稳定性的细节。 - 这个工程优化方法在深度学习中非常重要,特别是在处理诸如 Softmax 交叉熵损失、信念传播(Belief Propagation)等需要大量指数和对数运算的场景。
- 深度学习硬件配置中的概念 device rank world_size node 分别是指什么
- PPO策略的流程(from chatgpt) ^PPO-process
- 一、PPO算法核心思想
- PPO 属于**策略梯度(Policy Gradient)**家族,目标是通过不断优化策略参数,使得智能体在环境中获得更高的期望回报。
- 它的关键在于:在更新策略时限制更新幅度,防止策略改变太大导致训练不稳定。
- 二、PPO的基本流程(典型版本:PPO-Clip)
- 采样(Rollout)
- 使用当前策略 $\pi_{\theta_{old}}(a_t|s_t)$,与环境交互,收集一批数据: $(s_t, a_t, r_t, s_{t+1})$
- 并计算折扣回报 $R_t$ 。
- 计算优势函数(Advantage Estimate)
- 通常用 GAE(Generalized Advantage Estimation): $\hat{A}t = \sum{l=0}^{\infty} (\gamma\lambda)^l \delta_{t+l}$
- 其中 $\delta_t = r_t + \gamma V(s_{t+1}) - V(s_t)$。
- 计算重要性比率(ratio)
- $r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}$ 表示新旧策略在同一动作上的“概率变化”。
- 构建PPO的目标函数(Clipped Surrogate Objective)
- $\large L^{CLIP}(\theta) = \mathbb{E}_t \big[ \min( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1 - \epsilon, 1 + \epsilon) \hat{A}_t )\big]$
- 当 $r_t(\theta)$ 偏离 1 太多(超过 ±ε)时,会被截断(clip),防止过度更新。 ε 通常取 0.1~0.2。
- 优化目标 + 值函数 + 熵正则
- 实际优化的目标函数通常是三项之和: $L(\theta) = L^{CLIP}(\theta) - c_1 L^{VF}(\theta) + c_2 S[\pi_\theta]$
- $L^{VF}$:值函数的MSE损失
- $S[\pi_\theta]$:策略的熵(鼓励探索)
- 多次小步更新(K epochs)
- 用同一批采样数据,在每个 mini-batch 上优化 K 轮。
- 更新旧策略参数
- $\theta_{old} \leftarrow \theta$
- 采样(Rollout)
- 一、PPO算法核心思想
PPOTrainer中代码的流程总结 ^PPO-code-process
- 外循环:总训练batch数
- 对 policy model 生成的每一个序列样本,输入到reward model ,序列中最后一个token的 hidden state 被输入到一个线性头,最终输出一个标量值,表示序列二分类的打分score。
- 对 policy model 生成的每一个序列样本,每一个token输出前的vocabulary 分布和 ref_model的分布 两者计算kl散度(使用了近似方法使得计算加速),即输出一个 kl散度序列,序列中每个元素表示对应位置token的kl散度。
- 将序列级的score 加到kl序列中每一个位置,结果即为 rewards 序列
- 依次计算广义优势估计 gae,
GAE(t)=rewards(t) + gamma*value(t+1) - value(t) + gamma*lam*GAE(t+1),这里lam是指lambda- 计算 return
return(t) = GAE(t)+ value(t)。(这里把value理解成是 t步的状态state下,未来奖励的总和期望,即 状态本身的价值)。- value即 state value function,GAE即 针对的是action。
- 内循环:针对上述步骤生成的序列样本和对应的return、GAE、概率logp 序列,将样本拆分成 micro batch,执行下列步骤
- value 优化的损失函数为
(return - value(t))^2,其实就是约束 优势advantage GAE 本身尽量。- 使用当前的policy model 计算action 的新概率值 $\large \log p_{\theta}$
- policy gradient损失为 $\Large - GAE * exp(\log p_{\theta}-\log p_{\theta_{old}})$ 我想这里这么写是为了数值稳定性,因为一般是让模型输出 logp,然后计算 概率比,那么就直接相减然后取指数。背后其实就是 logsumexp算子。
- 两个损失加载一起进行反向传播,即会修改当前的policy_model和value_model
- 输出和保存各种模型
local_rollout_forward_batch_size 是每个节点,在本地进行多批次的训练,每个批次的大小即为 local_rollout_forward_batch_size。
policy_model和ref_model在rollout中调用的方法不同 3. policy_model使用trl.trainer.utils.batch_generation(是对所有计算节点的并行批量化计算)。就是在query之后拼接预测出来的response。 1. batch_generation 使用generation_config参数,代码中规定 max_new_tokens=args.response_length,即硬性指定了response长度。 2. response在生成出来之后,会合并所有的立马进行右padding。 3. 其中调用了GenerationMixin的generate方法,该方法的输出为token ids的序列。 4. ref_model 使用 forward。就是将整个query_response拼接结果全部输入到ref_model,一次性得出错位的预测结果。所以对logits索引的时候会往前错一位,并且最后一个位置是不需要使用的。 1. 我想batch_generation是需要进行复杂padding的,从而可能导致每一个小batch生成的输出长度是不一致的。但我想不到为什么这么调用的理由。 5. 总体上PPO有两个地方牵涉到概率的对比 1. 第一个是 policy_model和ref_model两个模型的KL散度,放置policy_model跑太远过于离谱。 2. 第二个是 policy_model和old_policy_model的样本概率对比,但不需要保留old_policy_old这个模型,因为训练中仅仅是使用了 old_policy_model的样本和其样本概率,所以通过old_policy_model一次采样出来一大堆数据之后(其中保留了样本概率 $\Large p_{theta_{old}}$), old_policy_model就可以丢弃,仅仅通过使用采样的样本和样本概率来进行policy_model的训练优化。 那么就相当于是两个模型合二为一。
class Qwen2ForSequenceClassification(Qwen2PreTrainedModel): The Qwen2 Model transformer with a sequence classification head on top (linear layer).
[Qwen2ForSequenceClassification] uses the last token in order to do the classification, as other causal models (e.g. GPT-2) do. 计算的时候会找到 response中的 last_non_pad_token,输出对应的logitis,然后经过score方法 进行linear_unit 计算。
class AlbertForSequenceClassification(AlbertPreTrainedModel): Albert Model transformer with a sequence classification/regression head on top (a linear layer on top of the pooled output) e.g. for GLUE tasks.
dataclass注解是 Python 3.7 及以上引入的一个装饰器,作用是简化类的编写,让类自动获得一些常用方法(如__init__,__repr__,__eq__等),用于表示数据结构。
为什么 计算ref_logprob 使用 selective_log_softmax方法,按理说KL散度应该是对vocabulary的所有词进行KL散度计算啊? 理论上,KL散度的定义是: $$K L \left(\right. p \parallel q \left.\right) = \underset{i}{\sum} p \left(\right. i \left.\right) log \frac{p \left(\right. i \left.\right)}{q \left(\right. i \left.\right)}$$ 这里 (i) 是整个 vocabulary 的所有 token。 实际工程实现(RLHF/PPO场景): 6. 我们通常只关心模型实际“走出的路径”,即生成的 token 序列。 7. PPO/Reward Modeling 里,KL项是用来约束新模型(policy)不要偏离旧模型(reference/policy)的行为,只需要对“已采样的 token”上的概率分布做约束。
rewards[[actual_start, actual_end]] += scores赋值操作,rewards中存放的是kl散度,这里则是将kl散度对应最后一个token位置向右错一位的位置加上一个最终的reward score。 这里的actual_start是样本的index,actual_end是指每一个样本的最后一个token位置+1。
在 Hugging Face Transformers 中,类名前的
Auto表示自动选择模型的意思。 8.AutoModelForSequenceClassification不是一个具体的模型类,而是一个工厂类。 9. 它可以根据你加载的 checkpoint(如"bert-base-uncased"、"roberta-base"、自定义路径等),自动实例化对应的具体模型类(如BertForSequenceClassification、RobertaForSequenceClassification等)。 10. 这让你不需要关心底层是哪个模型,只要传入模型名或路径,它会自动帮你选择正确的模型实现。
PPO中 value_model一般使用的是 sequence_classification model,比如 示例脚本 中代码如下,value_model和reward_model使用的是同一个模型类的不同实例。
value_model = AutoModelForSequenceClassification.from_pretrained(
training_args.reward_model_path, trust_remote_code=model_args.trust_remote_code, num_labels=1
)
reward_model = AutoModelForSequenceClassification.from_pretrained(
training_args.reward_model_path, trust_remote_code=model_args.trust_remote_code, num_labels=1
)
这里举例 transformers.models.qwen2.modeling_qwen2.Qwen2ForSequenceClassification
PPOTrainer中调用get_reward的时候,使用的是 sequence_classfication_model的 base_model_prefix指向的底层LLM原始模型 、 score方法(score方法就是在baseLLM模型后添加一个线性层,以映射到logits)。其效果就是对一对问答字符串序列输出一串reward数值。
get_reward方法中的具体逻辑。 lm_backbone底层使用的是基模型,其中 output_hidden_states 表示要把模型中所有层的hidden_states全部进行输出。所以在调用score的时候会只取最后一层的hidden_states,然后输入到score方法中(即再经过一层线性层得到logits)。效果就是对于问答字符串序列中的每一个token都会得到一个reward_logits数值。
output = lm_backbone(
input_ids=input_ids,
attention_mask=attention_mask,
position_ids=position_ids,
return_dict=True,
output_hidden_states=True,
use_cache=False, # otherwise mistral-based RM would error out
)
reward_logits = model.score(output.hidden_states[-1])
# 这里的output.hidden_states 表示base_model的所有层的输出
# output.hidden_states[-1] 则表示最后一层的输出
# Qwen2ForSequenceClassification 的forward中计算 loss,就是按照最后一个token的最后一层输出 + 真实label 一起计算出交叉熵
return (
reward_logits,
reward_logits[
torch.arange(reward_logits.size(0), device=reward_logits.device),
sequence_lengths,
].squeeze(-1),
sequence_lengths,
)
# 这里表示返回 response序列中最后一个合法token的logits输出
transformers.models.qwen2.modeling_qwen2.Qwen2ForSequenceClassification 是一个例子模型,该模型的forward方法中计算了每一个token输出的分类的logits,然后仅仅获取了每一个序列的最后一个 non_padding_token的logits作为输出。
reward_model得到的一个序列一个reward值,但其实是最后一步的immediate reward。 value_model得到的每一个动作(即token)一个value值(即长期效果的评估的指标)。
PPOTrainer中 为什么需要有 missing_eos_penalty?
在 PPOTrainer 中存在 missing_eos_penalty,主要是为了解决 生成结果没有包含终止符(如 eos_token_id)的情况,防止模型生成不完整或异常的响应。
- 在文本生成任务中,模型通常会在响应结尾生成一个“终止符”(例如
eos_token_id),表示响应结束。 - 如果模型没有生成终止符,响应可能:
- 超长(一直生成到最大长度)
- 不完整(缺少语法上的结尾)
- 影响后续评估和训练(如奖励模型、PPO等)
- 如果响应没有终止符,可能是模型没学会“何时结束”,这种响应一般是不符合任务预期的,需要惩罚。
missing_eos_penalty就是对这种情况加一个负分,鼓励模型在合适的时候生成终止符。
PPOTrainer中为什么要whiten_rewards 在
PPOTrainer中,存在一个参数whiten_rewards,其作用是对奖励(reward)进行归一化/标准化(whitening)。 奖励的尺度和分布直接影响优势的分布,而优势分布又影响梯度更新的稳定性和训练速度。 如果奖励很大或很小,会导致策略梯度很大/很小,影响收敛速度,甚至导致梯度爆炸/消失。
那么在采样阶段计算的reward、 value等数据,因为在训练阶段这些数据中的很多会重新生成,所以采样阶段很多计算是不是浪费了?
- 采样阶段的 value/logprob只用一次,训练阶段会重新算“新策略”的 value/logprob。
for t in reversed(range(gen_length)):
nextvalues = values[:, t + 1] if t < gen_length - 1 else 0.0
delta = rewards[:, t] + args.gamma * nextvalues - values[:, t]
lastgaelam = delta + args.gamma * args.lam * lastgaelam
advantages_reversed.append(lastgaelam)
advantages = torch.stack(advantages_reversed[::-1], axis=1)
returns = advantages + values
advantages = masked_whiten(advantages, ~padding_mask)
advantages = torch.masked_fill(advantages, padding_mask, 0)
这里计算return和advantage。return就是 state value的一个真实采样,用来计算 value_loss,即return和估计的value之间的平方损失。advantage 就是 优势,即当前时刻以及每个之后的时刻所计算的advantage的打折之和,用来给策略梯度加权。
损失函数
logprobs_diff = new_logprobs - mb_logprobs
ratio = torch.exp(logprobs_diff)
pg_losses = -mb_advantage * ratio
pg_losses2 = -mb_advantage * torch.clamp(ratio, 1.0 - args.cliprange, 1.0 + args.cliprange)
pg_loss_max = torch.max(pg_losses, pg_losses2)
pg_loss = masked_mean(pg_loss_max, ~padding_mask[micro_batch_inds])
loss = pg_loss + args.vf_coef * vf_loss
这块代码中为什么没有使用 logp的导数,原因是 $\Large \mathbb{E}{\beta} \left[ \frac{\pi{\theta}(a|s)}{\beta(a|s)} Q^{\pi}(s,a)\nabla_{\theta} \ln \pi_{\theta}(a|s) \right]$ ,其实就是 $\Large \mathbb{E}{\beta} \left[ \frac{\nabla{\theta} \pi_{\theta}(a|s)}{\beta(a|s)} Q^{\pi}(s,a) \right]$,那么对应的损失函数就是以$\large \theta$为优化参数的 $\Large - \mathbb{E}{\beta} \left[ \frac{\pi{\theta}(a|s)}{\beta(a|s)} Q^{\pi}(s,a) \right]$。
mb_logprobs 在loss中不会反向传播梯度么
因为 mb_logprobs计算的时候是在 with torch.no_grad(): 中。
vf_losses1 = torch.square(vpred - mb_return) 这里的mb_return 就是后续多步累积的reward值。
为什么policy loss 和 value function loss 要加起来进行optimize?
- 如果你设计了分离的 policy model 和 value model(即 Actor 和 Critic 完全分离),那么确实可以分开 optimize、分开 backward。
- 但最主流的实现(比如 Huggingface Transformers 的 PPOTrainer)是合一模型,一个模型里有两个输出 head,参数是共享的,所以必须把 loss 合在一起,统一 backward 和 optimize。
- 优化器(optimizer)会对所有参数做梯度更新,policy head 和 value head的梯度会分别回传到主干和各自 head。
在train 方法内,ref_policy 是自始至终 一致保持不变的。ref_policy 只是避免 policy_model 跑得太远。而样本重用是使用importantce weight来解决的,即 $\large \mathbb{E}{\beta} \left[ \frac{ \pi{\theta}(a|s)}{\beta(a|s)} \right]$ 。
这个PPO算法的实现,为什么既有kl散度计算作为 reward,同时又有clip操作
- clip objective:控制单次更新不要太大,防止训练不稳定。
- KL 散度奖励:保证整体策略不会逐渐偏离参考模型(human-preference-aligned policy)太远。
- KL 散度相当于在 奖励层面惩罚策略远离参考模型,避免模型跑偏;这就是所谓的“KL 奖励塑形”。
- clip 解决 短期训练稳定性;
- KL 奖励解决 长期偏移问题,相当于给模型加了个“牵引绳”。
你看到的实现里,KL 散度不是直接当约束用,而是作为奖励项参与回报计算;而 clip 则在优化目标里约束策略更新。这两者是互补关系。
DPO
TRPO中的PO和 DPO中的PO指的不是一个东西?
- TRPO:它的目标是最大化环境奖励。TRPO (Trust Region Policy Optimization)
- DPO:它的目标是最大化人类偏好。 DPO (Direct Preference Optimization)
DPO的直觉化理解
- 整体的目标函数为 $$\large L(\theta) = \max_{\pi_\theta} \mathbb{E}{x \sim D, y \sim \pi\theta} [r(x, y)] - \beta \text{KL}[\pi_\theta(y|x), \pi_{\theta_{\text{old}}}(y|x)]$$,函数表达的意义是,在一个奖励结构上,policy的结构必须尽量与奖励结构保持一致,但同时不要偏离老的policy太远,后者可以认为是一定程度的正则化。
- 形象化理解,对于 y变量的概率分布和奖励分布(即横轴是y 的各种取值,纵轴是对应y值行为的概率值大小、奖励大小),那么用图形理解就是两条曲线(只不过一个是归一化的正值,一个是可能正负值均存在)。对于上述目标函数来说,暂且认为奖励分布$r(x,y)$就是真实的训练数据值(但其实是从 正负例response的 contrastive loss拟合出来的值),那么$L(\theta)$中的唯一变量就是 策略函数 $\pi_{\theta}$ ,这个时候目标函数$L(\theta)$其实就是一个以 $\pi_\theta$为自变量的一个函数(当然自变量本身就是概率分布函数,那么这其实就是一个泛函优化问题);对上述目标函数进行变换,可以得出 $L(\theta)$取得最大值对应的自变量 $\pi_\theta$ 是有一个固定公式的,即 $$\large \pi_r(y|x) = \pi^*(y|x) = \frac{1}{Z(x)}\pi_{\theta_{\text{old}}}(y|x) \exp\left(\frac{1}{\beta}r(x,y)\right)$$。
- 这里可以极简的方法推导处理,如下 $$L =\mathbb{E}[r]-KL = \sum_{p} p*(r-\log(\frac{p}{q})) = \sum_{p} p*(\log(e^r)-\log(\frac{p}{q}))=\sum_{p} p*(\log(\frac{e^rq}{p})) = - \sum_{p} p(\log(\frac{e^rq}{p})) = - KL(p, e^rq)$$,而最后一个式子就是KL散度的公式取负数,KL散度公式存在最小值,那么$L$就存在最大值,最优点即为 $\large p = e^r*q$,最后公式外围套个归一化就是最终的推理结果。
- 直觉化的理解就是 策略函数 -> 和奖励结构对应的最优策略函数 -> 得到最优的整体目标值。而奖励结构必须与真实的正负样例偏好结构一致,所以整体上就是 奖励结构和策略函数是绑死的有固定函数关系,而奖励结构通过损失函数与真实正负样例偏好对齐,那么反向来说就是 真实正负样例偏好->指导奖励结构计算 ->指导对应的策略函数, 而对应策略函数其实本来就应该达到一种极端策略(即使得奖励最大的response的概率直接拉到最大值1),但因为正则化的存在使得 策略函数是介于 老策略函数和 极端策略 之间的中间策略。
- 假如没有正则化,那么整个过程就是极端策略,让正样例的生成概率无限大,负样例的生成概率无限小,也就是对应的logits之差无限大,那么正样例的概率直接为1,负样例的概率直接为0,但这样其实就是过拟合。所以需要正则化来限制,那么就是通过 用概率的logits来表示 $r(x,y)$,同时假定损失结构是 让 $r(x,y)$ 和 KL散度直接相加,来作为最终的目标函数。
直观上理解DPO的公式,即让正偏好的对应的策略action概率越大好,让负偏好对应的策略action概率越小越好。 $$\large L(\theta)=-\mathbb{E}{(x,y^+,y^-) \sim \mathcal{D}}\left[\log \sigma\left(\beta \log \frac{\pi{\theta}(y^+|x)}{\pi_{\theta_{old}}(y^+|x)}-\beta \log \frac{\pi_{\theta}(y^-|x)}{\pi_{\theta_{old}}(y^-|x)}\right)\right].$$ 其中$\large \hat{r}\theta(x,y) = \beta \log \left( \frac{\pi\theta(y|x)}{\pi_{\theta_{old}}(y|x)} \right)$ 意思是,如果当前策略对应的预估奖励是多少。
直觉上我将DPO比作是一个三个铁杆上分别套着一个环,三个铁环之间 有两个绳子连接,传统的policy gradient是将通过拉最下面一个环,让上面两个间接连带着移动。而DPO是将上面两个环固化成一体,只要移动最下面一个,就能达到直接移动最上面铁环的目的。
DPO算法的训练标准数据,都是配对的。
DPO算法中损失函数中的logp指的是不是整条episode中答案的概率log,即在给定prompt的情况下,给出response的每个token概率的乘积,也就是logp的和。
GRPO
- DeepSeekMath 论文的解读 [[2025Q3-论文学习日记#2025-07-16 DeepSeekMath]]
- GRPO 中使用了process supervision 信号,即单个reponse的多步推理,每一步都有标注的reward。然后 advantage 就是 归一化后的reward 在t步之后的累计和。即基于同一个prompt的所有response的reward的均值和方差 进行标准化 $\Large \tilde{r}i^{index(j)} = \frac{r_i^{index(j)} - \text{mean}(\mathbf{R})}{\text{std}(\mathbf{R})}$ ,然后累计和 $\Large \hat{A}{i,t} = \sum_{index(j) \geq t} \tilde{r}_i^{index(j)}$ ,这里t就是每一个token,j是每一个reasoning step,即每一个token其reward就是该token之后所有对应于 reasoning step end token的token的奖励之和。我想这里之所以没有使用$\gamma$ 可能是因为reasoning step数量本来都是极其有限的,并且采样应该会控制其数量。
- 所以GRPO是需要使用 process reward model 对response的每一个推理步骤进行打分的。
The training data of the reward model is based on the rule judgment. Data Source: question in SFT dataset with outputs sampled from SFT model. Reward Function: Rule (whether the answer is correct or not) 论文里的公式里有很多讲究。
原始论文中的公式19
$\large \mathcal{J}{\text{GRPO}}(\theta) = \mathbb{E}{\mathbf{q} \sim P_{\text{sft}}(Q), {o_i}{i=1}^G \sim \pi{\theta_{old}}(O|q)} \large \frac{1}{G} \sum_{i=1}^G \frac{1}{|o_i|} \sum_{t=1}^{|o_i|} \left[ \frac{\pi_\theta(o_{i,t}|q, o_{i,<t})}{\pi_{\theta_{old}}(o_{i,t}|q, o_{i,<t})} \hat{A}{i,t} - \beta \left( \frac{\pi{ref}(o_{i,t}|q, o_{i,<t})}{\pi_\theta(o_{i,t}|q, o_{i,<t})} - \log \frac{\pi_{ref}(o_{i,t}|q, o_{i,<t})}{\pi_\theta(o_{i,t}|q, o_{i,<t})} - 1 \right) \right].$
$\large w_{i,t}(\theta) = \frac{\pi_\theta(y_{i,t}|x, y_{i,<t})}{\pi_{\theta_{\text{old}}}(y_{i,t}|x, y_{i,<t})}$
GSPO
GSPO Group Sequence Policy Optimization
$\large J_{\text{GSPO}}(\theta) = \mathbb{E}{x \sim 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)\hat{A}_i, \text{clip}(s_i(\theta), 1-\epsilon, 1+\epsilon)\hat{A}_i\right) \right]$
$\large \hat{A}i = \frac{r(x, y_i) - \text{mean}({r(x, y_j)}{j=1}^G)}{\text{std}({r(x, y_j)}_{j=1}^G)}$
$\large 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)$
GSPO和GRPO强化学习算法的区别
| GRPO | GSPO | |
|---|---|---|
| 训练数据 | process supervision | outcome supervision |
| 重要性采样 | 基于每一个token | 基于整个序列,所有token重要性的的几何平均 |