Spaces:
Running
A newer version of the Gradio SDK is available: 6.15.2
title: Notinhas Endpoint
emoji: 📝
colorFrom: green
colorTo: gray
sdk: gradio
sdk_version: 5.50.0
python_version: '3.10'
app_file: app.py
pinned: false
short_description: Classificador de utilidade para community notes em PT-BR.
models:
- BAAI/bge-m3
Notinhas — endpoint de utilidade (FT-Solo)
Endpoint privado do modelo FT-Solo do projeto: dado o texto de uma community
note em português, devolve a probabilidade de ela ser classificada como "útil"
(label_binary_strict = 1), junto com uma leitura opcional da contribuição de
cada palavra.
Arquitetura: bge-m3 (568M params) + LoRA + cabeça linear (Ensemble e Calibração), idêntica ao
predict_from_text do notebook FT-Solo em modo fiel
(fold 01).
Estrutura do repositório
.
├── app.py # Gradio UI + API (abas Prever / Explicar / Sobre)
├── inference.py # Loader + predict + explain (occlusion word-level)
├── config.py # Constantes (modelo, prompt, paths, thresholds)
├── requirements.txt # Dependências Python
├── README.md # Este arquivo (com YAML header lido pelo HF)
├── .gitignore
└── artifacts/ # ← você popula isso (veja a seção Setup)
├── fold_01_adapter/ # Pasta do adapter LoRA
│ ├── adapter_config.json
│ └── adapter_model.safetensors
└── fold_01_head.pt # State dict do nn.Linear(1024, 1)
Setup — do zero até o Space no ar
1. Criar o Space (privado)
Na UI do Hugging Face:
- New Space.
- SDK: Gradio.
- Hardware: T4 small (recomendado — caber na memória em bf16 e inferência em ~0,5 s). A10G small dá latência ainda menor. ZeroGPU funciona mas com cold-start mais longo. CPU roda — inferência ~4–8 s com bge-m3 (vs 20–40 s do Qwen3).
- Visibility: Private.
2. Popular artifacts/
Os pesos vêm do pipeline do projeto. O zip base do Drive (artefatos_projeto.zip)
traz as pastas com adapters e heads bge-m3. Rode localmente:
pip install gdown
gdown "https://drive.google.com/uc?id=1_wCCxZG25tcGIVHgrdfOj54vI5Iw6MUF" \
-O artefatos_projeto.zip
unzip -q artefatos_projeto.zip -d _raw/
# Estrutura esperada pelo Space:
mkdir -p artifacts
cp -r _raw/qwen4b_adapters/fold_01_adapter artifacts/
cp _raw/qwen4b_heads/fold_01_head.pt artifacts/
Qual fold usar? O notebook escolhe dinamicamente o "melhor fold" via
qwen4b_ftsolo_manifest.json. Para servir em produção é coerente reusar o mesmo. Se o manifesto apontar para outro fold (digamos,fold_03), renomeie os arquivos acima parafold_01_adapter/efold_01_head.ptou editeconfig.pypara apontar para os nomes reais.
3. Commitar e subir
Spaces são repositórios git hospedados no HF. Dentro da pasta clonada do Space:
git lfs install # safetensors > 10 MB usam LFS
git lfs track "*.safetensors"
git lfs track "*.pt"
git add .gitattributes artifacts/
git add app.py inference.py config.py requirements.txt README.md .gitignore
git commit -m "feat: endpoint inicial FT-Solo"
git push
O adapter bge-m3 em LoRA costuma ficar entre 20 e 60 MB (dependendo do rank e dos módulos-alvo). A cabeça é ~20 KB. Tudo cabe confortavelmente sem apertar quota.
4. (Opcional) Secrets
Em Settings → Variables and secrets:
HF_TOKEN— só necessário seBAAI/bge-m3virar gated no futuro. Hoje o modelo é público, então você pode ignorar.
5. Primeiro boot
Na primeira inicialização o Space:
- Instala
requirements.txt(~1 min). - Baixa
BAAI/bge-m3da HF (~2 GB, ~30–60 s). - Carrega adapter + head (~5 s).
- Fica pronto — e o warm-up do modelo já aconteceu, o primeiro request é rápido.
Acompanhe pela aba Logs do Space.
Uso
Via UI web
Basta acessar a URL privada do Space. Três abas:
- Prever — score + label + faixa de confiança.
- Explicar — o mesmo + texto com destaque por contribuição de palavra, mais uma tabela dos top 5 tokens de cada lado.
- Sobre — detalhes técnicos e limitações.
Via gradio_client (Python)
from gradio_client import Client
client = Client("<seu-usuario>/<nome-do-space>", hf_token="hf_...")
# Só a probabilidade
card_html, payload = client.predict(
"Segundo o Ministério da Saúde, o número é falso. Fonte: https://...",
api_name="/predict",
)
print(payload)
# {'proba_util': 0.87, 'label': 'Útil', 'confidence_band': 'Alta'}
# Com explicação
card, highlight_html, tokens_html, full = client.predict(
"Essa nota é claramente desnecessária, opinião pessoal.",
api_name="/explain",
)
print(full["tokens"][:3], full["contributions"][:3])
Via HTTP puro
Gradio 5 expõe as rotas em /gradio_api/call/<api_name>. Veja a doc oficial
em https://<seu-space>.hf.space/?view=api — o próprio Space gera a documentação
e exemplos de curl para os dois endpoints.
Arquitetura e decisões
Por que este stack
- Gradio 5 em vez de FastAPI puro: entrega UI + HTTP API de uma vez, com doc automática. Para um endpoint privado de MVP, dobrar a utilidade sem dobrar o código é o trade certo.
- Occlusion em vez de SHAP Partition: o notebook gasta 12–15 s/nota em SHAP
textual, justamente porque explora combinações de subconjuntos. Para servir
em tempo real, leave-one-out por palavra dá um
Δpor token em ~N+1 forward passes — 1 a 2 s para notas típicas, resultado visualmente comparável. - Fold único: o notebook também usou fold único para SHAP textual. Ensemble
dos 5 folds é a extensão natural (listar
[(adapter_i, head_i) for i in 1..5], mediar as sigmóides), mas não é obrigatório para o MVP.
O que muda se você quiser escalar
- Ensemble: substituir
load_model()porload_models()devolvendo uma lista de pares(encoder, head).predict_batchitera, mediana ou média das probabilidades. Dobra VRAM e latência — só vale quando a performance marginal justificar. - Vizinhos semânticos (como na seção 5 do notebook): exige embutir
embeddings_qwen3_4b_finetuned.npz(≈200 MB) e o dataset mestre para recuperar texto + label. É uma extensão natural — crie umartifacts/knn_index/com FAISS e adicione uma aba "Vizinhos" ao Gradio. - Inference Endpoint dedicado: se o Space virar gargalo, o mesmo repositório de código pode ser deployado como Inference Endpoint pago da HF, que aguenta paralelismo real e autoescala.
Limitações
- O rótulo
helpfulmede aceitabilidade bipartidária, não qualidade editorial — o notebook exemplifica casos em que vizinhos semânticos idênticos recebem rótulos opostos por razões políticas, não textuais. - Textos longos são truncados em 256 tokens.
- Predições são dependentes do fold servido; o notebook observou variação pequena mas não nula entre folds.
Créditos
Baseado no pipeline e no notebook de explicabilidade do projeto Notinhas.
O código aqui é o protótipo funcional da função predict_from_text virado serviço.
\n## Calibração e Ensemble\nEste Space carrega múltiplos folds como um ensemble, calculando a média das\nprobabilidades de 5 versões do modelo adaptado (LoRA + Cabeça Linear) para aumentar\na robustez da classificação. Além disso, as probabilidades passam por\nPlatt scaling com base nos parâmetros do config.py para melhorar a calibração.\n