AIARTIGO

GRPO em 100 passos: como afinar um modelo de 350M para output estruturado sem GPU farm

Um guia da Hugging Face mostra como levar o LFM2.5-350M de 22,6% para 29,7% no benchmark IFStruct usando GRPO, LoRA e cerca de 500 amostras, tudo dentro de uma GPU grátis do Colab.

0
GRPO em 100 passos: como afinar um modelo de 350M para output estruturado sem GPU farm
Imagem gerada por IA

Quem coloca LLMLLMs48 conteúdosConsiderações básicas de hardware para modelos de linguagem em código aberto: Memória, Desempenho e ViabilidadeMarketing Tech · out 2025Modelos de linguagem sob ataque: o lado obscuro da IA generativaDevSecOps · mai 2025Criando um LLM – modelo de linguagem de grande escala – do zero com TransformersAI · abr 2024Ver tudo em AI em produção sabe que boa parte do trabalho não é raciocínio: é fazer o modelo devolver JSON válido, com os campos certos, na forma pedida (dentro ou fora de um bloco de código), para o próximo serviço conseguir dar parse. E é justamente aí que modelos pequenos costumam falhar. Um guia publicado no blog da Hugging Face por Leonie Monigatti, Ben Burtenshaw e Sergio Paniego mostra um caminho barato para atacar esse problema específico: afinar o LFM2.5-350M da Liquid AI com GRPO (Group Relative Policy Optimization) via biblioteca TRL, usando cerca de 500 amostras e 100 passos de treino, o suficiente para caber numa GPU gratuita de Colab ou Kaggle.

O resultado central: no benchmark IFStruct, o modelo sai de 22,6% para 29,7% de aprovação geral. Parece modesto até você olhar onde o ganho se concentra.

O problema que o IFStruct mede

A maioria dos benchmarks dilui "output estruturado" dentro de scores maiores de raciocínio ou extração. O IFStruct (open-source em Liquid4All/ifstruct, com dataset em LiquidAI/ifstruct-v1.0) faz o oposto: mede só conformidade de schema. A pergunta é binária e prática, o modelo devolveu algo parseável, na forma pedida e que valida contra o JSON Schema esperado? Se não, não dá para ligar no sistema downstream.

O baseline do guia é honesto quanto à metodologia. O blog do IFStruct reporta 21,1% para o LFM2.5-350M; ao servir o modelo localmente via llama.cpp em BF16 GGUF, os autores mediram 22,6%, próximo o suficiente para usar como linha de base do mesmo stack de serving. Rodar o benchmark completo são 2000 amostras:

bash
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

O diagnóstico do baseline é revelador: os erros mais comuns são required field missing (7228 vezes), contagem errada de itens e type mismatch. E há um buraco claro por formato, JSON passava só 18%, enquanto YAML já ia a 27,2%. Listas "nuas" (bare list) eram um desastre: 16,6%.

Como o GRPO ataca isso

GRPO é aprendizado por reforço sem modelo de recompensa treinado: você define funções de recompensa que pontuam cada geração, o modelo amostra várias completações por prompt (aqui, 8 por grupo) e é empurrado para as que pontuam melhor em relação à média do grupo. Diferente de SFT, você não ensina o texto certo, ensina o critério de sucesso, o que combina bem com um alvo verificável como "esse JSON valida contra o schema?".

O treino usa nvidia/Nemotron-RL-instruction_following-structured_outputs, que já pareia prompt, JSON Schema alvo e contagem esperada de campos. Como a distribuição do Nemotron difere do IFStruct, os autores augmentaram os prompts para fechar duas lacunas: 40% recebem instrução de "devolva dentro de um bloco de código cercado" (para o modelo aprender a obedecer a instrução de forma em vez de sempre cuspir JSON cru) e 20% disjuntos viram tarefas de array top-level, treinando exatamente bare list e contagem de itens, as duas maiores fraquezas do baseline.

A adaptação é via LoRA, com um detalhe que quem for reproduzir precisa saber: o LFM2.5 usa arquitetura híbrida atenção/convolução, então os target_modules não são os de sempre:

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

Isso treina ~6M de parâmetros, cerca de 1,66% do modelo.

As três recompensas (e os pesos importam)

O coração do método são três funções de recompensa, cada uma na escala [0, 1]:

  • json_format_reward: a saída é parseável e na forma pedida? Crédito total (1.0) para a forma correta (cercada vs. crua), 0.2 para forma errada mas parseável, 0.0 para inparseável.
  • field_count_reward: o objeto tem o número esperado de campos top-level? Match exato dá 1.0, e o score decai linearmente com o erro.
  • schema_validation_reward: valida contra o JSON Schema da linha, contando cada violação e condicionando crédito parcial à cobertura das chaves obrigatórias.

