持续更新:https://www.big-yellow-j.top/posts/2025/08/29/QwenVLCode.html
所谓模板化模型的输入,很容易理解(通过把对话 messages 转成模型能理解的 prompt,不过值得注意的是不同模型可能处理的方式不同),就是将我的内容“填充”到模板中模拟对话内容,比如说上面处理得到的一个简单结果就是:
一般在data_loader里面就会提前将我们的模型需要的输入处理好,比如说我们定义如下的模板
对于上面内容输出理解,首先 一般是一组“发言”的开始和结束标记,而后里面内容就是我们的文本/图像内容,/ / 则是分别代表:用户、模型、角色(告诉模型今天扮什么角色)。:表示图像输入的占位符,告诉模型这里有一段视觉信息。:图像实际的 embedding 会在这里替换(填充),不是文字,而是图像编码后的向量。值得注意的是 后面的内容就是 模型需要输出的文本内容。上面过程很容易理解,只不过需要注意如下问题,因为QwenVL2.5对于分辨率是存在处理(一般直接通过处理,后续有介绍),因此如果涉及到目标识别,可能需要提前将坐标进行转换避免分辨率不同导致bbox对应不上的问题
编码模板输入就比较简单,因为我的输入都是文本/图片,此过程就是需要将这些内容转化为编码形式(比如tokenizer处理等),处理方式如下:
- 1、process_vision_info:返回我的图像/视频输出(都存储在list中)
上面3个小的子函数表示:计算factor倍数、向上取整计算倍数、向下取整计算倍数,对于smart_resize(去实现动态分辨率)函数:通过四舍五入的方式,重新设置图片的 h 和 w 值,确保它们可以被28整除,这样一来就得到了图像的需要修改的尺寸了,比如说:
输入: 一张 1000×500 的图像
计算基础尺寸:round(1000/28)=36, round(500/28)=18 → 1008×504
检查像素数:1008504 = 508,032 > MAX_PIXELS(200,704)
计算缩放系数:beta = sqrt(1000500/) ≈ 1.58
最终尺寸:floor(1000/1.58)=632, floor(500/1.58)=316 → 616×308(28的倍数)
视频处理过程(fetch_video)对于视频处理和图像处理相类似打开–>改变尺寸。只不过在打开过程中QwenLV2.5处理过程千问 Qwen 教程为:
对于设计了3中不同范式:1、_read_video_decord;2、_read_video_torchvision;3、_read_video_torchcodec。
对于其中的 函数处理过程也很简单(直接去计算视频开始、结束、总共多少帧),而后类似动态分辨率(smart_resize中成立相类似的)对于视频会通过智能视频帧数计算算法(smart_nframes),用于确定从视频中提取多少帧作为模型输入,处理过程为:第一种直接通过来得到帧数;第二种处理方式为(FPS_MIN_FRAMES = 4、FRAME_FACTOR = 2、FPS_MAX_FRAMES = 768、FPS = 2.0):
- 2、processor:去将图片/文本进行编码
对于上面处理过程中,首先对于 主要是对图像进行一些预处理:1、do_resize:改变图片大小(直接通过进行处理)2、do_rescale:像素缩减到0-1之间;3、do_normalize:对图片进行归一化处理(通道维度);而后直接对于预处理后的图像直接进行切割处理为不同的patch输入到Vit中。
回顾一下QwenVL2.5的图片处理过程:首先是去对图片进行改变尺寸(保证图片最后可以整除patch_size)/缩放/归一化。而后就是直接将图片处理为vit能够处理的“序列输入”得到的维度为:。
补充一:图片输入具体例子说明
假设默认参数为:patch_size= 14, temporal_patch_size= 2, merge_size= 2
图像输入为(通过process_vision_info提前处理之后的维度):(1092, 1568)
首先计算 得到 812 1176
首先计算:grid_t=1,grit_h=812//14=58,grid_w=1176//14=84那么计算得到为 4872另外一项为 1176也就是最后图像处理得到的输出为:
补充二:对于 smart_resize快速估算最后大小:
先 round 到 factor 的倍数
如果超出 max_pixels → 除以 sqrt(HW/max_pixels),floor → factor 倍数
如果小于 min_pixels → 乘以 sqrt(min_pixels/HW),ceil → factor 倍数
其实也就是:首先将图像处理到为factor倍数的分辨率,而后去判断和max_pixels和min_pixels之间大小,大于前者就缩小,小于前者就放大
最后通过一系列编码之后得到输出:
整体模型输入处理,输入模型也就是上面编码模板输入几个部分,只不过主要就是如下几个处理:首先是模型处理输入 以及我的图像 ( 代码),而后将输入进行位置编码处理(代码),最后输出模型结果(代码),对于QwenVL2.5完整模型结构:
- 首先:对于视觉部分处理()
对于视觉模型主要需要处理的就是 ,假设输入的 信息为:,image_grid_thw为: [1, 84, 58](就是对应grid_t、grid_h、grid_w这三个数值)
总结上述图像处理过程:对于任意输入图像首先通过smart_resize(首先将图像改变到 factor的倍数,然后去判断和min_pixels和max_pixels之间大小,然后进行扩大,缩小)进行处理保证都可以整除patch_size(14)然后丢到 中进行处理主要是对图像归一化、正则化、改变维度(还会通过smart_resize在处理一次),处理之后再去确定他的 (对于这3个参数确定:直接通过 第二次smart_resize处理之后的结果除 patch_size即可)也就是tokens数量,而后将图像内容通过 conv3d处理得到:,最后就是计算window_attention(首先确定widow_size索引,通过索引进行切分,最后计算注意力)
补充:对于window-attention可以用卷积的思路去理解,比如说我得到“图像”: 我提前计算我的“卷积核”大小()为了保证我的 “图像”可以被卷积核处理就需要做一部分填充,而后用这个“卷积核”去划分成不同“小块”在到这个小块里面计算注意力。
所有的代码:https://github.com/shangxiaaabb/Docparse-QwenVL
补充一:节约显存可以进行的操作
1、使用:
2、使用 进行优化
3、使用 而不是
4、使用 (),不过需要注意的是 QwenVL2.5不支持使用 (除此之外安装也比较复杂)
5、避免显存碎片(不要过度的去评估模型),可以使用 去适当的减小缓存压力,对于不需要的内容(中间值)直接通过 处理掉
https://www.f22labs.com/blogs/complete-guide-to-fine-tuning-qwen2-5-vl-model/
SFT数据处理过程
首先假设数据(通过jsonl进行存储)输入格式为:
然后就只需要将参数丢到这个函数里面就可以自动化的将数据处理好(补充一点,对于上面几个参数,一般来说其中就是我的模型需要输出的label,而后其他的内容就是模型的输入),其次就只需要将输入进行编码即可也就是说直接通过:
SFT模型处理
一般来说如果直接使用lora去对模型进行微调,处理也比较简答:
这样一来模型就会被lora“包裹”,微调过程也就是优化lora的参数,不过如果需要使用(lora量化版本)再模型加载过程中需要使用参数 :
对于模型训练以及参数优化过程就比较简单:
强化学习框架很多,1、huggingface-trl: https://github.com/huggingface/trl;2、字节跳动-verl: https://github.com/volcengine/verl;3、OpenRLHF:https://github.com/OpenRLHF/OpenRLHF
强化学习处理过程(直接使用 trl(使用版本:0.22.1)库,它里面提供了多种脚本)对于多模态/大语言模型使用RL中比较常见的的数据类型:一般就是抛出问题,而后给出选项让模型进行选择。此类数据集一般格式为:
初次之外,RL就和SFT一样需要让模型去按照我的数据进行输出,因此处理也就是直接得到模型最后输出(见相当于每个词的概率)
RL-DPO处理代码
首先在代码()主要是通过继承 (代码包裹好了各种处理过程比如数据加载模型评估等各项处理过程)直接看 里面的 (完整模型输入然后输出loss):
对于DPOTrainer里面data_loader处理过程为,首先对于 会通过 (一般来说也就是对于文本直接使用 tokenizer,亦或者直接使用 )进行处理,也就是说会提前将数据processor处理(和SFT处理方式相同)那么就会得到 ,那么接下来就是直接去通过代码(加载train_loader数据),其中处理方式为: 对于 里面处理过程为:直接去通过 model/ref_model去处理:(代码)得到模型输出: ,而后再去使用 去计算损失。
- 处理过程 Github-代码(实际解释使用 trl:0.22.1版本代码和github有差异)
Process-1:首先是将文本和回答进行拼接,而后去判断如果指定 那么就去根据 (掐头/去尾:保留序列末尾,移除开头多余部分)去裁减输入以及移除填充和限制计算范围来优化内存和性能最后丢到模型中进行处理。
回顾一下(模型处理)整个过程:首先是将 和 两部分进行拼接(做的,于此同时对于其他内容也都会拼接成两部分)作为我们模型的回答。而后丢到模型中进行处理(对于 可以直接给模型处理,如果不是那么就通过截断裁剪等处理来节约存储在由模型处理)得到 ,去通过logits, label得到每个token的对数概率:,而后再去判断是否进行优化策略: 或者 (长度去敏化)去优化得到的 (对其直接切分就可以得到: 和 )
- 计算损失过程 Github-代码(实际解释使用 trl:0.22.1版本代码和github有差异)
2、KL散度计算

