IntenBERT v2.0 — Documentação de Arquitetura e Capacidades

IntenBERT v2.0 — Documentação de Arquitetura e Capacidades

Modelo Base: BERTimbau (neuralmind/bert-base-portuguese-cased)
Domínio: Classificação de Intenção
Idioma: Português Brasileiro (PT-BR)
Formato de Produção: ONNX Runtime (CPU)
Parâmetros: 110M
Latência: ~50ms por mensagem em CPU


1. Arquitetura do Modelo

1.1 Backbone: BERTimbau

O modelo utiliza BERTimbau (Souza et al., 2020) como backbone, uma variação do BERT pré-treinada exclusivamente em corpora de português brasileiro. Diferente do BERT multilingual que divide capacidade entre 104 idiomas, o BERTimbau concentra toda a representação em ~3.5 bilhões de tokens de PT-BR (Wikipedia, web crawls, livros digitais, notícias).

Configuração arquitetural:

Componente Valor
Hidden size 768
Attention heads 12
Transformer layers 12
Intermediate (FFN) 3072
Max sequence length 128
Vocabulary 29.7K tokens (WordPiece case-sensitive)
Parâmetros totais ~110M

A escolha de case-sensitive é deliberada: em WhatsApp Business, capitalização carrega sinal. "Bom dia" (saudação formal) tem semântica diferente de "bom dia" (dentro de frase). O tokenizador preserva maiúsculas, permitindo que o modelo aprenda essas distinções.

1.2 Head de Classificação

Sobre a saída do [CLS] do BERT, adicionamos uma camada densa de 768 → 4 unidades (uma por classe), sem ativação intermediária. A saída bruta (logits) alimenta uma Softmax durante treinamento (Cross Entropy) e inferência.

Input tokens → BERTimbau Encoder → [CLS] pooling → Linear(768, 4) → Softmax → Probabilidades

A saída é um vetor de probabilidade sobre as 4 classes:

  • MACHINE_BOT: mensagem de URA, bot, protocolo, fila de espera
  • HUMAN_POSITIVE: intenção explícita ou implícita de compra
  • HUMAN_NEGATIVE: recusa, solicitação de remoção, spam
  • HUMAN_INVALID: saudação, conversa fiada, curiosidade sem intenção comercial

2. Treinamento

2.1 Pipeline de Dados

O treinamento não usa dados reais (sensíveis sob LGPD). Em vez disso, construímos um pipeline de geração sintética controlada via LLM (Qwen2.5-7B-Instruct), seguido de deduplicação semântica e filtragem de qualidade.

Arquitetura do pipeline de dados:

Prompts de geração (few-shot por classe)
         ↓
Qwen2.5-7B-Instruct (vLLM inference)
         ↓
Raw dataset (~15K exemplos)
         ↓
Filtragem heurística (gibberish detection)
         ↓
Deduplicação semântica (embeddings all-MiniLM-L6-v2, cosine < 0.85)
         ↓
Dataset final (~12K exemplos)
         ↓
Split 80/10/10 (treino/valid/teste)

2.2 Engenharia de Prompts para Geração

Cada classe tem um prompt específico com 4 componentes:

  1. Contexto: "Você está simulando mensagens de WhatsApp Business..."
  2. Definição semântica: o que caracteriza a classe
  3. Few-shot examples: 3-5 exemplos concretos
  4. Constraints de diversidade: "inclua gírias, erros de digitação, regionalismos, mensagens curtas e longas"

Exemplo de prompt para HUMAN_POSITIVE:

Gere 80 mensagens curtas de WhatsApp de pessoas querendo COMPRAR,
pedir PREÇO ou fazer COTAÇÃO. Varie entre:
- Diretas: "Quanto custa?"
- Com contexto: "Bom dia! Gostaria de fazer uma cotação."
- Compra direta sem "preço": "Tenho interesse", "Quero comprar"
- Gírias: "Mano, manda o link aí que eu pago"
- Erros de digitação: "qnt custa?", "qr comprar"

2.3 Hard Negatives

O diferencial do dataset é a inclusão de casos adversariais (hard negatives) — exemplos que parecem pertencer a uma classe mas na verdade pertencem a outra. Isso força o modelo a aprender fronteiras de decisão mais nítidas:

Tipo de Hard Negative Exemplo Classe Real
Elogio sem intenção "Gostei muito do produto de vocês" INVALID
Compra para terceiro "Meu chefe pediu pra eu cotar" POSITIVE
Curiosidade com preço "Só estou pesquisando preços" INVALID
Rejeição branda "Vou deixar quieto, vlw" INVALID
Formalidade enganosa "Cordialmente, solicito retorno" INVALID
Compra direta implícita "Tenho interesse sim" POSITIVE

