Extrator de cardรกpios 0.5B

Modelo de 0,5 bilhรฃo de parรขmetros que converte cardรกpios de restaurante escritos em texto livre (mensagens de WhatsApp, texto copiado de PDF, posts e saรญda de OCR) em um catรกlogo estruturado em JSON Lines, com nome, descriรงรฃo, preรงo, porรงรฃo, categoria, restriรงรตes alimentares, tamanhos e adicionais de cada item. ร‰ um ajuste fino com LoRA do Qwen2.5 0.5B Instruct, treinado em uma GTX 1070 sobre o conjunto cardรกpios sintรฉticos em portuguรชs.

A ideia รฉ medir atรฉ onde chega, numa tarefa de extraรงรฃo bem delimitada, um modelo pequeno que roda em CPU e custa uma fraรงรฃo de uma chamada a um modelo grande, quando os dados e a avaliaรงรฃo sรฃo construรญdos com cuidado. Uma demonstraรงรฃo em Gradio para rodar localmente estรก em codigo/demo_gradio.py no conjunto de dados.

English summary. A 0.5B model fine tuned with LoRA from Qwen2.5 0.5B Instruct to turn free text Brazilian Portuguese restaurant menus into a structured JSON Lines catalog. On a hand written out of distribution test set that was never used for any decision, it reaches an item level F1 of 0.645, against 0.450 for a rule based parser and 0.060 for the same base model prompted without fine tuning.

Uso

from transformers import AutoModelForCausalLM, AutoTokenizer

modelo_id = "vkusterl/extrator-cardapios-0.5b"
tok = AutoTokenizer.from_pretrained(modelo_id)
modelo = AutoModelForCausalLM.from_pretrained(modelo_id)

cardapio = """Boa noite! Segue o cardรกpio de hoje ๐Ÿ˜‹
*Marmita P* arroz, feijรฃo, frango grelhado e salada R$ 18
*Marmita G* arroz, feijรฃo, frango grelhado e salada R$ 24
Adicional de ovo +R$ 2
Refri lata R$ 6"""

mensagens = [
    {"role": "system", "content": "Extraia o cardรกpio em JSON Lines, com o restaurante na primeira linha quando houver e um item por linha."},
    {"role": "user", "content": cardapio},
]
entrada = tok.apply_chat_template(mensagens, add_generation_prompt=True, return_tensors="pt", return_dict=True)
saida = modelo.generate(**entrada, max_new_tokens=1024, do_sample=False)
print(tok.decode(saida[0, entrada["input_ids"].shape[1]:], skip_special_tokens=True))

A saรญda deste modelo para o exemplo acima, gerada em CPU, foi a seguinte. O adicional fica no item logo acima dele, que รฉ a convenรงรฃo dos dados de treino.

{"nome": "Marmita P", "descricao": "Arroz, feijรฃo, frango grelhado e salada", "preco": 18.0, "categoria": "Marmitas"}
{"nome": "Marmita G", "descricao": "Arroz, feijรฃo, frango grelhado e salada", "preco": 24.0, "categoria": "Marmitas", "adicionais": [{"nome": "Ovo", "preco": 2.0}]}
{"nome": "Refri Lata", "preco": 6.0, "categoria": "Bebidas"}

Use sempre a mesma instruรงรฃo de sistema do exemplo, que รฉ a do treino. A saรญda tem uma linha por item e pode ser validada com o esquema em codigo/esquema.py do conjunto de dados. Linhas invรกlidas sรฃo raras, mas convรฉm descartรก-las em vez de rejeitar o cardรกpio inteiro, como faz a funรงรฃo de_jsonl.

Resultados

A mรฉtrica principal รฉ o F1 de item correto, em que um item sรณ conta quando o nome foi pareado com o gabarito e o preรงo (ou a grade de tamanhos) e a categoria estรฃo certos. Sรฃo trรชs conjuntos de avaliaรงรฃo. O teste sintรฉtico vem do mesmo gerador do treino. Os dois conjuntos fora da distribuiรงรฃo foram escritos ร  parte, em formatos que o gerador original nรฃo produzia. O de desenvolvimento orientou a segunda versรฃo dos dados, e o de teste foi escrito depois dela e nunca foi usado para decidir nada, entรฃo รฉ a medida mais honesta de generalizaรงรฃo.