3、JS散度计算

在计算得到不同方式得到的结果:logits然后再去根据不同 去做处理(比如说: 处理过程为:)
RL-DPO处理过程总结
首先对于我们的数据集(假设为3元组:[问题, 接受回答, 拒绝回答])首先就是去通过 (比如Qwen2.5vl可以直接 load)去编码我的所有内容(这一步和SFT过程相似),而后就是去通过这个函数将我们的3元组进行拼接得到:[问题,问题], [接受回答, 拒绝回答]而后得到模型的输入为:[问题+接受回答, 问题+拒绝回答],将输入直接交给的模型(由于见内容直接拼接起来,可能会优化模型的输入/出长度过长导致爆显存,因此输入之前会由一些裁剪处理操作)去得到输出:,而后通过logits, label得到每个token的对数概率:,(通过对进行拆分)就可以得到接受回答的值(),以及拒绝回答的值(),最后在得到这两部分值之后就是直接去计算loss。
对于loss计算过程(假设为KL散度):。对于里面两项相减过程代码:
反思:如果需要手搓一个DPO训练过程代码(需要借鉴代码来辅助实现)
RL-GRPO处理代码
官方实现代码,对于DPO过程很容易发现一点在GRPO中直接不要 只是用一个model不过设计了一个。
- 数据处理过程
不过在得到类似上面数据集之后,不是直接丢到模型里面进行处理,在DPOTrainer中首先会去由(代码)函数进行处理,对于测试直接通过函数 处理,对于训练数据集
- 奖励函数设计
GRPO没有使用ref_model转而使用奖励函数,对于奖励函数设计:, 。对于很容易理解代码就是直接对比模型输出和答案之间是否正确(通过 [] 去解析最后输出打答案然后对比两者之间是否正确)。对于:这个更加直接,直接去判断输出是不是有 包裹(有=1,无/缺失=0)
当然不一定要使用自定义的(这么粗糙的)在DPOTrainer中对于(代码)也可以直接去加载训练好的模型
- 模型处理过程
直接去看loss计算过程:
其中使用了两种loss处理过程: 以及 。
- 处理过程(Github-代码)(实际解释使用 trl:0.22.1版本代码和github有差异)
首先是将输入问题和回答拼接起来,然后直接丢到(直接将数据丢到模型中,而后去截取模型输出中“真正回答”的内容)中进行处理得到(每个token的概率),(每个token的信息熵),而后就是通过高熵去过滤token只在高熵位置计算 loss,而后就是计算KL散度(),避免新策略漂移太远
处理过程(Github-代码)(实际解释使用 trl:0.22.1版本代码和github有差异)
其处理过程比较简单,直接将所有的数据都处理成模型输入(GRPO不想DPO那样需要将3元组进行拆开拼接)如:input_ids、pixel_values等然后直接在得到模型的输出之后后续就是对输出做一些截断处理(如只需要模型回答部分的输出)而后去计算 (通过温度系数来确定输出内容多样化)最后再去通过:(selective_log_softmax只去计算completion_ids部分的log_softmax值)就可以得到最后的值。
RL-GRPO处理过程总结

