--- 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("/", 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/`. Veja a doc oficial em `https://.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