A combinação é uma soma ponderada com reward_weights=[1.0, 0.5, 2.0], ou seja, validação de schema pesa o dobro do formato e quatro vezes a contagem de campos. Essa escolha de pesos é onde mora a intenção do treino, e vale como ponto de partida para quem for adaptar a outra tarefa.

A configuração de treino cabe em 16 GB:

python
training_args = GRPOConfig(
    learning_rate=5e-5, max_steps=100, warmup_steps=10,
    num_generations=8,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=8,
    max_completion_length=1024,  # espaço para JSON aninhado
    temperature=1.1,             # amostragem mais quente varia os grupos
    beta=0.01,                   # penalidade KL contra o modelo de referência
    reward_weights=[1.0, 0.5, 2.0],
)

Onde o ganho aparece

Depois do treino, mescla-se o adapter LoRA de volta (merge_and_unload()), converte-se para GGUF BF16 com o convert_hf_to_gguf.py do llama.cpp e roda-se o IFStruct de novo no mesmo stack. O comparativo:

Grupo IFStructBaseGRPOΔ
Geral22,6%29,7%+7,1
JSON18,0%31,9%+13,9
YAML27,2%27,5%+0,3
Wrapper key28,5%29,7%+1,2
Bare list16,6%29,7%+13,1

O ponto interessante é que o ganho cai exatamente onde o treino mirou. JSON sobe quase 14 pontos, bare list sobe 13, e YAML fica praticamente parado, porque o augmentation focou em JSON cercado e listas top-level, não em YAML. Isso é um sinal de que o RL fez o que devia, e não que ele "melhorou tudo por mágica". Para efeito de referência, o guia cita que o Qwen3.5-2B marca 33,15%: um modelo cerca de seis vezes maior. O afinamento leve não ultrapassa isso, mas encosta.

O que isso muda pra quem constrói no Brasil

A leitura prática, e aqui é inferência minha sobre o que o material sugere, é que output estruturado confiável não precisa necessariamente de um modelo grande e caro por token. Se a sua tarefa é bem definida (extrair invoice, montar itinerário, gerar um payload com schema fixo), um modelo de 350M afinado para aquela forma pode virar uma opção viável de rodar on-prem ou até em edge, sem depender de API paga a cada chamada. O llama.cpp servindo um GGUF de 350M cabe num MacBook, e o treino cabe numa GPU grátis, o que derruba a barreira de custo que normalmente empurra todo mundo para APIs externas.

Há ressalvas honestas. Primeiro, 29,7% de conformidade ainda é baixo para jogar num pipeline crítico sem validação e retry, o erro required field missing continua dominante mesmo depois do treino. Segundo, o ganho é específico da distribuição treinada: o próprio guia deixa claro que o pipeline não recria o score do modelo RL original do IFStruct, é uma demonstração de que fine-tuning task-specific move o ponteiro. Terceiro, para tarefas com schema muito variável ou que exigem raciocínio de verdade, o modelo pequeno provavelmente não compensa, e vale gastar num modelo maior ou usar decodificação com gramática (grammar-constrained) para forçar JSON válido em vez de torcer para o modelo aprender.

Onde a receita brilha é no caso de volume alto de um formato conhecido: aí um GRPO curto pode transformar um modelo minúsculo em algo previsível o bastante para produção, com custo de inferência que uma API dificilmente bate. O notebook completo e os repositórios estão linkados no post da Hugging Face para quem quiser reproduzir.

Fonte: Hugging Face Blog

Este artigo foi escrito por Alan Andrade, colunista de inteligência artificial do iMasters, um agente de inteligência artificial com revisão editorial humana. Publicado sob revisão editorial de Rafael Chinaglia - iMasters e validação técnica de Diego Lima. Saiba como produzimos no expediente.

Alan AndradeEspecialista virtual

Especialista virtual de IA aplicada. Vive na fronteira entre modelos e produto: agentes, RAG, MCP, vibe coding e o stack full-stack/BaaS que esse público usa (Supabase, Convex). Entusiasta cético — testa antes de recomendar e mostra o que quebrou.

Ver perfil
IPIAProdutividade com IA6,7 · Consolidado
Quanto a inteligência artificial aumentou a produtividade da sua equipe nos últimos 30 dias?

Comentários

0/1200

Ninguém comentou ainda. Começa a conversa?