Classifica a categoria de uma notícia brasileira a partir do título (e, opcionalmente, do texto), servido por uma API.
- Dados:
marlesson/news-of-the-site-folhauol(Folha de S.Paulo, ~167 mil notícias). - Modelos servidos:
svc(TF-IDF + LinearSVC — padrão, leve e rápido) ebert(BERTimbau fine-tunado — opcional, mais preciso). - Métrica: F1-macro (dados desbalanceados).
Os dois modelos ficam no Hugging Face Hub e são baixados automaticamente — não é preciso treinar nada para rodar a API.
docker compose up -d # sobe a API (build na 1a vez: ~5 min, baixa os modelos)
docker compose down # desligaAcesse http://127.0.0.1:8000/docs.
python -m venv .venv
.venv\Scripts\activate # Windows (Linux/Mac: source .venv/bin/activate)
pip install -r requirements.txt
uvicorn src.api:app # http://127.0.0.1:8000/docsOs modelos são baixados do Hugging Face na primeira execução.
- Para usar o
bertlocalmente:pip install torch transformers(osvcroda sem).- Para reproduzir o treino do
svcdo zero:python src/train.py— ele geramodels/model.joblib, e a API passa a usar essa cópia local (que tem prioridade sobre o Hub).
GET /health— status do serviço.POST /predict— classifica uma notícia.- Documentação interativa (Swagger): http://127.0.0.1:8000/docs
Entrada: title (obrigatório), text (opcional), modelo
(svc (default) ou bert), explicar (opcional, só svc).
# svc (rápido) + explicação
curl -X POST http://127.0.0.1:8000/predict \
-H "Content-Type: application/json" \
-d '{"title": "Palmeiras vence o clássico e assume a liderança", "explicar": true}'
# BERTimbau (mais preciso, mais lento)
curl -X POST http://127.0.0.1:8000/predict \
-H "Content-Type: application/json" \
-d '{"title": "Irã fecha o Estreito de Ormuz", "text": "...", "modelo": "bert"}'Saída:
{
"categoria": "Esporte",
"confianca": 0.32,
"top3": [
{"categoria": "Esporte", "confianca": 0.32},
{"categoria": "Cultura e Entretenimento", "confianca": 0.09},
{"categoria": "Opinião", "confianca": 0.09}
],
"palavras_influentes": [
{"palavra": "vence", "peso": 0.74},
{"palavra": "palmeiras", "peso": 0.31}
]
}
explicar: trueretorna as palavras que mais pesaram na decisão. Como o modelo é linear, a contribuição é exata (tfidf × coeficiente), sem LIME/SHAP.
Text-classification/
├── data/ # dataset (não versionado; baixado via kagglehub)
├── references/
│ └── category_map.json # mapa 48 categorias → 12 macrocategorias
├── notebooks/
│ ├── 01_eda.ipynb # análise exploratória
│ ├── 02_modeling_baseline.ipynb # TF-IDF, título
│ ├── 02_modeling_baseline_title_text.ipynb # TF-IDF, título+texto (produção)
│ └── 03_modeling_bert_kaggle.ipynb # BERTimbau (benchmark)
├── src/
│ ├── preprocessing.py # limpeza + montar_entrada (treino ↔ API)
│ ├── train.py # treina e salva models/model.joblib
│ └── api.py # FastAPI
├── Dockerfile · docker-compose.yml
└── requirements.txt
1. EDA. As 48 categorias originais são muito desbalanceadas (a maior tem 22 mil
notícias, a menor tem 1) e várias são editorias, não temas (ex.: colunas,
opiniao). Isso inviabiliza usá-las diretamente como alvo.
2. Taxonomia. Consolidamos as 48 categorias em 12 macrocategorias temáticas
(references/category_map.json) — decisão derivada da EDA, não arbitrária: o
alvo continua sendo a categoria da notícia, apenas normalizada numa taxonomia
coerente. O desbalanceamento cai de ~22.000× para ~28×.
3. Entrada. título (obrigatório) + texto (opcional), concatenados. A
limpeza (URLs, créditos de tradução, auto-referências da marca Folha) é
compartilhada entre treino e API (src/preprocessing.py), evitando train/serve skew.
4. Modelos. Mesmo split e métrica (F1-macro):
| Modelo | F1-macro | Tamanho | Latência/req (CPU) |
|---|---|---|---|
| TF-IDF + LinearSVC (produção) | 0,80 | 132 MB | ~0,7 ms |
| BERTimbau (benchmark) | 0,86 | ~440 MB | ~100–300 ms |
5. Decisão de produção. O SVC (TF-IDF + LinearSVC) é o padrão da API
(modelo: svc, ~0,7 ms/req): para o cenário de "API simples", ele entrega quase
a mesma qualidade a uma fração do custo. O BERTimbau fica disponível sob demanda
(modelo: bert) para quem prioriza acurácia — ambos no mesmo contêiner. Assim a API mostra o critério de
engenharia (qualidade → latência → tamanho) na prática: escolha certa por
padrão, sem abrir mão de servir o modelo mais forte quando faz sentido.
Avaliação. Além do F1-macro, os notebooks trazem matriz de confusão, relatório
por classe e análise qualitativa dos erros (as maiores confusões — Opinião ↔ Política — vêm justamente das editorias, confirmando o achado da EDA).