2.4 Hiperparâmetros de Treinamento

Parâmetro Valor Justificativa
Learning rate 2e-5 Padrão BERT fine-tuning; maior que isso causa instabilidade
Batch size 64 (train) / 128 (eval) Máximo que cabe em H100 com mixed precision desabilitado
Epochs 8 Convergência observada após 6; 8 garante generalização
Weight decay 0.01 Regularização L2 leve para evitar overfit no dataset sintético
Warmup 0% Simplificado para estabilidade; LR constante desde o início
Max sequence length 128 99% das mensagens de WhatsApp têm < 50 tokens
FP16 Desabilitado Prevenção de NaN observados em testes preliminares
Loss Cross Entropy (label_smoothing=0) Focal Loss testada mas causou instabilidade numérica
Seed 42 Reprodutibilidade completa

2.5 Infraestrutura de Treinamento

  • Hardware: NVIDIA H100 80GB (Lightning AI VPS)
  • Framework: PyTorch 2.4.0 + Transformers 4.49.0 + Accelerate
  • Otimizador: AdamW (monkey-patched para compatibilidade com accelerate 0.33)
  • Dataset caching: Parquet para evitar regeneração entre runs
  • Tempo de treinamento: ~45 minutos para 8 epochs com ~12K exemplos

2.6 Estabilidade e Convergência

O treinamento apresentou curvas estáveis:

  • Loss de treino: decaimento monotônico até ~0.15
  • Loss de validação: plateau após epoch 6, sem overfit severo
  • Acurácia de validação: ~82% (ligeriamente otimista devido a distribuição sintética)
  • Grad norm: estável, sem explosões

Não houve instâncias de NaN loss após a desabilitação de FP16 e a troca de ModernBERT para BERTimbau.


3. Exportação para ONNX

3.1 Motivação Arquitetural

O modelo PyTorch tem dependências pesadas (torch 2.4.0, CUDA opcional, 2GB+ de libs). Para deploy em produção, convertemos para ONNX Runtime:

  • Portabilidade: ONNX é formato padrão, roda em qualquer linguagem
  • Performance: ONNX Runtime aplica otimizações de grafo (constant folding, dead code elimination, kernel fusion)
  • Tamanho: 400MB (mesmos pesos, mas sem overhead do PyTorch)
  • Latência: ~50ms por mensagem em CPU moderna vs ~150ms PyTorch puro

3.2 Processo de Conversão

from optimum.onnxruntime import ORTModelForSequenceClassification

model = ORTModelForSequenceClassification.from_pretrained(
    "lead_model_final",
    export=True,
    provider="CPUExecutionProvider"
)
model.save_pretrained("lead_model_onnx/")

O optimum da Hugging Face automatiza:

  1. Exportação do grafo PyTorch → ONNX
  2. Validação de equivalência (comparação de outputs em 100+ amostras)
  3. Otimização do grafo (simplificação, fusão de operações)

3.3 Validação de Equivalência

Comparamos logits PyTorch vs ONNX em 100 mensagens aleatórias:

  • Diferença máxima absoluta: < 1e-5
  • Diferença relativa: < 1e-7
  • Discrepância de classe: 0% (mesma predição em todos os casos)

A equivalência é perfeita dentro da precisão float32.


4. Capacidades e Comportamento do Modelo

4.1 Representação Interna

O BERTimbau aprende representações contextuais densas (768-D) onde:

  • Mensagens com intenção comercial clusterizam em regiões distintas
  • Saudações e conversa fiada ocupam espaço vetorial separado
  • Tokens de ação ("comprar", "preço", "cotar") ativam neurônios específicos na última camada

A análise de atenção (attention weights) revela que o modelo foca fortemente em:

  • Verbos de intenção: "quero", "preciso", "gostaria"
  • Substantivos comerciais: "preço", "cotação", "desconto"
  • Padrões de recusa: "não quero", "parem", "remover"
  • Padrões de bot: "protocolo", "fila", "opção", "aguarde"

4.2 Calibração de Confiabilidade

O modelo não está bem calibrado. Em análise de confiança:

Métrica Valor
Confiança média (acertos) 97.75%
Confiança média (erros) 95.00%
Erros com confiança > 80% 89.3% (25/28)

Isso significa que quando o modelo erra, ele ainda afirma ter 95% de certeza. Em produção, isso exige:

  • Threshold adaptativo: não confiar cegamente em scores > 90%
  • Fallback humano: rotear mensagens de alta confiança mas baixa certeza contextual para revisão
  • Temperature scaling: ajuste posterior dos logits para melhorar calibração

4.3 Robustez por Comprimento de Texto