对于上面loss计算公式中主要就是如下几个值需要关注:1、advantage值;2、KL散度值。
因此简单总结一些GRPO代码处理过程,首先,对于数据处理,这块内容比较简单直接 模板化、编码内容即可,因为GRPO是“一个问题抛出多组回答然后评估回答”,因此在数据处理过程中通过模型生成回答 而后需要做的就是将生成内容进行拆分得到和 (得到这一部分值之后就只需要在去还原成text文本然后再去通过reward函数去计算reward值以及计算最后需要的 值),除此之外还会去通过model和model_ref分别计算回答中每个token的logits值: 和
这个过程直接通过函数 _get_per_token_logps_and_entropies处理,他的处理思路简单直接将 model需要的内容再丢到model里面得到每个token的logits然后再去计算softmax值
最后得到一个完整的output如下:
而后,对于loss计算过程首先将上面output中的 问题+回答进行组合再丢到中得到每个token概率以及熵的值:,,而后就是:1、选择出高熵值的token();2、计算KL散度();3、重要性采样权重:比较当前 log 概率和旧策略(),得到 importance weight,做 clipping 限制。构造两个候选 loss(不裁剪和裁剪),取最小值,形成 再去乘上 entropy_mask和加上 KL 惩罚项就可以得到最后的loss值。
RL-PPO处理代码
回顾一下,对于加载的llm在使用generate时一般返回如下4个值:
:生成的 token ids(跟默认返回一样);
:每一步的 logits(如果 output_scores=True)
:注意力矩阵(如果 output_attentions=True)
:隐藏层表示(如果 output_hidden_states=True)
一般而言使用到的主要是上面两项,对于第一项一般得到的完整的回答(prompt+模型生成的内容),所以一般会有一个截取处理(只需要记录然后去截取即可);对于第二项一般得到的是通常是logits(需要去通过softmax计算才能得到token概率);因此在GRPO和PPO中为了得到每一个token的log概率值,直接通过这种方式去计算来节约显存。
除此之外也有直接通过 这样处理一般得到的是
RL-PPO处理过程总结
第一阶段:首先是对于问题()通过丢到模型中处理得到(完整问题+模型回答) 和(每个token对应的概率),进一步将其得到回答token的概率值()同样的处理过程通过policy_model将(从 挑选的)输入到模型进行处理同样的处理得到,最后就是通过去计算()得到奖励值。
第二阶段:kl值:直接计算(也就是计算上面阶段的ref_logprob和 logprob之间差值);rewards值:直接copy计算的kl结果然后再序列的结尾补充上scores;advantage值:根据 reward 和 value,用 GAE 算 advantage。GAE计算过程: 和最后计算也就是
第三阶段:进行迭代优化模型参数过程,优化过程首先是直接将小批次的 输入到模型中计算得到然后就是老操纵得到每个token的logits值,然后计算去计算:计算loss1()和loss2()的最大值。:计算loss1()和loss2()的最大值然后取mean。最后得到loss为
vpred、vpredclipped、mb_return分别通过从vpred_temp选择回答token、对vpred进行clamp裁剪、advantages + values
对比一下GRPO和DPO的处理过程
DPO纯数据驱动过程,数据驱动:训练时需要标注好的偏好对:。计算流程:1. 输入同一个问题 ,分别拼接上正样本回答 和负样本回答 。2. 用当前模型和参考模型分别计算 。3. 基于这 4 个 log-prob,直接计算一个 logistic 回归式的 loss,强制模型在正样本上比分数更高,在负样本上比分数更低。
GRPO生成驱动过程,生成驱动:训练时只给定问题 prompt,模型自己 roll-out 多个回答。计算流程:1. 对每个问题生成 个回答。2. 通过奖励函数(或打分器)给每个回答打分 。3. 组内归一化奖励 → 得到 advantage 值 (比组内平均好/差多少)。4. 用参考模型计算 ref_per_token_logps(使用ref_model生成没有的话直接用model代替ref_model)。5. 用旧策略(冻结一帧的当前模型)得到 old_per_token_logps(直接通过model生成)。6. 用当前模型得到 per_token_logps。7. 计算重要性比率和 KL 散度(使用per_token_logps和ref_per_token_logps计算)近似,再套 PPO 风格的剪切目标(使用old_per_token_logps和per_token_logp) → 最终 loss。
对于DPO、GRPO、PPO中KL计算差异
1、DPO中计算KL:在model_ref以及model分别输入“3元组”数据之后会去计算不同token的概率值,也就是model和ref都会生成 reject和choose的概率值,然后去计算: 的sigmoid 损失优化相对偏好
2、GRPO中计算KL:通过model_ref对于问题Q以及模型生成的多组回答进而可以得到每组回答的token概率: 而后我又通过model去生成多组回答以及token概率:接下来就是直接他们之间KL散度:

3、PPO中计算KL:通过model得到回答中的每一个token的概率,同样的再去通过model_rf也去计算每一个token的概率然后去计算KL

DPO:通过“偏好差值”间接引入 KL 约束,偏重于 对比学习。
GRPO:显式计算 生成候选组的 token 级 KL,作为正则项,保证模型不偏离参考策略。
PPO:基于当前策略与参考策略(或旧策略)的 KL,常作为 正则或 early stopping 信号
对于GRPO以及PPO中优势值计算过程
GRPO优势值计算过程:对于给出多组回答直接通过奖励函数去计算每组回答的奖励值而后去上计算:
PPO优势值计算过程:一般直接通过广义优势估计方法GAE来计算优势值,首先通过奖励函数评估模型输出(问题+回答),而后计算GAE
对比DPO、GRPO、PPO中loss计算差异
DPO的loss计算:
GRPO的loss计算:
PPO的loss计算:
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/257155.html原文链接:https://javaforall.net
