communitynotesbr / README.md
histlearn's picture
Update README.md
2ac97d5 verified
---
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:
1. **New Space**.
2. SDK: **Gradio**.
3. 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).
4. 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:
```bash
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 para `fold_01_adapter/` e `fold_01_head.pt` **ou** edite
> `config.py` para apontar para os nomes reais.
### 3. Commitar e subir
Spaces são repositórios git hospedados no HF. Dentro da pasta clonada do Space:
```bash
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 se `BAAI/bge-m3` virar gated no futuro.
Hoje o modelo é público, então você pode ignorar.
### 5. Primeiro boot
Na primeira inicialização o Space:
1. Instala `requirements.txt` (~1 min).
2. Baixa `BAAI/bge-m3` da HF (~2 GB, ~30–60 s).
3. Carrega adapter + head (~5 s).
4. 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)
```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()` por `load_models()` devolvendo uma lista
de pares `(encoder, head)`. `predict_batch` itera, 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 um `artifacts/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 `helpful` mede **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\n**Platt scaling** com base nos parâmetros do `config.py` para melhorar a calibração.\n