Comprimento Comportamento Exemplo Problemático
1-2 palavras Tendência a INVALID "preço" → INVALID (deveria ser POSITIVE)
3-5 palavras Melhora, mas ainda fraco "qnt custa?" → INVALID (deveria ser POSITIVE)
6-15 palavras Zona de melhor performance "Bom dia! Gostaria de fazer uma cotação." → POSITIVE
15+ palavras Pode confundir com INVALID "Olá! Gostaria de fazer uma cotação. Ah não, esquece." → POSITIVE (deveria ser INVALID)

O modelo tem déficit em textos muito curtos porque o dataset de treino enfatiza mensagens completas. Palavras isoladas como "preço", "comprar", "não" perdem contexto.

4.4 Robustez por Registro Linguístico

Tipo de Texto Performance Nota
Português formal Alta (90%+) "Gostaria de solicitar uma cotação"
Português informal/gírias Média (70%) "Mano, manda o link aí"
Erros de digitação Baixa (50%) "qnt custa?", "qr comprar"
Inglês puro Muito baixa "Hello, I want to buy" → INVALID
Código-misturado (pt+en) Média-baixa "Hi, quanto custa?" → funciona; "Hello, I want to buy" → falha
Emojis sozinhos Alta (correta) "👍" → INVALID

O modelo é monolíngue por design — o BERTimbau nunca viu inglês durante pré-treinamento. Mensagens 100% em inglês são mapeadas para a distribuição mais provável do PT-BR (frequentemente INVALID).

4.5 Comportamento por Classe

MACHINE_BOT (melhor classe):

  • Precision: 86.67%, Recall: 92.86%
  • Sinais fortes: "protocolo", "fila de espera", "digite 1", "avaliação"
  • Risco: mensagens formais humanas confundidas com bot (raro)

HUMAN_POSITIVE (classe mais fraca):

  • Precision: 73.68%, Recall: 53.85%
  • Problema: 42.3% dos positivos reais são classificados como INVALID
  • Causa raiz: o treino enfatiza "pedir preço" como sinal de positivo, mas não captura intenção implícita ("tenho interesse", "quero comprar")

HUMAN_NEGATIVE (baixo recall):

  • Precision: 100.00%, Recall: 50.00%
  • Problema: 45.5% dos negativos vão para INVALID
  • Causa raiz: mensagens curtas de recusa ("não", "mt caro", "ja comprei") são mapeadas como INVALID

HUMAN_INVALID (classe dominante):

  • Precision: 75.00%, Recall: 94.03%
  • Comportamento: modelo "joga para o lado seguro"
  • Risco: falso positivo em INVALID reduz a descoberta de leads reais

5. Benchmarking e Avaliação Profissional

5.1 Stress Test Adversarial

Construímos um conjunto de 129 casos adversariais cobrindo:

  • Edge cases físicos: strings vazias, emojis, números, caracteres especiais
  • Saudações: 9 variações de "oi" a "salve"
  • Respostas curtas: "ok", "sim", "não", "vlw"
  • Erros de digitação: 11 casos com abreviações e erros ortográficos
  • Gírias: 8 variações regionais
  • Misturas: mensagens com múltiplas intenções
  • Ambiguidades: intenção implícita, curiosidade sem compra
  • Multi-língue: 5 mensagens em inglês/espanhol misturado

5.2 Matriz de Confusão (% por classe esperada)

Esperado \ Predito BOT POS NEG INVALID
BOT 92.9% 7.1%
POS 3.9% 53.8% 42.3%
NEG 4.5% 50.0% 45.5%
INVALID 1.5% 4.5% 94.0%

5.3 Métricas de Classificação

Classe Precision Recall F1-Score Support
MACHINE_BOT 86.67% 92.86% 89.66% 14
HUMAN_POSITIVE 73.68% 53.85% 62.22% 26
HUMAN_NEGATIVE 100.00% 50.00% 66.67% 22
HUMAN_INVALID 75.00% 94.03% 83.44% 67
Macro Avg 83.84% 72.68% 75.50% 129
Weighted Avg 80.26% 78.29% 76.98% 129

5.4 Comparação com LLM Local (Liquid LFM2 1.2B)

Para estabelecer baseline de qualidade, comparamos o IntenBERT contra um LLM local via LM Studio API.

Dimensão IntenBERT (ONNX) LLM (LFM2 1.2B)
Acurácia 78.3% 40.3%
Latência (CPU) ~50ms ~2000ms
Tamanho 400MB ~3GB
RAM 1GB 6GB
Dependências ONNX Runtime LM Studio + GPU
Offline Sim Sim

Métricas detalhadas do LLM (LFM2 1.2B):

