尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

VLM奖励模型结构感知微调:从原理到LoRA实战

VLM奖励模型结构感知微调:从原理到LoRA实战 在视觉语言模型VLM的训练链路中奖励模型Reward Model的优劣直接决定强化学习阶段的上限。用一句话概括奖励模型是 VLM 的“老师”老师评分不准学生学得再多也容易跑偏。最近在项目里踩了不少坑也沉淀了一些经验尤其是“结构感知”这个方向对奖励模型效果提升非常明显。本文从概念、原理到完整微调代码把这条链路拆开来讲适合正在做多模态 RLHF、偏好对齐或者视觉 Agents 的后端/算法工程师参考。1. VLM 奖励模型与结构感知微调先搞清楚在做什么1.1 什么是 VLM 奖励模型VLMVision-Language Model是同时处理图像和文本输入的多模态大模型。它既能看到图片的内容也能理解用户的问题并生成回答。典型任务包括图片描述、文档问答、图表理解、GUI 操作识别等。在基于人类反馈的强化学习RLHF中奖励模型承担“打分者”的角色。它接收模型生成的回答预测人类偏好该回答的概率。强化学习阶段以这个得分作为奖励信号优化主模型Policy Model的输出策略。举一个例子VLM 收到一张产品使用说明书截图用户问“第三步操作是按下哪个按钮”Policy Model 生成了候选回答 A 和回答 B。Reward Model 分别对 A 和 B 打分得分高的回答会被强化学习算法强化。如果 Reward Model 的打分和人类偏好不一致强化学习阶段会把模型推向错误方向最终得到的模型即使损失很低也不符合真实需求。1.2 为什么普通奖励模型不够用在多模态场景下奖励模型的输入远比纯文本复杂。很多团队直接把文本奖励模型改一版或者只用 CLIP 特征拼接文本特征结果出现两类典型问题视觉信息弱化。模型过度关注文本流畅度忽略图像中关键区域比如按钮位置、表格行列对应关系、流程图方向。结构关系缺失。文档、网页、表格、UI 界面都有明确空间结构。HTML 有 DOM 结构表格有行列关系UI 有控件层级。普通奖励模型把这些结构摊平成 token 序列丢失了空间和层级信息。一个用户偏好评分的典型场景模型回答把图表上的数值描述对了但把某列数据和某行标签的对应关系搞反。人类一眼能看出错误但普通奖励模型可能因为整体语句通顺而给出高分。这就是结构感知不足导致的问题。1.3 结构感知微调解决什么问题结构感知Structure-Aware微调指的是在奖励模型训练过程中显式引入输入数据的结构信息。它不是简单加一个注意力头而是从数据构造、模型架构到损失函数让模型学习“对象之间的位置关系、层级关系、从属关系”。本文涉及的结构信息包括三类文本结构Markdown 标题层级、列表嵌套、表格行列语义。视觉布局结构物体检测框坐标、OCR 词框坐标、版面分析区域框。跨模态对齐结构某个文本片段对应图像中的哪个区域某个表格表头对应哪些单元格。通过结构感知微调奖励模型不仅能判断“这段话好不好”还能判断“这段话是否准确引用了图中的结构和位置信息”。1.4 本文的适用范围与掌握目标本文适合以下读者正在做多模态 RLHF 数据管道的算法工程师。需要对现有 VLM 奖励模型进行迭代优化的研究员。研究偏好对齐、多模态安全对齐的学生。学完本文你将掌握结构感知训练数据的构造思路。使用 LoRA 对 VLM 奖励模型进行结构感知微调的完整流程。在有限显存下训练和评估奖励模型的实操方案。训练过程中常见问题的排查方法。2. 环境准备与实验设计2.1 硬件环境奖励模型微调的显存需求取决于模型规模和训练方式7B 到 8B 模型全参数微调需要 80GB 以上显存如 A100/H100使用 LoRA 后 24GB 到 40GB 显存可跑。2B 到 4B 模型LoRA 微调在 16GB 到 24GB 显存可跑。1B 以下模型消费级显卡也能尝试。如果你使用的是 24GB 显存的显卡例如 RTX 3090/4090建议优先选择 2B 到 4B 的底座模型并配合 LoRA。本文示例重点演示训练思路实际模型规模请根据你的显存情况调整。2.2 软件环境以下版本是我们验证过的组合但不是唯一选择Python 3.10 或 3.11PyTorch 2.1 或更高版本CUDA 11.8 或 12.1Transformers 4.40 或更高版本PEFT 0.10 或更高版本TRL 0.8 或更高版本datasets、accelerate、bitsandbytes安装命令pip install torch2.1.2 torchvision0.16.2 --index-url https://download.pytorch.org/whl/cu121 pip install transformers4.40.0 peft0.10.0 trl0.8.6 accelerate0.29.0 datasets2.18.0 bitsandbytes0.43.1需要特别提醒Transformers 版本对 VLM 的处理器Processor影响较大不同版本之间 API 可能有微调。如果遇到image_size或patch_size相关的报错优先检查版本匹配。2.3 实验数据设计的整体思路结构感知微调的训练数据在普通偏好对的基础上增加了结构标注层。一条完整的训练样本包含四部分输入图像。用户指令文本。被评分的模型回答chosen / rejected 为一对。结构信息标注JSON 形式。结构信息标注的粒度取决于任务文档问答类提供版面分析区域框标注文本块属于“标题”“正文”“表格”还是“页脚”。图表问答类提供图表区域和关键数值坐标。UI 操作类提供控件检测框、控件类型、可交互状态。网页理解类提供 DOM 结构简化树标注文本节点对应的视觉坐标。实验建议从单一任务切入不要一开始就混合多任务否则难以定位效果变化来自哪一部分结构信息。2.4 训练策略选型完整微调Full Fine-Tuning和参数高效微调PEFT差别很大Full Fine-Tuning效果好但是显存需求高且容易破坏底座模型的多模态基础能力需要谨慎设置学习率。LoRA只训练低秩分解矩阵显存占用小训练速度快适合在有限资源下验证结构感知方法是否有效。QLoRA在 LoRA 基础上对底座模型做 4-bit 量化进一步降低显存但训练速度会变慢。对于结构感知奖励模型的首次实验推荐使用 LoRA。原因很简单你的核心目标是验证“结构信息是否有效”而不是“全参微调能不能刷分”。LoRA 能快速迭代之后的工程化再考虑全参或更大规模。3. 结构感知微调的核心原理拆解3.1 奖励模型的训练目标奖励模型的本质是“偏好分类器”。给定同一个输入图像用户指令两个候选回答分别记为 chosen人类更喜欢的和 rejected人类不太喜欢的。奖励模型为两个回答打分训练目标通常是让 chosen 的分数高于 rejected 的分数。常用的损失函数是 Bradley-Terry 模型。对一对样本奖励模型输出分数 ( r_{chosen} ) 和 ( r_{rejected} )损失为[ \mathcal{L} -\log \sigma(r_{chosen} - r_{rejected}) ]其中 ( \sigma ) 是 sigmoid 函数。这个损失的本质是最大化 chosen 分数高于 rejected 分数的概率。结构感知微调并没有替换这个损失而是在输入侧、特征侧和训练策略上增加结构约束使模型在打分时学会参考结构信息。3.2 结构信息注入的三种层次在实现结构感知微调时可以把结构信息注入分成三个层次由浅入深。第一层文本提示注入。把结构信息序列化为文本拼接到输入中。比如在用户指令后面加上结构化描述[Structure Info] 1. 文档区域 0 Introduction 类型标题 坐标(0.12, 0.08, 0.85, 0.15) 2. 文档区域 1 System Architecture 类型图片 坐标(0.10, 0.30, 0.90, 0.55) 3. 文档区域 2 Model Details 类型正文 坐标(0.12, 0.60, 0.88, 0.85)这种方法最简单但容易造成 token 过长而且文本表达空间结构的能力有限。第二层视觉特征拼接。借助检测模型或版面分析模型将图像中关键对象的位置坐标映射为视觉特征中的位置编码或 token与原始图像 token 一起输入。这种方式需要改造 VLM 内部的视觉编码器适配层实现难度中等。第三层跨模态对齐损失。在奖励模型的训练损失之外额外增加一个辅助损失要求模型的注意力权重在“提到某个区域时”能够覆盖对应视觉区域。这种方法训练成本更高但对结构关系的建模能力最强。本文示例以第一层为主配合部分第二层思路。第一层最容易落地而且能最快验证结构感知对奖励模型分数是否有效。3.3 结构数据增强提升鲁棒性的关键真实场景中的结构信息标注可能不完整或存在噪声奖励模型需要具备一定的抗干扰能力。数据增强是常用手段坐标扰动对标注框坐标加上不超过 2% 的随机扰动。区域遮挡随机遮挡文档中部分非核心区域并同步修改结构描述看模型是否过度依赖某些局部特征。结构信息缺失以一定概率比如 10%随机丢弃部分结构描述模拟真实环境中多模态理解模块输出不完整的情况。增强后的训练数据能有效降低奖励模型对训练集结构的过拟合提升在真实推理阶段的泛化能力。3.4 训练过程中的指标监控奖励模型训练时不能只看训练损失。建议额外监控三个指标Chosen 分数均值与 Rejected 分数均值两者差距逐渐拉大说明奖励模型开始区分偏好。准确率判断 chosen 分数大于 rejected 分数的比例。与人工评测的相关性每训练一小步取部分验证集样例计算模型排序和人工排序的 Spearman 相关系数。效果排序通常参考准确率但最终上线更应参考人工相关性。如果训练损失很低但人工相关性不提升需要警惕过拟合和奖励黑客Reward Hacking问题。4. 完整实战案例基于 LoRA 的结构感知奖励模型微调下面进入完整实战环节。我们用一个小规模示例演示整个流程使用 Qwen2-VL 系列模型作为底座实际环境请替换为你可获取的模型通过 LoRA 训练一个结构感知奖励模型。代码结构清晰可以按模块复制到你的工程中。4.1 项目结构建议按以下结构组织代码structure_aware_rm/ ├── data/ │ ├── train_data.jsonl │ └── val_data.jsonl ├── models/ │ └── checkpoints/ ├── scripts/ │ ├── train_rm.py │ ├── evaluate_rm.py │ └── inference_rm.py ├── configs/ │ └── lora_config.yaml └── requirements.txt这样划分的好处是数据、代码、配置、模型产物相互隔离便于实验管理和回溯。4.2 训练数据格式训练数据采用 JSONL 格式每行一个训练样本。示例数据如下{ id: sample_001, image_path: data/images/doc_001.png, prompt: 根据这张系统架构图API Gateway 和 Auth Service 之间是什么关系, chosen: API Gateway 接收外部请求后会调用 Auth Service 进行身份校验校验通过后继续路由到后端服务。从图中箭头方向看Auth Service 是 API Gateway 的下游依赖。, rejected: API Gateway 和 Auth Service 是并列关系它们都直接接收外部请求并独立处理。, structure_info: { layout: [ {region_id: 0, type: diagram, box: [0.1, 0.05, 0.9, 0.45], text: system architecture diagram}, {region_id: 1, type: text, box: [0.1, 0.5, 0.9, 0.9], text: architecture description} ], relations: [ {from_region: 0, to_region: 1, relation: visual_contain}, {from_region: 0, to_region: 0, relation: arrow_from_api_gateway_to_auth_service} ] } }这里chosen是人类更喜欢的回答rejected是较差回答。structure_info是结构描述包含版面区域和关系三元组。在构建数据集时需要注意“结构性正确但表述不流畅”和“表述流畅但结构错误”这两类样本都要保留这样才能让奖励模型区分“形式好”和“结构对”。4.3 加载模型与处理器第一步是加载奖励模型底座和处理图像/文本的 Processor。# 文件路径scripts/train_rm.py import torch from transformers import AutoModelForVision2Seq, AutoProcessor model_id Qwen/Qwen2-VL-2B-Instruct processor AutoProcessor.from_pretrained(model_id, trust_remote_codeTrue) model AutoModelForVision2Seq.from_pretrained( model_id, torch_dtypetorch.bfloat16, device_mapauto, trust_remote_codeTrue, ) # 奖励模型需要移除语言模型头部输出一个标量分数 # 不同 VLM 结构差异较大需要根据底座模型重写输出层 # 这里给出一个通用的包装思路 class RewardModel(torch.nn.Module): def __init__(self, base_model, hidden_size2048): super().__init__() self.base_model base_model self.score_head torch.nn.Sequential( torch.nn.Dropout(0.1), torch.nn.Linear(hidden_size, 1), ) def forward(self, pixel_values, input_ids, attention_mask, structure_embedsNone): outputs self.base_model( pixel_valuespixel_values, input_idsinput_ids, attention_maskattention_mask, output_hidden_statesTrue, ) # 取最后一个 token 的隐藏状态作为整体表示 last_hidden outputs.hidden_states[-1] # [batch, seq_len, hidden] last_token_hidden last_hidden[:, -1, :] # 取序列最后一个位置 score self.score_head(last_token_hidden) return score.squeeze(-1)代码说明AutoModelForVision2Seq是 Transformers 中处理视觉语言序列生成模型的入口。奖励模型不生成文本而是输出一个标量分数。取最后一个 token 的隐藏状态是一种简化策略实际工程中也可以使用mean_pooling或attention_pooling。如果你的底座模型隐藏层维度不是 2048需要根据model.config.hidden_size动态指定。注意上面代码是核心结构示例直接运行需要按你使用的底座模型调整细节。奖励模型输出层和普通生成模型的输出层不一样这不是 Transformers 默认支持的行为。4.4 构建结构感知输入为了让模型感知结构我们需要把structure_info序列化成文本并与用户指令拼接在一起。# 文件路径scripts/train_rm.py import json def format_structure_info(structure_info: dict) - str: layout_text [] for region in structure_info.get(layout, []): region_desc ( f区域{region[region_id]}类型{region[type]} f坐标[{region[box][0]:.2f},{region[box][1]:.2f}, f{region[box][2]:.2f},{region[box][3]:.2f}] f文本{region[text]} ) layout_text.append(region_desc) relation_text [] for rel in structure_info.get(relations, []): rel_desc ( f{rel[from_region]}-{rel[to_region]} f关系{rel[relation]} ) relation_text.append(rel_desc) structure_str [版面区域]\n \n.join(layout_text) \n[区域关系]\n \n.join(relation_text) return structure_str def build_model_input(processor, image, prompt, structure_info): structure_text format_structure_info(structure_info) full_prompt f{prompt}\n{structure_text} messages [ { role: user, content: [ {type: image}, {type: text, text: full_prompt}, ], } ] text_prompt processor.apply_chat_template(messages, add_generation_promptFalse) inputs processor( texttext_prompt, imagesimage, return_tensorspt, paddingTrue, ) return inputs, full_prompt需要注意的是VLM 的处理器对图像输入有固定尺寸要求。如果输入图像分辨率过高会被 resize 到设定尺寸标注框坐标需要同步缩放。建议在数据预处理阶段统一图像尺寸例如统一到 448x448 或 1024x1024然后把结构坐标归一化到 0-1 之间。4.5 LoRA 配置与训练参数本示例使用 PEFT 库进行 LoRA 配置。结构感知微调的 LoRA 不仅作用在语言层也需要作用在视觉编码器的部分投影层这取决于你的底座模型。# 文件路径scripts/train_rm.py from peft import LoraConfig, get_peft_model lora_config LoraConfig( r16, lora_alpha32, target_modules[q_proj, k_proj, v_proj, o_proj], lora_dropout0.05, biasnone, task_typeCAUSAL_LM, ) model get_peft_model(model, lora_config) for name, param in model.named_parameters(): if not any(layer_name in name for layer_name in [lora_, score_head]): param.requires_grad False这段代码做了两件事把 LoRA 适配器挂在模型的 q/k/v/o 投影层上。冻结非 LoRA 参数和原模型参数只训练 LoRA 参数和奖励头。task_typeCAUSAL_LM是 PEFT 库最常见的设置即使我们做的是奖励模型也建议使用这个类型因为它匹配大多数 VLM 的模型结构。4.6 训练数据加载与预处理创建一个 PyTorch Dataset 来管理训练样本。# 文件路径scripts/train_rm.py from torch.utils.data import Dataset from PIL import Image import json class RewardModelDataset(Dataset): def __init__(self, data_path, processor, image_dir): self.data [] with open(data_path, r, encodingutf-8) as f: for line in f: self.data.append(json.loads(line)) self.processor processor self.image_dir image_dir def __len__(self): return len(self.data) def __getitem__(self, idx): item self.data[idx] image_path f{self.image_dir}/{item[image_path]} image Image.open(image_path).convert(RGB) chosen_inputs, _ build_model_input( self.processor, image, item[prompt], item[structure_info] ) chosen_inputs[text_answer] item[chosen] rejected_inputs, _ build_model_input( self.processor, image, item[prompt], item[structure_info] ) rejected_inputs[text_answer] item[rejected] return { chosen: chosen_inputs, rejected: rejected_inputs, }在实际训练中chosen 和 rejected 是同一个 prompt 下的两个回答所以图像、prompt、结构信息都一样只有text_answer不同。注意这里文本编码时需要把答案拼接到 prompt 后面一起编码形成“prompt answer”的完整序列。4.7 训练循环主体奖励模型训练的损失函数是 Bradley-Terry 损失。训练循环的核心代码如下# 文件路径scripts/train_rm.py import torch from torch.utils.data import DataLoader from transformers import get_linear_schedule_with_warmup def collate_fn(batch): chosen_batch { pixel_values: torch.cat([x[chosen][pixel_values] for x in batch]), input_ids: torch.cat([x[chosen][input_ids] for x in batch]), attention_mask: torch.cat([x[chosen][attention_mask] for x in batch]), } rejected_batch { pixel_values: torch.cat([x[rejected][pixel_values] for x in batch]), input_ids: torch.cat([x[rejected][input_ids] for x in batch]), attention_mask: torch.cat([x[rejected][attention_mask] for x in batch]), } return { chosen: chosen_batch, rejected: rejected_batch, } def compute_bradley_terry_loss(model, chosen_inputs, rejected_inputs): chosen_scores model( pixel_valueschosen_inputs[pixel_values], input_idschosen_inputs[input_ids], attention_maskchosen_inputs[attention_mask], ) rejected_scores model( pixel_valuesrejected_inputs[pixel_values], input_idsrejected_inputs[input_ids], attention_maskrejected_inputs[attention_mask], ) logits chosen_scores - rejected_scores loss -torch.nn.functional.logsigmoid(logits).mean() return loss, chosen_scores, rejected_scores训练主流程如下# 文件路径scripts/train_rm.py def train(): dataset RewardModelDataset(data/train_data.jsonl, processor, image_dirdata/images) dataloader DataLoader(dataset, batch_size2, shuffleTrue, collate_fncollate_fn) optimizer torch.optim.AdamW(model.parameters(), lr1e-5, weight_decay0.01) total_steps len(dataloader) * 3 scheduler get_linear_schedule_with_warmup( optimizer, num_warmup_steps100, num_training_stepstotal_steps ) model.train() global_step 0 for epoch in range(3): for batch in dataloader: loss, chosen_scores, rejected_scores compute_bradley_terry_loss( model, batch[chosen], batch[rejected] ) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() scheduler.step() global_step 1 if global_step % 10 0: chosen_mean chosen_scores.mean().item() rejected_mean rejected_scores.mean().item() acc (chosen_scores rejected_scores).float().mean().item() print( fStep {global_step} | Loss {loss.item():.4f} | fChosen {chosen_mean:.4f} | Rejected {rejected_mean:.4f} | Acc {acc:.4f} )训练参数建议学习率奖励模型的训练比生成模型更敏感1e-5到3e-5比较安全。Batch Size受限于显存建议先设为 1 或 2配合梯度累积。梯度裁剪把梯度范数限制在 1.0避免训练不稳定。Epoch小数据量下 2-3 轮即可过多会过拟合。4.8 评估脚本训练结束后需要用独立验证集评估奖励模型。# 文件路径scripts/evaluate_rm.py def evaluate(model, processor, val_data_path, image_dir): model.eval() dataset RewardModelDataset(val_data_path, processor, image_dir) dataloader DataLoader(dataset, batch_size1, shuffleFalse, collate_fncollate_fn) correct 0 total 0 with torch.no_grad(): for batch in dataloader: loss, chosen_scores, rejected_scores compute_bradley_terry_loss( model, batch[chosen], batch[rejected] ) if chosen_scores rejected_scores: correct 1 total 1 accuracy correct / total print(fValidation Accuracy: {accuracy:.4f}) return accuracy除了准确率建议在验证集中混入“结构错误样本”把 chosen 和 rejected 故意写反或者把结构描述中的关键区域坐标故意弄错观察奖励模型能否识别出异常。如果模型对这类样本仍然给出高分说明结构感知能力没有真正学到。4.9 推理与打分训练完成后奖励模型的应用方式是“给一段输入打分”而不是生成回答。# 文件路径scripts/inference_rm.py def reward_inference(model, processor, image_path, prompt, structure_info, candidate_answer): image Image.open(image_path).convert(RGB) inputs, _ build_model_input(processor, image, prompt, structure_info) # 拼接候选回答 text_with_answer inputs[input_ids] # 实际实现时需把 candidate_answer 编码并拼接 with torch.no_grad(): score model( pixel_valuesinputs[pixel_values].to(model.device), input_idstext_with_answer.to(model.device), attention_maskinputs[attention_mask].to(model.device), ) return score.item()推理阶段奖励模型会对候选回答输出一个标量分数。分数越高表示该回答与人类偏好的匹配程度越高。在强化学习阶段这个分数会作为 reward 信号传给优化器。5. 常见问题与排查思路结构感知奖励模型训练过程中问题往往集中在显存、过拟合、训练不稳定和结构信息失效四个方面。下表列出常见问题。问题现象常见原因解决思路CUDA Out of Memory图像分辨率过高batch size 过大缩小图像尺寸使用梯度累积启用 bf16 或 4-bit 量化Loss 为 NaN学习率过高或 logits 数值不稳定降低学习率使用梯度裁剪检查是否有异常图像训练准确率 99% 但验证准确率不升过拟合记住了训练数据中的表面模式增加数据增强减少 epoch增加验证集多样性加上结构信息后效果反而变差结构编码方式与模型不匹配结构文本过长干扰原始语义简化结构文本使用更紧凑的坐标描述方式奖励分数整体漂移奖励头初始化过于随机使用更小的 reward head 初始化范围或先 warmup 几轮模型对“结构错误”的回答打高分结构感知未真正生效模型仍在用文本流畅度打分增加结构错误样本比例人工检查结构编码是否正确5.1 CUDA Out of Memory显存不足是最常见的问题。VLM 的视觉编码器会占用大量显存图像 token 数量往往是文本 token 的几十倍。如果使用 2B 模型batch size 1 也可能爆显存。排查步骤检查图像输入尺寸尝试降低到 224x224 或 336x336。开启 gradient checkpointing。使用 LoRA 并冻结底座模型参数。使用 bitsandbytes 做 4-bit 量化。model.gradient_checkpointing_enable() model.config.use_cache False5.2 结构信息过拟合结构信息过拟合有两种表现模型把坐标值背下来换个坐标就失效。模型对结构描述中的文本过度敏感忽略坐标关系。对策是增加结构扰动增强。训练时每次随机为坐标框加上 ±0.03 的噪声并以 15% 概率随机丢弃部分区域描述。这样可以强迫模型学习到“结构关系”而不是“固定坐标”。5.3 奖励黑客Reward Hacking在 RLHF 中奖励黑客指模型找到奖励函数的漏洞通过不合理的方式获得高分。在奖励模型训练阶段如果我们只使用 Bradley-Terry 损失模型可能倾向于给“更长更复杂的回答”打高分因为训练集中长回答占比高。预防手段控制 chosen 和 rejected 的长度分布尽量匹配。加入长度归一化项。在验证集中专门加入“短但正确”和“长但错误”的样本。6. 最佳实践与工程建议6.1 数据质量是第一优先级结构感知微调的效果上限由数据质量决定。结构标注错误、坐标偏移、关系三元组不完整都会直接污染训练信号。在实际项目中建议先对 500 条数据进行人工质检确认结构标注准确率超过 95% 再开始大规模标注。每条样本必须有原始图像可回溯方便排查模型错误打分时定位原因。定期分析模型打分与人工评分的差异把差异大的样本加入训练集做补充训练。6.2 分阶段训练从简单到复杂不建议一开始就训练多任务结构感知奖励模型。推荐分三个阶段第一阶段在纯文本奖励数据上训练让奖励头收敛。第二阶段加入图像输入但不加结构信息让模型学会视觉理解。第三阶段加入结构信息重点关注结构感知能力的提升。每阶段保存一个 checkpoint方便回退。6.3 关注结构信息缺失时的降级行为在真实生产环境版面分析模型、OCR 模型不是 100% 准确的。奖励模型必须在结构信息缺失或错误时仍然给出合理分数。工程建议在线推理时如果检测不到结构信息不要直接给最低分而应使用“无结构信息”模式打分。训练时保留 5%-10% 的样本不注入结构信息让模型学会在缺失结构时依赖视觉原始特征。不要强制模型在所有样本上都使用结构信息否则会造成过度依赖。6.4 安全与合规边界奖励模型在多模态场景中同样承担安全对齐职责。结构感知微调后需要特别验证以下边界模型是否会对包含敏感信息的图像区域给出不当高分。模型是否会被构造的“伪结构信息”欺骗比如把恶意文本包装成表格单元格。模型是否在无关区域被刻意标注后产生误导性高分。这些验证必须在测试环境完成不能在未验证的情况下直接用于线上强化学习。奖励模型是强化学习的引导信号一旦信号被污染整个模型的输出策略都会被带偏。6.5 可复现性管理训练奖励模型最怕实验结果不可复现。建议在工程中固定随机种子数据采样、模型初始化、训练顺序。数据版本每个训练集文件用 Git LFS 或 DVC 管理。代码版本训练脚本纳入代码仓库并记录 commit hash。模型版本记录底座模型 ID 和 LoRA checkpoint 路径。def set_seed(seed42): import random import numpy as np random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed)7. 收尾动手实验的建议结构感知微调不是一个高不可攀的方向它本质上是在“教奖励模型读图、读版式、读关系”。从本文示例出发你可以先用一个小数据集跑通流程把 LoRA 加上把结构信息加进去对比一下有结构和无结构的准确率差异。很多团队在第一次对比时发现结构感知让验证集准确率提升了 3 到 8 个百分点这个提升幅度已经足够说明问题。在继续深入之前有几个方向值得优先尝试在你的业务数据上重新设计结构信息字段不一定照搬坐标框和三元组可以先从“表格行列”“标题层级”这类显式结构开始。使用更大参数量比如 7B的底座模型观察结构感知带来的收益是否仍然存在。把结构感知奖励模型接入强化学习管线对比最终策略模型的真实表现差异。如果你在复现过程中遇到问题先从数据和显存两个维度排查。把训练日志、Loss 曲线、验证准确率三项放一起看通常能快速定位问题所在。希望这篇文章能给你一个可落地的起点。
返回列表