Como reduzir o custo de categorizar milhares de produtos treinando um modelo local
Categorização Híbrida de Produtos: Sklearn local + IA como fallback
Como reduzimos o custo de categorizar milhares de produtos treinando um modelo local que resolve parte dos casos a custo zero antes de chamar uma LLM.
O problema
O catálogo do Lojas Paraguai recebe milhares de produtos novos vindos do crawler, e cada um precisa de categoria e marca para aparecer corretamente no site. Categorizar à mão é inviável, e a primeira solução automatizada, enviar todos os produtos para uma LLM, funcionava, mas com dois custos altos:
- Tempo: alguns segundos por batch via API (Ollama/Google/OpenAI).
- Dinheiro: cada batch de produtos era uma chamada de API paga, mesmo para casos óbvios como "Samsung Galaxy A50" → Smartphones / Samsung.
A maioria dos produtos é trivial de classificar. Pagar uma LLM para resolver o óbvio não fazia sentido.
A solução
Uma estratégia híbrida em duas fases, implementada no comando categorize_products_hybrid:
-
Fase 1, Sklearn (local, custo zero, mais rápido) Um classificador treinado com os próprios dados do banco prediz categoria e marca a partir do nome do produto. Se a confiança for ≥ ao threshold (padrão 0.75), o resultado é aceito direto.
-
Fase 2, LLM (fallback, tem custo, mais lento) Só os produtos em que o modelo local ficou inseguro vão para a IA (via LiteLLM, padrão
gemini-2.5-flash-lite), que retorna nomes de categoria/marca resolvidos contra o banco por um mapa de aliases. Esse custo de api é cortado com a possibilidade do uso de modelos locais.
A fração que o modelo local resolve sozinho depende do mix de produtos: em itens comuns é alta, na cauda longa (nomes ruins, produtos raros, marcas obscuras) a confiança cai e a LLM assume. Cada produto que o Sklearn resolve é tempo e custo de API economizados.
Comparativo por batch de 50 produtos (mesma carga nas duas colunas):
| Métrica | Sklearn (local) | LLM (se tudo fosse IA) |
|---|---|---|
| Tempo/batch (50) | <50ms | alguns segundos |
| Custo API/batch | 0 | ~0.005 USD |
| Acurácia | cat ~94% · marca ~90% | ~95–98% (estimado) |
Como resolvemos
1. Treinamos um modelo para rodar localmente
O comando train_category_brand_model treina dois classificadores independentes, um para categoria (~1000 classes) e outro para marca (~200), salvos como pickles separados.
O pipeline é TF-IDF + SGDClassifier, gera os modelos como pickle:
crawler/data/models/categoria_classifier.pkl
crawler/data/models/marca_classifier.pkl
lnp train_category_brand_model
Esse é o ponto-chave: o modelo roda 100% local, sem API, sem rede, sem custo por predição. Carregado uma vez, classifica milhares de produtos por segundo. E como é treinado com os dados do próprio banco, retorna IDs diretos de categoria e marca, sem necessidade de resolução de nomes.
2. Orquestramos as duas fases com threshold de confiança
O processor_hybrid.py decide por produto: aceita a predição do Sklearn se a confiança passa do threshold, senão acumula em lotes para a LLM. O threshold é o botão de ajuste entre custo e qualidade:
| Threshold | Efeito |
|---|---|
| 0.50 | Aceita mais predições locais → menos IA, mais barato, menor precisão |
| 0.75 (padrão) | Equilíbrio entre custo e qualidade |
| 0.95 | Só aceita predições muito confiantes → mais IA, mais caro, maior precisão |
Quanto cada threshold resolve localmente depende do mix de produtos do lote, não é um percentual fixo.
Resultados
Saída real de uma execução sobre o backlog de produtos não categorizados:
📊 Resumo Categorização
Modo HÍBRIDO
Total processado 164
Sklearn 80 (49%)
IA 84 (51%)
Cat adicionada 98
Marca adicionada 84
Sem categoria 66
Cat novas 34
Erros 1
Medindo 4 execuções seguidas (764 produtos do backlog), o modelo local resolveu ~36% a custo zero e o restante foi para a LLM, uma economia de custo de ~1,5× neste cenário.
⚠️ Importante: esse teste consome o backlog acumulado de casos difíceis (a cauda longa que sobrou sem categoria), o pior cenário para o modelo local. A fração resolvida cai a cada execução (49% → 37% → 27% → 32%) conforme os fáceis são esgotados. Em produção contínua, com produtos comuns chegando do crawler e retreino frequente, a fração local tende a ser maior, mas isso depende do mix real de cada dia.
Lições
- Nem todo problema de classificação precisa de LLM. Um TF-IDF + classificador linear treinado nos próprios dados resolve boa parte dos casos em menos de 1ms e a custo zero, quanto mais comum o produto, maior a fatia que ele cobre.
- A LLM brilha como fallback, não como primeira linha: ela cuida dos casos ambíguos, genéricos ou novos que o modelo local nunca viu.
- Modelo local melhora com o tempo: cada produto categorizado (por IA ou revisão) vira dado de treino para o próximo
train_category_brand_model, reduzindo progressivamente a dependência da LLM.