通过100步GRPO微调350M模型以获得更好的结构化输出
摘要
本指南详细介绍了使用TRL库中的Group Relative Policy Optimization(GRPO)进行微调的方案,以提升LFM2.5-350M模型的结构化输出合规性,将IFStruct基准性能从22.6%提高到29.7%。
查看缓存全文
缓存时间: 2026/09/03 11:49
使用GRPO在100步内微调350M模型以获得更优的结构化输出
来源:https://huggingface.co/blog/grpo-with-trl-ifstruct 本指南提供了一套完全公开且低成本的方案,旨在显著提升小型模型在结构化输出合规性方面的表现。我们将使用TRL库(https://huggingface.co/docs/trl/en/index)通过群组相对策略优化对LFM2.5-350M模型(https://huggingface.co/LiquidAI/LFM2.5-350M)进行微调,并在IFStruct基准测试(https://huggingface.co/datasets/LiquidAI/ifstruct-v1.0)上进行评估。整个流程仅需约500个样本和100个训练步骤,规模足够在免费版Colab或Kaggle GPU上运行,相关代码已发布在GitHub(https://github.com/Liquid4All/cookbook/blob/main/finetuning/notebooks/grpo_with_trl_ifstruct.ipynb)。结果表明,即使是轻量级的微调流程也能在IFStruct基准测试中将性能从22.6%提升至29.7%。
结构化输出是大语言模型最常见的实际任务之一,然而大多数基准测试将其纳入更广泛的推理或提取分数中,而非单独进行评估。模型能否以请求的格式和结构稳定返回有效、可解析的输出——即模式合规性——往往决定了它能否被接入下游系统。
请注意,本文描述的训练流程并非用于训练IFStruct博客(https://www.liquid.ai/blog/ifstruct-v1.0)中所述的强化学习模型。本笔记本旨在展示如何通过针对特定任务的微调来提升较小模型的性能,使其达到远大于其规模的模型的效果,而非复现IFStruct基准测试分数。
前提条件
本指南分为两部分,分别在不同环境中运行:
- 微调部分在GPU上运行。配套笔记本针对免费版Colab或Kaggle GPU设计。
- 评估部分可在MacBook本地运行(此处为配备Apple M5 Max芯片和36GB统一内存的MacBook Pro),通过
llama.cpp(一个提供OpenAI兼容服务器的工具)与IFStruct评估器通信。
我们需要使用uv(https://docs.astral.sh/uv/)进行Python工具管理,并使用llama.cpp提供服务。请参考Liquid AI的llama.cpp部署文档(https://docs.liquid.ai/deployment/on-device/llama-cpp),使用Homebrew安装llama.cpp并验证llama-server是否可用:
brew install llama.cpp
llama-server --version
在LFM2.5-350M基础模型上进行IFStruct评估
在开始之前,让我们先在IFStruct基准测试(https://huggingface.co/datasets/LiquidAI/ifstruct-v1.0)上评估LFM2.5-350M,看看能否复现其报告的21.1%分数。
IFStruct是一个用于测试大语言模型输出有效性及模式遵循度的基准测试。该基准测试在Liquid4All/ifstruct(https://github.com/Liquid4All/ifstruct)中开源,公共基准测试数据集可在Hugging Face的LiquidAI/ifstruct-v1.0(https://huggingface.co/datasets/LiquidAI/ifstruct-v1.0)上获取。
git clone https://github.com/Liquid4All/ifstruct.git
为进行评估对比,我们在MacBook上使用llama.cpp本地部署模型。我们将使用BF16格式的GGUF文件(LiquidAI/LFM2.5-350M-GGUF:https://huggingface.co/LiquidAI/LFM2.5-350M-GGUF)。
然后使用以下命令启动基础模型服务器:
llama-server \
-hf LiquidAI/LFM2.5-350M-GGUF:BF16 \
-c 32768 \
-np 4 \
-ngl 99 \
--alias LiquidAI/LFM2.5-350M \
--host 127.0.0.1 \
--port 8080
--alias:IFStruct发送到OpenAI兼容端点的模型名称-ngl 99:要求llama.cpp在可用时将所有层卸载到GPU-np 4:并行处理四个请求-c 32768:提示上下文大小
服务器启动后,我们可以运行包含2000个样本的完整基准测试:
uv run ifstruct-eval \
--model LiquidAI/LFM2.5-350M \
--base-url http://localhost:8080/v1 \
--api-key dummy \
--dataset data/test.jsonl \
--results-file results/lfm2.5-350m-llamacpp-base.json \
--n-threads 4 \
--max-tokens 2048 \
-v
============================================================
模型:LiquidAI/LFM2.5-350M
============================================================
总体结果:452/2000通过(22.6%)
平均延迟:1453毫秒
按格式:
JSON:180/1000通过(18.0%)
YAML:272/1000通过(27.2%)
按顶层结构:
包装键:288/1011通过(28.5%)
裸列表:164/989通过(16.6%)
按实体类型:
test__camera_review 6/83通过(7.2%)
test__clinical_trial 20/104通过(19.2%)
test__conference_schedule 7/87通过(8.0%)
test__escaping__bug_report_batch 24/89通过(27.0%)
test__escaping__config_snippet_audit 15/85通过(17.6%)
test__escaping__customer_email_thread 5/73通过(6.8%)
test__escaping__dialogue_sample 14/95通过(14.7%)
test__escaping__interview_transcript_segment 21/80通过(26.2%)
test__escaping__log_parser_examples 21/72通过(29.2%)
test__escaping__pr_discussion 22/87通过(25.3%)
test__escaping__repro_steps_batch 16/73通过(21.9%)
test__escaping__screenplay_scene 16/92通过(17.4%)
test__escaping__short_story_chapter 15/84通过(17.9%)
test__escaping__support_ticket_batch 27/73通过(37.0%)
test__escaping__terminal_session_notes 20/70通过(28.6%)
test__event_ticket_booking 49/107通过(45.8%)
test__gpu_review 6/94通过(6.4%)
test__invoice 28/86通过(32.6%)
test__job_posting 25/85通过(29.4%)
test__real_estate_listing 31/82通过(37.8%)
test__recipe 3/70通过(4.3%)
test__rental_car_booking 27/79通过(34.2%)
test__scientific_experiment 13/69通过(18.8%)
test__travel_itinerary 21/81通过(25.9%)
常见错误:
7228x 必填字段缺失
738x 项目数量错误
540x 类型不匹配
317x 未闭合代码块
190x 多余字段 'notes'
181x 多余字段 'path'
175x 多余字段 'constraints'
170x 多余字段 'type'
170x 缺失代码块
100x 期望裸列表,得到包装器
IFStruct发布博客中报告LFM2.5-350M的得分为21.1%(https://www.liquid.ai/blog/ifstruct-v1.0)。我们本地的llama.cpp/BF16设置测得22.6%,与IFStruct博客报告的21.1%接近。我们使用此本地结果作为相同服务栈下的基线。
使用TRL对结构化输出进行GRPO微调
完整可运行的流程位于配套笔记本(https://github.com/Liquid4All/cookbook/blob/main/finetuning/notebooks/grpo_with_trl_ifstruct.ipynb)中。本节仅涵盖相关部分。
训练数据
我们使用nvidia/Nemotron-RL-instruction_following-structured_outputs(https://huggingface.co/datasets/nvidia/Nemotron-RL-instruction_following-structured_outputs),其中每个提示都配有目标JSON模式和期望字段数量。我们使用约500个样本进行训练。
由于Nemotron数据分布与IFStruct评估存在差异,我们对提示进行增强以弥补两者之间的两个差距:
- **40%**的提示附加了“在带代码围栏的代码块中返回输出”指令,使模型学会遵循格式指令,而非总是直接输出原始JSON。
- 另外20%(与前述不重叠)被转换为顶层数组任务(模式被包裹在需要项目数量的
array中),用于训练裸列表输出和项目数量合规性。
模型与LoRA
我们加载LiquidAI/LFM2.5-350M并附加LoRA适配器。由于LFM2.5采用混合注意力/卷积架构,我们针对LFM特有的模块名称进行设置:
lora_config = LoraConfig(
r=16,
lora_alpha=32,
bias="none",
task_type="CAUSAL_LM",
target_modules=[
"q_proj", "k_proj", "v_proj", "out_proj", "in_proj",
"w1", "w2", "w3",
],
)
这将训练约600万参数,约占模型总参数的1.66%。
奖励函数
然后我们定义三个奖励函数,每个函数的评分范围为[0, 1],用于评估每个生成结果提取的结构是否正确:
json_format_reward:输出是否可解析,并符合请求的形式?符合请求形式(带围栏/原始)得满分(1.0),形式错误但可解析得0.2,不可解析输出得0.0。field_count_reward:对象是否具有期望的顶层字段数量?完全匹配得1.0,得分随差异程度线性衰减。schema_validation_reward:输出是否能通过该行的JSON模式验证?它计算每个约束违反情况,并在必填键覆盖率基础上控制部分得分。
我们将三者以reward_weights=[1.0, 0.5, 2.0]的权重相加组合。
训练
我们使用8个生成/提示组的配置进行100步训练,适用于免费版16GB GPU:
from trl import GRPOConfig
training_args = GRPOConfig(
output_dir="./outputs/lfm25-350m-nemotron-schema-grpo",
learning_rate=5e-5,
max_steps=100,
warmup_steps=10,
num_generations=8, # 每个提示组采样的完成数
per_device_train_batch_size=4,
gradient_accumulation_steps=8, # 每个优化器步骤处理4个提示组
steps_per_generation=2,
max_completion_length=1024, # 为嵌套JSON预留空间
mask_truncated_completions=False,
temperature=1.1, # 较热的采样使组内结果多样化
beta=0.01, # 相对于参考模型的KL惩罚
reward_weights=[1.0, 0.5, 2.0], # json_format, field_count, schema_validation
logging_steps=1,
save_steps=100,
)
如笔记本所示,在训练过程中,所有三个奖励分量都上升,相对于参考模型的KL散度在预热后从零开始上升,截断完成的比例保持接近零。
模型的合并与保存
最后,我们将LoRA适配器合并回基础权重,并将其保存为一个独立的检查点,准备转换为GGUF格式用于部署:
MERGED_DIR = f"{training_args.output_dir}-merged"
merged_model = trainer.model.merge_and_unload()
merged_model.save_pretrained(MERGED_DIR)
tokenizer.save_pretrained(MERGED_DIR)
在GRPO调优后的LFM2.5-350M上进行IFStruct评估
完成GRPO微调后,我们重新运行IFStruct评估。为此,我们需要将合并后的模型检查点转换为BF16 GGUF格式。转换器脚本包含在llama.cpp源码中,因此我们克隆仓库一次并安装转换器的gguf包。
git clone --depth 1 https://github.com/ggml-org/llama.cpp
pip install ./llama.cpp/gguf-py
mkdir -p models
python llama.cpp/convert_hf_to_gguf.py \
PATH_TO_YOUR_MERGED_MODEL \
--outfile ./models/lfm25-350m-grpo-bf16.gguf \
--outtype bf16
然后使用以下命令部署合并后的模型:
llama-server \
-m ./models/lfm25-350m-grpo-bf16.gguf \
--alias lfm25-350m-grpo-structured-output \
-c 32768 \
-np 4 \
-ngl 99 \
--host 127.0.0.1 \
--port 8081
接着,我们将使用微调后的模型再次运行完整的IFStruct评估:
uv run ifstruct-eval \
--model lfm25-350m-grpo-structured-output \
--base-url http://localhost:8081/v1 \
--api-key dummy \
--dataset data/test.jsonl \
--results-file results/lfm25-350m-grpo.json \
--n-threads 4 \
--max-tokens 2048 \
-v
============================================================
模型:lfm25-350m-grpo-structured-output
============================================================
总体结果:594/2000通过(29.7%)
平均延迟:1518毫秒
按格式:
JSON:319/1000通过(31.9%)
YAML:275/1000通过(27.5%)
按顶层结构:
包装键:300/1011通过(29.7%)
裸列表:294/989通过(29.7%)
按实体类型:
test__camera_review 5/83通过(6.0%)
test__clinical_trial 31/104通过(29.8%)
test__conference_schedule 11/87通过(12.6%)
test__escaping__bug_report_batch 32/89通过(36.0%)
test__escaping__config_snippet_audit 24/85通过(28.2%)
test__escaping__customer_email_thread 9/73通过(12.3%)
test__escaping__dialogue_sample 17/95通过(17.9%)
test__escaping__interview_transcript_segment 13/80通过(16.2%)
test__escaping__log_parser_examples 33/72通过(45.8%)
test__escaping__pr_discussion 26/87通过(29.9%)
test__escaping__repro_steps_batch 23/73通过(31.5%)
test__escaping__screenplay_scene 34/92通过(37.0%)
test__escaping__short_story_chapter 24/84通过(28.6%)
test__escaping__support_ticket_batch 36/73通过(49.3%)
test__escaping__terminal_session_notes 23/70通过(32.9%)
test__event_ticket_booking 62/107通过(57.9%)
test__gpu_review 7/94通过(7.4%)
test__invoice 36/86通过(41.9%)
test__job_posting 33/85通过(38.8%)
test__real_estate_listing 32/82通过(39.0%)
test__recipe 7/70通过(10.0%)
test__rental_car_booking 37/79通过(46.8%)
test__scientific_experiment 14/69通过(20.3%)
test__travel_itinerary 25/81通过(30.9%)
常见错误:
7331x 必填字段缺失
890x 项目数量错误
555x 类型不匹配
102x 期望裸列表,得到包装器
62x 多余字段 'metadata.tone'
55x 6大于最大值5
49x 多余字段 'speaker_labels'
47x 多余字段 'tone'
44x 'cups'不在允许值'mg', 'g', 'kg', 'oz', 'lb', 'ml', 'l', 'cl', 'dl'中
44x 多余字段 'notes'
在相同服务栈上比较两次运行结果:
| IFStruct分组 | 基础模型 | GRPO调优后 | 变化量(Δ) |
|---|---|---|---|
| 总体 | 22.6% | 29.7% | +7.1 |
| JSON | 18.0% | 31.9% | +13.9 |
| YAML | 27.2% | 27.5% | +0.3 |
| 包装键 | 28.5% | 29.7% | +1.2 |
| 裸列表 | 16.6% | 29.7% | +13.1 |
收益恰好落在训练目标上:JSON通过率提升了近14个百分点(18.0% → 31.9%),而YAML基本保持不变。虽然这仍低于Qwen3.5-2B的33.15%得分(https://www.liquid.ai/blog/ifstruct-v1.0),但它表明即使是轻量级的特定任务微调也能让小模型接近更大模型的表现。
结论
一个简短的GRPO训练流程,仅需约500个样本和100个步数,就能将350M参数的小模型在IFStruct上的得分从22.6%提升至29.7%。关键结论是,一个低成本、特定任务的奖励信号能够显著提升小型模型在形式方面的可靠性,大幅缩小与其数倍大小模型之间的差距。
要复现或扩展此工作,请参阅原始的IFStruct v1.0博客文章(https://www.liquid.ai/blog/ifstruct-v1.0)、Liquid4All/ifstruct(https://github.com/Liquid4All/ifstruct)基准测试仓库以及LiquidAI/ifstruct-v1.0(https://huggingface.co/datasets/LiquidAI/ifstruct-v1.0)数据集。
相似文章
用于财务建议生成的GRPO:在CATE评估中超越商业LLM
本文提出了一种方法,使用组相对策略优化(GRPO)对开放权重语言模型进行微调,以生成可操作的财务建议,在独立于评判者的CATE评估中优于商业LLM,同时满足安全标准。
多模块 GRPO:组合策略梯度与提示优化的语言模型程序方法
本文提出 mmGRPO,一种多模块扩展的群体相对策略优化(GRPO)方法,通过优化语言模型调用和提示来提升模块化 AI 系统的准确率。实验表明,该方法在各类任务上平均带来 11% 的准确率提升,并在 DSPy 中提供了开源实现。
在三个从头训练的LLM(353M/316M/672M)上使用相同的GRPO配方得到了三种不同的结果,且与规模没有明显的关系 [P]
一项实验使用相同的GRPO配方训练三个从头开始的LLM,得到了不一致的结果,GRPO在某些模型中降低了性能,特别是中等规模的那个,并且与规模没有明显的关系。
LC-GRPO:利用朗之万校正弥合基于流的GRPO训练-推理差距
本文介绍了LC-GRPO,一种带有朗之万校正的基于流的GRPO框架,通过将随机训练轨迹与确定性ODE采样对齐来弥合训练与推理之间的差距,从而在SD3.5、FLUX.1-Dev和HunyuanVideo等模型上提升奖励优化效果。
@akshay_pachaar: https://x.com/akshay_pachaar/status/2064700531600458093
本文介绍了如何使用GRPO微调LLM(Qwen3-8B)以实现可靠的JSON结构化输出,将模式准确率从62%提升至82%,超越了GPT-4.1的58%。