Hugging Face、GRPOを用いた小規模モデルの構造化出力改善手法を公開
本文の状態
日本語全文を表示中
詳細モードで約11分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
Hugging Face Blog
Liquid AI は、GRPO と TRL ライブラリを用いて 350M パラメータの軽量モデルをわずか 100 ステップで微調整し、構造化出力の適合率を 29.7% に向上させる手法を公開した。
AI深層分析を開く2026年9月3日 20:43
AI深層分析
キーポイント
軽微な微調整による性能向上
Liquid AI は GRPO アルゴリズムと TRL ライブラリを活用し、LFM2.5-350M モデルを約 500 サンプルで 100 ステップ微調整するだけで、IFStruct ベンチマークにおける構造化出力適合率を 22.6% から 29.7% に引き上げた。
低コストな実装環境
このトレーニングパイプラインは無料枠の Colab や Kaggle GPU で実行可能であり、評価プロセスも MacBook のローカル環境で完結するため、開発者が容易に試行錯誤できる。
構造化出力の重要性と課題
LLM が要求された形式やスキーマに従って信頼性のある出力を生成する能力は、システム連携の成否を決める重要な要素だが、多くのベンチマークではこの指標が単独で測られていない。
ベースライン評価の実施
LFM2.5-350MモデルをMacBook上でllama.cppを用いてローカルサーバー化し、IFStructベンチマークで再評価を行った。
評価結果の分析
全体合格率は22.6%となり、JSON形式が18.0%、YAML形式が27.2%であった。
重要な引用
The results show that even a light fine-tuning procedure improves performance from 22.6% to 29.7% on the IFStruct benchmark.
Whether a model reliably returns valid, parseable output in the requested format and shape — schema compliance — is often what decides whether it can be wired into a downstream system at all.
Overall: 452/2000 passed (22.6%)
By format: JSON: 180/1000 passed (18.0%), YAML: 272/1000 passed (27.2%)
編集コメントを表示
編集コメント
このブログ記事は、大規模モデルへの依存を減らしつつ実用性を高めるための現実的なアプローチを示している。特に、GRPO を活用した軽量モデルの微調整手法が、リソース制約のある現場でも即座に適用可能な点が高く評価される。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
このガイドは、小規模モデルの構造化出力への準拠能力を劇的に向上させるための、誰でも無料で実行可能なレシピです。LiquidAI の LFM2.5-350M モデルに、TRL ライブラリ を用いて Group Relative Policy Optimization (GRPO) で微調整を施し、IFStruct ベンチマーク で評価を行います。必要なデータは約 500 サンプル、学習ステップは 100 ステップ程度で、無料枠の Colab や Kaggle GPU でも十分に実行可能です。詳細な手順は GitHub で公開されています。
この軽微な微調整により、IFStruct ベンチマークでのスコアが 22.6% から 29.7% に向上することが確認できました。
構造化出力は、LLM が実世界で最も頻繁に扱うタスクの一つですが、多くのベンチマークではこれを広範な推論や情報抽出のスコアに含めて評価しており、独立した指標として測定されていません。モデルが指定された形式と形状に従って、確実に有効かつ解析可能な出力を返せるか(スキーマ準拠)——これが、そのモデルを実際のシステムに組み込めるかどうかを決定する重要な要素となります。
※ここで説明するトレーニングパイプラインは、IFStruct ブログ で紹介されている RL モデルの学習には使用されていません。このノートブックの目的は IFStruct ベンチマークスコアの再現ではなく、小規模モデルをタスク固有で微調整することで性能が向上し、はるかに大規模なモデルに匹敵する結果が得られることを示すことにあります。
事前準備
本ガイドは、異なる環境で実行される 2 つの部分から構成されています。
- 微調整(Fine-tuning) は GPU 上で行います。同梱のノートブックは、無料枠の Colab または Kaggle の GPU で動作するように最適化されています。
評価は、MacBook(ここではApple M5 Max搭載のMacBook Proと36GBの統合メモリ)上でローカル実行が可能です。llama.cpp を介してOpenAI互換サーバーを起動し、IFStruct evaluator がそのサーバーに接続して動作します。
Python ツールには `uv` を、推論サーバーとしては llama.cpp を使用します。[Liquid AI の llama.cpp 展開ガイド]( に従ってください。
Liquid AI のドキュメント(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の評価
まずは、LFM2.5-350M を IFStruct ベンチマーク で評価し、報告されているスコア 21.1% の再現が可能か確認しましょう。
IFStructは、LLMの出力の有効性とスキーマ準拠性を検証するためのベンチマークです。このベンチマークはオープンソースで、GitHub上のLiquid4All/ifstructで公開されています。また、公的なベンチマークデータセットはHugging FaceのLiquidAI/ifstruct-v1.0でも入手可能です。
git clone https://github.com/Liquid4All/ifstruct.git
評価比較では、MacBook上でllama.cppを使用してモデルをローカルで実行します。ここではBF16形式のGGUF (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: OpenAI互換エンドポイントに対してIFStructが送信するモデル名
-ngl 99:利用可能な場合、すべてのレイヤーを GPU にオフロードするようにllama.cppに指示します。
-np 4: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
============================================================
Model: LiquidAI/LFM2.5-350M
============================================================
Overall: 452/2000 passed (22.6%)
Average latency: 1453ms
By format:
JSON: 180/1000 passed (18.0%)
YAML: 272/1000 passed (27.2%)
By top-level structure:
Wrapper key 288/1011 passed (28.5%)
Bare list 164/989 passed (16.6%)
By entity type:
test__camera_review 6/83 passed (7.2%)
test__clinical_trial 20/104 passed (19.2%)
test__conference_schedule 7/87 passed (8.0%)
test__escaping__bug_report_batch 24/89 passed (27.0%)
test__escaping__config_snippet_audit 15/85 passed (17.6%)
test__escaping__customer_email_thread 5/73 passed (6.8%)
test__escaping__dialogue_sample 14/95 passed (14.7%)
test__escaping__interview_transcript_segment 21/80 passed (26.2%)
test__escaping__log_parser_examples 21/72 passed (29.2%)
test__escaping__pr_discussion 22/87 passed (25.3%)
test__escaping__repro_steps_batch 16/73 passed (21.9%)
test__escaping__screenplay_scene 16/92 passed (17.4%)
test__escaping__short_story_chapter 15/84 passed (17.9%)
test__escaping__support_ticket_batch 27/73 passed (37.0%)
test__escaping__terminal_session_notes 20/70 passed (28.6%)
test__event_ticket_booking 49/107 passed (45.8%)
test__gpu_review 6/94 passed (6.4%)
test__invoice 28/86 passed (32.6%)
test__job_posting 25/85 passed (29.4%)
test__real_estate_listing 31/82 passed (37.8%)
test__recipe 3/70 passed (4.3%)
test__rental_car_booking 27/79 passed (34.2%)
test__scientific_experiment 13/69 passed (18.8%)
test__travel_itinerary 21/81 passed (25.9%)
Common errors:
7228x required field missing
738x wrong item count
540x type mismatch
317x Unclosed code block
190x extraneous field 'notes'
181x extraneous field 'path'
175x extraneous field 'constraints'
170x extraneous field 'type'
170x missing code block
100x expected bare list, got wrapper
IFStruct のリリースブログでは、LFM2.5-350M のスコアが 21.1% と報告されています。一方、私たちのローカル環境(llama.cpp を BF16 で実行)で測定した値は 22.6% です。これは IFStruct ブログの報告値とほぼ同等です。本稿では、同じサービングスタックでの比較を行うため、このローカル結果をベースラインとして使用します。
TRL を用いた構造化出力向けの GRPO ファインチューニング
完全な実行可能なパイプラインは、同梱のノートブック にあります。ここでは関連する部分のみを取り上げます。
訓練データ
nvidia/Nemotron-RL-instruction_following_structured_outputs データセットを使用します。このデータセットでは、各プロンプトに対応するターゲットの JSON スキーマと期待されるフィールド数がペアになっています。訓練には約 500 サンプルを利用します。
Nemotron データの分布は IFStruct の評価用データとは異なるため、両者のギャップを埋めるためにプロンプトを増強しました。 (原文の技術表記: nvidia/Nemotron-RL-instruction_following-structured_outputs)
出力をフェンス付きコードブロック内に返すという指示を付与するケースが全体の 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% に相当します。
リワード関数
次に、抽出された構造が正しいかどうかを評価する 3 つの報酬関数を定義します。これらはすべて [0, 1] の範囲でスコア付けされます。
json_format_reward:出力が構文解析可能か、かつ要求された形式になっているかを判定します。要求された形式(囲み付きの形式など)であれば満点(1.0)を付与します。
正解だが構文解析可能な形式には 0.2、構文解析不可能な出力には 0.0 を割り当てる。
field_count_reward:生成されたオブジェクトが、期待されるトップレベルのフィールド数を正確に持っているかを確認する報酬です。完全一致の場合は1.0が付与され、不一致の場合はその差に応じて線形にスコアが減点されます。
「schema_validation_reward」は、出力が各行の JSON スキーマに適合しているかを検証します。すべての制約違反をカウントし、必須キーのカバー率に応じて部分的な報酬を与える仕組みです。
これら 3 つの指標を重み付き和で組み合わせます。重みは reward_weights=[1.0, 0.5, 2.0] です。
トレーニング
フリーティアの 16 GB GPU で動作するサイズとして、100 ステップ、プロンプトグループあたり 8 世代分の生成でトレーニングを行います。
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, # completions sampled per prompt group
per_device_train_batch_size=4,
gradient_accumulation_steps=8, # 4 prompt groups per optimizer step
steps_per_generation=2,
max_completion_length=1024, # room for nested JSON
mask_truncated_completions=False,
temperature=1.1, # hotter sampling keeps groups varied
beta=0.01, # KL penalty toward the reference model
reward_weights=[1.0, 0.5, 2.0], # json_format, field_count, schema_validation
logging_steps=1,
save_steps=100,
)
ノートブックのグラフを見ると、学習が進むにつれて報酬の 3 つの要素すべてが上昇し、ウォームアップ後に参照モデルからの 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 微調整後の 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
============================================================
Model: lfm25-350m-grpo-structured-output
============================================================
Overall: 594/2000 passed (29.7%)
Average latency: 1518ms
By format:
JSON: 319/1000 passed (31.9%)
YAML: 275/1000 passed (27.5%)
By top-level structure:
Wrapper key 300/1011 passed (29.7%)
Bare list 294/989 passed (29.7%)
By entity type:
test__camera_review 5/83 passed (6.0%)
test__clinical_trial 31/104 passed (29.8%)
test__conference_schedule 11/87 passed (12.6%)
test__escaping__bug_report_batch 32/89 passed (36.0%)
test__escaping__config_snippet_audit 24/85 passed (28.2%)
test__escaping__customer_email_thread 9/73 passed (12.3%)
test__escaping__dialogue_sample 17/95 passed (17.9%)
test__escaping__interview_transcript_segment 13/80 passed (16.2%)
test__escaping__log_parser_examples 33/72 passed (45.8%)
test__escaping__pr_discussion 26/87 passed (29.9%)
test__escaping__repro_steps_batch 23/73 passed (31.5%)
test__escaping__screenplay_scene 34/92 passed (37.0%)
test__escaping__short_story_chapter 24/84 passed (28.6%)
test__escaping__support_ticket_batch 36/73 passed (49.3%)
test__escaping__terminal_session_notes 23/70 passed (32.9%)
test__event_ticket_booking 62/107 passed (57.9%)
test__gpu_review 7/94 passed (7.4%)
test__invoice 36/86 passed (41.9%)
test__job_posting 33/85 passed (38.8%)
test__real_estate_listing 32/82 passed (39.0%)
test__recipe 7/70 passed (10.0%)
test__rental_car_booking 37/79 passed (46.8%)
test__scientific_experiment 14/69 passed (20.3%)
test__travel_itinerary 25/81 passed (30.9%)
Common errors:
7331x required field missing
890x wrong item count
555x type mismatch
102x expected bare list, got wrapper
62x extraneous field 'metadata.tone'
55x 6 is greater than maximum 5
49x extraneous field 'speaker_labels'
47x extraneous field 'tone'
44x 'cups' not in allowed values ['mg', 'g', 'kg', 'oz', 'lb', 'ml', 'l', 'cl', 'dl'
44x extraneous field 'notes'
同一のサービング環境における 2 つの実行結果を比較します。
| IFStruct グループ | base | 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% を下回っていますが、タスク特化型の軽量なファインチューニングでも、小規模モデルを大規模モデルに近づけられる可能性を示しています。
結論
約 500 サンプルと 100 ステップの短い GRPO 実行で、IFStruct ベンチマークにおいてパラメータ数 3.5 億(350M)の小規模モデルを 22.6% から 29.7% に引き上げることができました。
重要なのは、安価なタスク特化型の報酬信号が、小規模モデルの「形式」に関する信頼性を劇的に高め、数倍サイズのモデルとの差を大幅に縮められる点です。
この研究の再現や拡張については、元の IFStruct v1.0 ブログ記事、Liquid4All/ifstruct ベンチマークリポジトリ、および LiquidAI/ifstruct-v1.0 データセット を参照してください。
関連記事
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み