Modelo Parรขmetros Teste sintรฉtico (100) Desenvolvimento fora da distribuiรงรฃo (24) Teste fora da distribuiรงรฃo (24)
Extrator v2 (este modelo) 0,5 B 0,972 0,659 0,645
Extrator v1 (primeira rodada) 0,5 B 0,821 0,580 0,582
Qwen2.5 1.5B Instruct sem ajuste fino 1,5 B n/d 0,051 0,120
Qwen2.5 0.5B Instruct sem ajuste fino 0,5 B 0,064 0,090 0,060
Analisador por regras nรฃo se aplica 0,496 0,359 0,450

Na comparaรงรฃo, o teste sintรฉtico usa as 100 primeiras amostras, as mesmas para todos os modelos. No teste sintรฉtico completo, com 500 cardรกpios, este modelo obtรฉm 0,967. Os modelos sem ajuste fino tendem a responder com uma lista JSON dentro de um bloco de cรณdigo, com campos trocados e categorias fora da lista, e mesmo com leitura tolerante ficam longe do modelo ajustado. O Qwen 1.5B sem ajuste fino nรฃo foi rodado no teste sintรฉtico, porque nos dois conjuntos fora da distribuiรงรฃo ele jรก fica muito abaixo do modelo ajustado. Em CPU, limitada a 2 threads, o extrator leva em mรฉdia 38,2 s por cardรกpio, gerando cerca de 8 tokens por segundo.

Acerto por campo deste modelo, calculado sobre os itens pareados.

Mรฉtrica Teste sintรฉtico (500) Desenvolvimento (24) Teste fora da distribuiรงรฃo (24)
Itens encontrados (pareados) 0,989 0,890 0,910
Nome idรชntico ou quase 0,994 0,967 0,970
Preรงo ou grade de tamanhos 0,990 0,934 0,941
Categoria 0,985 0,762 0,743
Porรงรฃo 0,988 0,910 0,861
Restriรงรตes 0,996 0,959 0,980
Adicionais 0,998 1,000 1,000
Descriรงรฃo (F1 de tokens) 0,975 0,776 0,613
Restaurante 0,994 0,667 0,750
Item correto, precisรฃo 0,969 0,655 0,660
Item correto, revocaรงรฃo 0,964 0,664 0,631
Item correto, F1 0,967 0,659 0,645
JSON vรกlido 0,996 0,958 0,917
Cardรกpio inteiro perfeito 0,732 0,125 0,042

F1 de item correto por estilo de escrita no teste sintรฉtico.

Estilo Cardรกpios Modelo Regras
corrido 40 0,934 0,000
informal 31 0,929 0,214
instagram 31 0,961 0,203
lista 86 0,964 0,378
ocr 46 0,950 0,361
pdf 113 0,982 0,484
tabela 49 0,966 0,709
whatsapp 104 0,980 0,689

Como o modelo chegou aqui

A primeira versรฃo foi treinada com um gerador que cobria sete estilos de escrita. Ela acertava praticamente tudo no teste sintรฉtico, mas ficava em 0,580 no conjunto escrito ร  parte. A anรกlise dos erros mostrou trรชs padrรตes. Sabores listados sem o nome do prato, como pizzas sob uma tabela de sabores, recebiam categorias erradas. Linhas com preรงo compartilhado ("Pastรฉis (R$ 9,00 cada): carne, queijo...") nรฃo eram expandidas em um item por sabor. Tamanhos declarados sรณ no cabeรงalho confundiam a associaรงรฃo entre preรงo e tamanho. Nomes fora do vocabulรกrio tambรฉm saรญam com erros de grafia.

A segunda versรฃo do gerador passou a produzir esses formatos, ganhou um estilo informal, mais nomes compostos e pratos internacionais, e o modelo foi treinado de novo do zero. Para nรฃo medir a melhora com o mesmo material que a orientou, um segundo conjunto fora da distribuiรงรฃo foi escrito antes de qualquer resultado da nova versรฃo. Nele, a primeira versรฃo obtรฉm 0,582 e esta obtรฉm 0,645.

