Hugging Face Blog

Fine-tuning a 350M Model for Better Structured Outputs in 100 GRPO Steps

8.5内容质量

TL;DR · AI 摘要

使用GRPO方法微调350M模型可在100步内提升IFStruct基准性能至29.7%。

核心要点

  • GRPO微调使350M模型在IFStruct基准上提升7.1个百分点(22.6%→29.7%)
  • TRL库与llama.cpp组合支持免费GPU环境下的高效微调
  • MacBook Pro M5 Max可本地运行IFStruct评估服务

结构提纲

按章节快速跳转。

  1. 介绍通过GRPO方法提升模型结构化输出性能的核心目标

  2. 使用TRL库实现GRPO微调,基于LFM2.5-350M模型

  3. 描述Colab/Kaggle GPU微调与MacBook本地评估的双环境配置

  4. 在IFStruct基准上验证微调效果,对比基线模型21.1%得分

  5. 微调后模型在IFStruct基准达到29.7%的显著提升

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • GRPO微调实践
    • 方法论
      • TRL库实现GRPO
    • 实验环境
      • Colab/Kaggle GPU
      • MacBook llama.cpp
    • 评估结果
      • IFStruct 29.7%得分

金句 / Highlights

值得收藏与分享的关键句。

#GRPO#微调#IFStruct#TRL库#Hugging Face
打开原文

通过 100 步 GRPO 微调 350M 模型以实现更优结构化输出

返回文章列表

[-1

]

[0

发布于 2026 年 9 月 3 日

GitHub 上的更新

赞同

107

[

  • +101

Leonie Monigatti

iamleonie

关注

LiquidAI

ben burtenshaw

burtenshaw

Sergio Paniego

sergiopaniego

本指南提供了一种完全公开且成本低廉的方案,使小型模型在结构化输出合规性方面显著提升。我们使用 TRL 库通过 Group Relative Policy Optimization (GRPO) 对 LFM2.5-350M 进行微调,并在 IFStruct 基准测试中进行评估。完整训练过程约需 500 个样本和 100 个训练步骤,规模足够小,可在免费版 Colab 或 Kaggle GPU 上运行,相关代码已开源在 GitHub。结果显示,即使进行轻量级微调,模型在 IFStruct 基准测试中的表现也能从 22.6% 提升至 29.7%。

结构化输出是 LLM 最常见的现实任务之一,但大多数基准测试会将其纳入更广泛的推理或提取评分中,而非单独衡量。模型是否能可靠地返回符合请求格式和形状的有效、可解析输出(即模式合规性),往往是决定其能否接入下游系统的关键因素。

请注意,此处描述的训练流程与 IFStruct 博客中描述的 RL 模型训练流程不同。本笔记本的目标不是重现 IFStruct 基准测试的评分,而是展示如何通过针对特定任务的微调提升小型模型性能,使其达到远超自身规模的大型模型水平。

先决条件

本指南分为两部分,分别在不同环境中运行:

  • 微调在 GPU 上执行。配套的笔记本已针对免费版 Colab 或 Kaggle GPU 进行优化。
  • 评估可在 MacBook 上本地运行(此处使用配备 Apple M5 Max 和 36 GB 统一内存的 MacBook Pro),通过 llama.cpp 提供的 OpenAI 兼容服务器进行,该服务器由 IFStruct 评估器访问。

我们需要 uv 用于 Python 工具链,以及 llama.cpp 用于服务。按照 Liquid AI 的 llama.cpp 部署文档,通过 Homebrew 安装 llama.cpp 并验证 llama-server 是否可用:

code
brew install llama.cpp
llama-server --version

LFM2.5-350M(基础模型)的 IFStruct 评估

在开始之前,让我们先在 IFStruct 基准测试中评估 LFM2.5-350M,看看是否能复现报告的 21.1% 评分。

IFStruct 是一个用于测试 LLM 输出有效性和模式遵循度的基准测试。该基准测试在 Liquid4All/ifstruct 中开源,公开的基准测试数据集可在 Hugging Face 的 LiquidAI/ifstruct-v1.0 中获取。

code
git clone https://github.com/Liquid4All/ifstruct.git

为了进行评估比较,我们使用 llama.cpp 在 MacBook 上本地运行模型。我们将使用 BF16 GGUF 格式(LiquidAI/LFM2.5-350M-GGUF)。

然后使用以下命令启动基础模型服务器:

code
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 可用时将所有层卸载到 GPU
  • -np 4:并行处理四个请求
  • -c 32768:提示上下文的大小

服务器启动后,我们可以使用 2000 个样本运行完整的基准测试:

code
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
code
============================================================
模型: LiquidAI/LFM2.5-350M
============================================================
总体: 452/2000 通过 (22.6%)
平均延迟: 1453ms

按格式划分:
  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%。我们本地的 llama.cpp/BF16 配置测得 22.6%,与 IFStruct 博客中报告的 21.1% 接近。我们使用这个本地结果作为相同服务堆栈比较的基准。

使用 TRL 对 GRPO 进行结构化输出微调

完整的可运行流水线位于配套的 notebook 中。本节仅涵盖相关内容。

训练数据

我们使用 nvidia/Nemotron-RL-instruction_following-structured_outputs 数据集,该数据集为每个提示配对目标 JSON Schema 和预期字段数量。我们使用约 500 个样本进行训练。

由于 Nemotron 数据分布与 IFStruct 评估存在差异,我们通过增强提示来弥合两者之间的两个差距:

  • 40% 的提示附加了 "将输出放入代码块中" 的指令,使模型学会遵循格式指令而非始终输出原始 JSON。
  • 20% 的独立数据被转换为顶级数组任务(模式被封装在数组中并指定必需的项目数量),这训练了裸列表输出和项目数量合规性。

模型与 LoRA

我们加载 LiquidAI/LFM2.5-350M 并附加 LoRA 适配器。由于 LFM2.5 使用混合注意力/卷积架构,我们针对 LFM 特定的模块名称:

code
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"
,
    ],
)

这训练了约 6M 参数,约占模型总参数的 1.66%。

奖励函数

然后我们定义了三个奖励函数,每个函数的评分范围为 [0, 1],用于评估每次生成的结构是否正确:

  • json_format_reward : 输出是否可解析且符合要求的格式?若符合要求格式(带代码块或原始格式)得满分(1.0),若格式错误但可解析得 0.2 分,若无法解析得 0 分。
  • field_count_reward : 对象是否具有预期数量的顶级字段?完全匹配得 1.0 分,匹配偏差越大得分线性衰减。
  • schema_validation_reward : 输出是否符合该行的 JSON Schema?该函数统计所有约束违规项,并根据必需字段覆盖率决定部分得分。

我们将三者按权重求和,权重为 [1.0, 0.5, 2.0]。

训练

我们使用 8 个生成样本/提示组进行 100 步训练,参数规模适配免费层级的 16 GB GPU:

code
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 格式、字段数量、Schema 验证
logging_steps=
1
,
    save_steps=
100
,
)

如笔记本所示,在训练过程中,所有三个奖励组件得分均上升,参考模型的 KL 散度在预热后脱离零点,截断完成比例始终保持在接近零的水平。

合并与保存模型

最后,我们将 LoRA 适配器重新合并到基础权重中,并保存为一个自包含的检查点,准备转换为 GGUF 格式进行服务:

code
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 包:

code
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

然后使用以下命令启动合并后的模型服务:

code
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 评估:

code
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
code
============================================================
模型: lfm25-350m-grpo-structured-output
============================================================
总体: 594/2000 通过 (29.7%)
平均延迟: 1518ms

按格式分类:
  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%

+1.2

纯列表

16.6%

+13.1

提升效果完全符合训练目标:JSON 通过率提升近 14 个百分点(18.0% → 31.9%),而 YAML 基本保持不变。尽管这仍然低于 Qwen3.5-2B 的 33.15% 分数,但表明即使是轻量级任务特定微调,也能让小型模型接近大型模型的表现。

结论

A short GRPO run with about 500 samples and 100 steps can lift a small 350M parameter model from 22.6% to 29.7% on IFStruct. The takeaway is that a cheap, task-specific reward signal can make a small model substantially more reliable about form , closing much of the gap to models several times its size.

To reproduce or extend this work, see the original IFStruct v1.0 blog post , the Liquid4All/ifstruct benchmark repo, and the LiquidAI/ifstruct-v1.0 dataset.

Models mentioned in this article 2

Datasets mentioned in this article 2

More Articles from our Blog

nlp

guide

community

使用Sentence Transformers进行多向量嵌入模型的训练和微调

128

2026年8月26日

使用Sentence Transformers的多向量(晚期交互)嵌入模型

111

2026年8月18日

社区

helpmehere

9天前

[2

这是我们的安全扫描测试评论

查看翻译

回复

未提供描述。

x0mhb

8天前

clickme

编辑

预览

通过将文件拖拽到文本输入框、粘贴或

点击此处

上传图片、音频和视频。

轻点或粘贴此处上传图片

评论

· 注册或登录以发表评论

  • +95