SK²Decompile 强化学习实战:基于 VERL/GRPO 的双阶段二进制反编译 RL 训练完整指南
人工智能大模型逆向工程微调代码模型【免费下载链接】LLM4DecompileReverse Engineering: Decompiling Binary Code with Large Language Models项目地址https://gitcode.com/GitHub_Trending/ll/LLM4Decompile点击查看免费下载SK²Decompile 是 LLM4Decompile 项目推出的两阶段二进制反编译方案Skeleton→Skin即先恢复结构骨架、再恢复标识符语义。本文以仓库内 RL 训练文档 为骨架结合 reward_functions 与 scripts 的参考实现源码完整讲解如何在 VERL 框架上复现 SK²Decompile 的 GRPO 强化学习管线。读完本文你将掌握两阶段奖励函数编译反馈 占位符 Jaccard、嵌入余弦相似度的数学设计与代码对应关系、奖励函数接入 VERL 的路由注册方法、Parquet 训练数据格式、两个参考训练脚本的逐参数含义以及 OOM、嵌入服务连接失败、编译超时三类典型问题的排查方法。背景SFT 之后为什么要用强化学习再对齐SK²Decompile 的训练管线分为三个阶段数据预处理伪代码规范化、两阶段监督微调SFT以及本文重点的强化学习RL。在 SFT 阶段两个模型分别学习Skeleton 模型Structure Recovery将二进制/伪代码转换为规范化中间表示IRSkin 模型Identifier Naming将 IR 还原为带语义标识符的可读源码。SFT 的目标是模仿数据集而 RL 的目标是直接优化任务特定目标。SK²Decompile 在 SFT 之后对每个阶段模型应用GRPOGroup Relative Policy Optimization算法DeepSeek-AI et al., 2025并用各自的奖励信号做对齐Structure RecoverySkeleton奖励基于编译器反馈——生成的 IR 只有成功编译才给予正向奖励并叠加占位符恢复正确性分量论文 Eq. 3Identifier NamingSkin奖励为生成代码与参考源码的嵌入余弦相似度鼓励语义对齐的标识符预测而非精确词法匹配论文 Eq. 4。仓库中提供的奖励函数与训练脚本是参考实现用于复现 RL 训练管线精确的奖励公式与设计动机请参阅论文第 3.5 节arXiv:2509.22114。目录结构与模块职责sk2decompile/verl/SK2DECOMPILE/ ├── README.md # RL 训练文档本文依据 ├── data/ │ └── sk2decompile-rl-examples.jsonl # RL 训练数据示例 ├── reward_functions/ # 参考奖励实现 │ ├── __init__.py │ ├── exe_type.py # 编译可行性 占位符 Jaccard │ ├── sim_exe.py # 编译可行性 词级相似度 │ ├── embedding_gte.py # 嵌入标识符相似度GTE │ └── embedding_qwen3.py # 嵌入标识符相似度Qwen3 └── scripts/ ├── run_struct_rl.sh # Structure Recovery RL 参考脚本 └── run_ident_rl.sh # Identifier Naming RL 参考脚本其中 reward_functions/init.py 将四个奖励函数统一导出为exe_type、sim_exe、embedding_gte、embedding_qwen3并明确说明接入 VERL 的方式把文件复制到verl/utils/reward_score/后在路由分发器中注册分支详见下文 Step 2。奖励公式与参考实现逐行解读Structure Recovery 奖励Eq. 3编译可行性 占位符恢复结构恢复奖励由两部分组成可编译性Compilability用 ground-truth 头文件编译生成的 IR仅在编译成功时给予 1.0 奖励头文件生成由 Psyche-C 验证占位符恢复Placeholder Recovery生成占位符集合 I_gen 与真实集合 I_IR 之间的 Jaccard 相似度r_placeholder |I_gen ∩ I_IR| / |I_gen ∪ I_IR| r_structure { 0.0, if IR 无法编译 { 1.0 r_placeholder, if IR 可编译仓库参考实现 exe_type.py 与该公式严格对应核心逻辑如下def compute_score(solution_str, ground_truth, extra_infoNone): type_score_value, _ type_score(solution_str, ground_truth, extra_info) compileable_score_value compileable_score(solution_str, ground_truth, extra_info) if compileable_score_value 0.0: return 0.0 return type_score_value compileable_score_valuetype_score通过四个正则模式r\bfunc\w*\b、r\btype\w*\b、r\bvar\w*\b、r\bfield\w*\b分别提取候选代码与 ground truth 中的占位符标识符集合再计算 Jaccard 相似度。这里对应的是规范 IR 中func*/type*/var*/field*前缀的通用占位符体系——这是 SK²Decompile 语言无关中间表示 的关键设计。compileable_score在临时目录中把extra_info携带的 C 头文件声明与候选代码拼接写入temp.c调用subprocess.run([gcc, -c, source_file, -o, object_file], timeout5, checkTrue)做gcc -c只编译不链接检查返回 1.0/0.0。注意两点每个样本 5 秒超时且异常如语法错误、死循环导致超时一律返回 0.0。extra_info中的header字段C 头文件声明是可编译性检查的前提这正是训练数据中必须携带该字段的原因。可选变体词级 Jaccard 编译可行性sim_exesim_exe.py 是结构恢复奖励的替代参考实现先计算候选与 ground truth 的词级 Jaccard 相似度对字符串lower().split()后取集合交并比再叠加编译分数def compute_score(solution_str, ground_truth, extra_infoNone): sim_score jaccard_similarity(solution_str, ground_truth) compile_score compileable_score(solution_str, ground_truth, extra_info) if sim_score 0.5: return sim_score compile_score return 0与exe_type的差异在于它以 0.5 为词级相似度阈值低于阈值直接给 0 分避免奖励被低质量输出污染。compileable_score与exe_type.py中完全相同同为 5 秒超时的gcc -c检查。Identifier Naming 奖励Eq. 4嵌入余弦相似度标识符命名奖励用嵌入向量之间的余弦相似度衡量生成代码与参考源码的语义相似度r_identifier cos(e_gen, e_src) (e_gen · e_src) / (||e_gen|| · ||e_src||)其中e_gen、e_src分别为生成代码与参考代码的嵌入。论文实验中使用的嵌入模型是 qwen-embedding-0.6BZhang et al., 2025。仓库提供了两个参考实现embedding_qwen3.py默认模型Qwen3-Embedding-0.6B与 embedding_gte.py默认模型gte-large-en-v1.5两者代码结构完全一致流程为tree-sitter 解析 C 代码并分类提取标识符_classify_node将节点分为四类——type_identifier→type、field_identifier→field、函数声明符/调用表达式中的identifier→func、声明类节点中的identifier→var每类最多 64 个构建命名摘要串_build_summary_text生成形如func: foo bar || type: my_type || field: field1 field2 || var: i j k的摘要单次 API 调用同时嵌入两个摘要_embed_two通过 OpenAI 兼容客户端一次请求返回两段文本的向量返回余弦相似度的平方compute_score返回name_score * name_score文档注释说明这是为了锐化奖励信号放大高低分差异。环境变量配置默认值见源码 docstring环境变量默认值说明QWEN3_EMBEDDING_MODEL_PATH/GTE_EMBEDDING_MODEL_PATHQwen3-Embedding-0.6B/gte-large-en-v1.5嵌入模型名/路径QWEN3_EMBEDDING_API_KEY/GTE_EMBEDDING_API_KEY或OPENAI_API_KEYnoneAPI KeyQWEN3_EMBEDDING_API_BASE/GTE_EMBEDDING_API_BASEhttp://127.0.0.1:8000/v1OpenAI 兼容 API 地址注意reward_functions/中的实现是演示奖励设计的参考代码完整的公式与设计动机请以论文 Section 3.5 为准。复现指南五步跑通 RL 训练Step 1安装 VERLRL 训练基于VERL v0.4.1HybridFlowSheng et al., 2024。为保证可复现性建议使用相同版本git clone VERL 仓库地址 cd verl git checkout v0.4.1 # 或最接近 v0.4.1 的 commit pip install -e .Step 2集成奖励函数到 VERL将四个奖励函数复制进 VERL 的奖励模块并在路由分发器中注册# 复制奖励函数 cp reward_functions/exe_type.py VERL_DIR/verl/utils/reward_score/sk2d_exe_type.py cp reward_functions/sim_exe.py VERL_DIR/verl/utils/reward_score/sk2d_sim_exe.py cp reward_functions/embedding_gte.py VERL_DIR/verl/utils/reward_score/sk2d_embedding_gte.py cp reward_functions/embedding_qwen3.py VERL_DIR/verl/utils/reward_score/sk2d_embedding_qwen3.py然后在VERL_DIR/verl/utils/reward_score/__init__.py的default_compute_score()函数中添加路由分支# Structure Recovery 奖励示例 elif data_source sk2decompile_structure: from . import sk2d_exe_type res sk2d_exe_type.compute_score(solution_str, ground_truth, extra_info) # Identifier Naming 奖励示例 elif data_source sk2decompile_identifier: from . import sk2d_embedding_qwen3 res sk2d_embedding_qwen3.compute_score(solution_str, ground_truth, extra_info)训练 Parquet 文件中的data_source字段决定了每个样本被分发到哪个奖励函数。从源码可见compute_score的统一签名是(solution_str, ground_truth, extra_infoNone)extra_info透传header、index、split等附加信息——这一统一接口正是让四个变体能够即插即用的基础。Step 3准备训练数据训练数据为 Parquet 格式每行包含以下字段字段说明promptChat 格式消息例如[{role: user, content: pseudocode... What is the source code?}]data_source奖励函数路由键必须与 Step 2 注册的分支匹配reward_model.ground_truth期望输出Structure Recovery 为 IRIdentifier Naming 为源码reward_model.stylerule基于规则的奖励extra_info.headerC 头文件声明用于可编译性检查仅 Structure Recovery仓库中的 data/sk2decompile-rl-examples.jsonl 提供了具体格式示例。观察示例数据可以发现两个值得注意的细节每条记录的data_source为llm4decompile_exe与 README 中的示例分支名sk2decompile_structure/sk2decompile_identifier不同——实际训练时需要把该字段改成与你在 Step 2 注册的分支一致的键这再次印证了data_source即路由键的设计extra_info中还包含index样本序号与splittrain/valid字段可用于数据溯源多数样本携带详细的headertypedef、struct 定义、函数声明而少数样本如 index 87208 的示例header为null——这类样本无法走编译检查类奖励。训练前需要把 JSONL 转换为 Parquet 格式。Step 4启动训练参考训练脚本位于 scripts/启动前先编辑脚本顶部的配置变量。Structure Recovery RL# 编辑 scripts/run_struct_rl.sh 设置 # VERL_DIR, VENV_PATH, MODEL_PATH, TRAIN_DATA, VAL_DATA, WANDB_* bash scripts/run_struct_rl.shIdentifier Naming RL需要运行中的嵌入服务# 1. 启动嵌入服务 python -m vllm.entrypoints.openai.api_server \ --model Qwen3-Embedding-0.6B --port 8000 --dtype float16 # 2. 编辑 scripts/run_ident_rl.sh 设置 # VERL_DIR, VENV_PATH, MODEL_PATH, TRAIN_DATA, VAL_DATA, WANDB_* bash scripts/run_ident_rl.sh从脚本源码可以补充两个关键开关run_struct_rl.sh顶部用REWARD_VARIANTexe_type可选exe_type或sim_exe选择结构恢复奖励变体run_ident_rl.sh用EMBEDDING_VARIANTgte可选gte或qwen3选择嵌入模型。两个脚本均基于 VERL 的python3 -m verl.trainer.main_ppo --config-pathconfig --config-nameppo_trainer-lm4dc.yaml启动 GRPO 训练并通过algorithm.adv_estimatorgrpo显式指定优势估计器。脚本中值得关注的训练运行参数以run_struct_rl.sh为例data.filter_overlong_promptsTrue、data.truncationerror对超长 prompt 做过滤与截断错误处理actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu4默认微批大小OOM 时可调小actor_rollout_ref.actor.use_kl_lossTrue与kl_loss_coef开启 KL 惩罚约束策略偏离actor_rollout_ref.rollout.namevllm与rollout.gpu_memory_utilization0.80使用 vLLM 做 rollout、显存占用上限 0.80actor_rollout_ref.rollout.n16GRPO 每组采样 16 条actor_rollout_ref.ref.fsdp_config.param_offloadTrue参考模型参数卸载到 CPU 以省显存trainer.default_local_dir${VERL_DIR}/checkpoints/${TASK_NAME}checkpoint 输出目录脚本会通过tee同时把标准输出/错误写入${VERL_DIR}/logs/${TASK_NAME}.log与.err文件NUM_NODES与GPUS_PER_NODE默认不同结构恢复脚本默认 2 节点 × 8 卡标识符命名脚本默认 1 节点 × 8 卡请按实际硬件调整。Step 5安装附加依赖# 编译类奖励Structure Recovery apt install gcc pip install psychec # 或从 Psyche-C 仓库源码构建 # 嵌入类奖励Identifier Naming pip install tree-sitter0.24.0 tree-sitter-c0.23.4 openai注意tree-sitter-c0.23.4与tree-sitter0.24.0的版本组合是嵌入类奖励源码中Language(tsc.language())正常工作所要求的固定搭配不建议随意升级。参考超参数配置训练脚本中使用的参考超参数如下参数Structure RecoveryIdentifier Namingtrain_batch_size128128max_prompt_length10241024max_response_length20482048lr1e-61e-6kl_loss_coef0.010.02kl_loss_typelow_var_kllow_var_klrollout.nGRPO 采样数1616total_epochs22两者的主要差异在于kl_loss_coefIdentifier Naming 使用 0.02略高于 Structure Recovery 的 0.01。脚本中还可以看到actor_rollout_ref.actor.entropy_coeff0不显式加熵奖励与algorithm.use_kl_in_rewardFalseKL 不进奖励、单独作为损失项。这些配置共同构成了 GRPO 训练的标准设置实际复现时可根据硬件与数据规模调整。常见问题排查OOM显存不足调小ppo_micro_batch_size_per_gpu默认 4开启actor.fsdp_config.param_offloadTrue调低rollout.gpu_memory_utilization默认 0.80。脚本源码中与显存相关的默认值还包括actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu1rollout log-prob 微批为 1与actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu4以及默认关闭的enable_gradient_checkpointing——如果 OOM 仍然严重可以尝试开启梯度检查点。嵌入服务连接错误仅 Identifier Naming确认 vLLM 嵌入服务运行在 8000 端口检查环境变量QWEN3_EMBEDDING_API_BASE默认http://127.0.0.1:8000/v1GTE 变体则检查GTE_EMBEDDING_API_BASE。奖励中编译超时仅 Structure Recoverygcc -c每个样本有 5 秒超时源码中timeout5的直接体现若大量样本超时检查生成代码是否包含死循环。结合仓库快速对照如果你想基于本文档做源码级深挖建议按以下顺序阅读仓库文件奖励实现reward_functions/exe_type.py、sim_exe.py、embedding_qwen3.py、embedding_gte.py训练脚本scripts/run_struct_rl.sh、scripts/run_ident_rl.sh训练数据格式data/sk2decompile-rl-examples.jsonl上游完整管线SK²Decompile 的 SFT 与数据预处理见 sk2decompile/README.md其中 RL 章节同样指向本文档RL 训练数据在sk2decompile/verl/SK2DECOMPILE/data/下。理解清楚奖励设计Eq. 3/Eq. 4→ 参考实现4 个 reward 模块→ 路由注册data_source→ GRPO 训练2 个脚本→ 排错这条链路后你就能基于本仓库在自己的数据集与硬件上复现 SK²Decompile 的强化学习训练并根据任务特点替换奖励变体或调整超参数。赞分享人工智能大模型逆向工程微调代码模型【免费下载链接】LLM4DecompileReverse Engineering: Decompiling Binary Code with Large Language Models项目地址https://gitcode.com/GitHub_Trending/ll/LLM4Decompile点击查看免费下载相关推荐SK²DecompileLLM 双阶段二进制反编译框架Skeleton to Skin完整训练与评测指南SK²DecompileLLM 双阶段二进制反编译框架Skeleton to Skin完整训练与评测指南 SK²Decompile 是 LLM4Decom人工智能大模型逆向工程微调代码模型使用 verl 对 Qwen3 进行强化学习RL训练GRPO/PPO 全流程实战指南使用 verl 对 Qwen3 进行强化学习RL训练GRPO/PPO 全流程实战指南 verl 是火山引擎开源的 LLM 强化学习RL训练库也是论文人工智能大模型Qwen模型评测示例工程本地部署教程verl 强化学习训练实战指南基于 HybridFlow 的 PPO/GRPO 大模型后训练verl 强化学习训练实战指南基于 HybridFlow 的 PPO/GRPO 大模型后训练 verlVolcano Engine ReinforcemenAI 技能人工智能大模型深度学习上一篇Home Assistant OSO Energy 集成使用 osoenergy.turn_off 关闭热水器加热下一篇Rerun 可视化 ControlNet 图像生成全流程逐扩散时间步记录 Canny 边缘条件扩散过程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →