Spaces:
Running
Running
| 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 |