A melhora foi real, mas parcial. No teste final, o modelo encontrou 101 dos 111 itens e acertou o nome de quase todos, mas 26 deles saรญram com a categoria errada, o que faz da categoria o principal gargalo. Pratos fora do vocabulรกrio do gerador, como abarรก, quesadilla e bolinho de estudante, tendem a cair em Porรงรตes ou Acompanhamentos, e itens de cafรฉ da manhรฃ como pรฃo de queijo e cuscuz saem como Lanches. Parte desses casos admite mais de uma resposta razoรกvel (mandioca frita pode ser porรงรฃo ou acompanhamento), mas a maioria รฉ erro de fato.

Os formatos que motivaram a segunda versรฃo ainda falham em parte. Em linhas de preรงo compartilhado, o modelo ร s vezes registra sรณ o sabor ("Carne" em vez de "Pastel de Carne"), e grades de tamanho declaradas sรณ no cabeรงalho continuam sendo perdidas em alguns cardรกpios. Tambรฉm aparecem erros em formatos que o gerador nรฃo produz, como o preรงo promocional ("de R$ 59,90 por R$ 44,90"), a venda por unidade e por cento e o prefixo omitido ("Sanduรญche natural de frango 14 / de atum 15"). O prรณximo passo รฉ ampliar o vocabulรกrio com pratos regionais e de cafรฉ da manhรฃ e levar esses formatos ao gerador.

Treino

Configuraรงรฃo Valor
Modelo base Qwen/Qwen2.5-0.5B-Instruct
Mรฉtodo LoRA (r 16, alfa 32, dropout 0,05) em todas as projeรงรตes lineares, depois incorporado ao modelo
Dados 4.000 cardรกpios sintรฉticos, 1 รฉpoca, perda calculada sรณ na resposta
Otimizaรงรฃo AdamW, taxa de aprendizado 2e-4 com decaimento cosseno, lote efetivo de 8
Comprimento mรกximo 2.048 tokens
Hardware NVIDIA GTX 1070 (8 GB), fp32
Tempo 106 minutos
Perda final treino 0,0276, validaรงรฃo 0,0108

A instruรงรฃo de sistema usada no treino รฉ curta (40 tokens), porque o formato รฉ aprendido a partir dos dados. Os modelos sem ajuste fino da comparaรงรฃo recebem uma instruรงรฃo longa, com o esquema e a lista de categorias, que รฉ a melhor condiรงรฃo para eles, e suas respostas sรฃo lidas de forma tolerante.

Limitaรงรตes

O modelo foi treinado sรณ com dados sintรฉticos, e a diferenรงa entre o teste sintรฉtico e os conjuntos fora da distribuiรงรฃo mostra quanto o desempenho cai diante de formatos novos. Cardรกpios reais com combinaรงรตes, observaรงรตes de preparo ou layouts muito irregulares podem gerar itens faltando, nomes alterados ou preรงos associados ao item errado, entรฃo a saรญda deve passar por revisรฃo humana antes de virar catรกlogo. Em listas muito curtas, sem descriรงรตes, o modelo ร s vezes inventa descriรงรตes e porรงรตes que nรฃo estรฃo no texto, provavelmente porque cerca de dois terรงos dos itens dos dados sintรฉticos trazem descriรงรฃo. A demonstraรงรฃo descarta esses campos quando suas palavras e seus nรบmeros nรฃo aparecem no cardรกpio (funรงรฃo filtra em codigo/demo_gradio.py), e o mesmo cuidado vale para qualquer uso real. As categorias seguem uma taxonomia fixa de 14 classes. O modelo nรฃo lรช imagens, entรฃo cardรกpios em foto precisam de OCR antes. Restriรงรตes alimentares sรณ sรฃo registradas quando o cardรกpio as declara, e o modelo nรฃo deve ser usado para inferir alergรชnicos.

Licenรงa e autoria

Apache 2.0, a mesma do modelo base. Treinado e avaliado por Vinicius Kuster Lodi, Universidade Federal de Viรงosa. O cรณdigo de geraรงรฃo, treino e avaliaรงรฃo estรก na pasta codigo/ do conjunto de dados.

Downloads last month
15
Safetensors
Model size
0.5B params
Tensor type
BF16
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for vkusterl/extrator-cardapios-0.5b

Finetuned
(1107)
this model
Adapters
1 model

Dataset used to train vkusterl/extrator-cardapios-0.5b