Classe Precision Recall F1-Score
MACHINE_BOT 100.00% 7.14% 13.33%
HUMAN_POSITIVE 27.91% 92.31% 42.86%
HUMAN_NEGATIVE 63.16% 54.55% 58.54%
HUMAN_INVALID 68.18% 22.39% 33.71%
Macro Avg 64.81% 44.10% 37.11%
Weighted Avg 62.66% 40.31% 37.58%
Accuracy 40.31%

Análise de confiança LLM:

  • Confiança média (acertos): 94.15% (σ=5.24%)
  • Confiança média (erros): 95.47% (σ=1.71%)
  • Erros com confiança > 80%: 77 de 77 erros (100%)

Trade-off arquitetural: o IntenBERT é 40x mais rápido, 7x menor e quase 2x mais acurado. O LLM de 1.2B parâmetros apresenta viés catastrófico para HUMAN_POSITIVE: classifica 65.7% dos INVALID e 92.9% dos BOT como positivos, com precision de apenas 27.91% para a classe positiva. Isso demonstra que modelos de linguagem pequenos via prompting zero-shot não são competitivos com classificadores fine-tuned para domínios específicos — o conhecimento especializado embutido nos 110M parâmetros do BERTimbau fine-tuned supera o conhecimento generalista de 1.2B parâmetros sem adaptação de domínio.


6. Limitações Arquiteturais e Caminhos de Melhoria

6.1 Limitações Identificadas

  1. Contexto zero: o modelo classifica mensagem a mensagem, sem histórico da conversa. Uma sequência "Oi" → "Quanto custa?" não beneficia do contexto da saudação.

  2. Comprimento mínimo: textos com < 3 tokens têm representação pobre no [CLS]. O modelo precisa de contexto sintático mínimo para ativar padrões de intenção.

  3. Calibração fraca: distribuição de confiança não reflete probabilidade real de acerto. Temperature scaling ou Platt scaling são necessários para uso em decisões automatizadas.

  4. Viés para INVALID: a classe majoritária no treino (46.6%) causa tendência conservadora. O modelo prefere errar para INVALID a errar para outras classes.

  5. Monolinguismo estrito: zero capacidade em inglês puro. Mensagens 100% em inglês são classificadas com base em tokens coincidentes (ex: "buy" não existe no vocabulário PT-BR do BERTimbau).

6.2 Melhorias Arquiteturais Propostas

Prioridade Melhoria Mudança Técnica Impacto Esperado
Alta Aumentar recall de POSITIVE Oversampling + peso de classe na loss +15% recall POSITIVE
Alta Capturar intenção implícita Adicionar exemplos "comprar", "tenho interesse" ao treino Reduzir falsos INVALID
Alta Temperature scaling Ajuste de logits post-training com validação Calibração confiável
Média Ensemble de 3 modelos Votação de modelos com seeds diferentes +5-7% acurácia
Média Dados reais anotados Coleta LGPD-compliant com consentimento Fechar synthetic-to-real gap
Média Quantização INT8 ONNX Runtime INT8 (QDQ) -50% tamanho, +30% velocidade
Baixa Contexto multi-turn Concatenar últimas 3 mensagens com separadores Resolver ambiguidades temporais
Baixa DistilBERT ou MobileBERT Reduzir para 66M ou 4.3M params Inferência < 20ms

7. Conclusão

O Lead Classifier v2.0 é um sistema de classificação de intenção otimizado para o domínio de WhatsApp Business em português brasileiro. Sua arquitetura — BERTimbau fine-tuned com dados sintéticos controlados e exportado para ONNX — entrega 78.3% de acurácia em condições adversariais com latência de ~50ms em CPU, viabilizando deploy em infraestrutura modesta.

As principais forças são:

  • Alta precisão em BOT (86.67%) — útil para filtragem de URA
  • Recall elevado em INVALID (94.03%) — bom para descartar conversa fiada
  • Inferência ultrarrápida em CPU — viável para escala

Os principais pontos de atenção são:

  • Sobre-confiança (95% média mesmo nos erros)
  • Recall fraco em POSITIVE (53.85%) — leads reais podem ser perdidos
  • Tendência para INVALID — modelo conservador

Para produção, recomenda-se:

  1. Aplicar temperature scaling nos scores
  2. Definir threshold de confiança por classe (não global)
  3. Rotear mensagens de alta confiança mas classe incerta para fallback humano
  4. Coletar feedback real para re-treino contínuo

Documento gerado em 2026-06-01. Baseado em stress test de 129 casos adversariais e análise de matriz de confusão com precision, recall e F1 por classe.

Subscribe to Kodjao Labs

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Inscreva-se