communitynotesbr / README.md
histlearn's picture
Update README.md
2ac97d5 verified

A newer version of the Gradio SDK is available: 6.15.2

Upgrade
metadata
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:

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:

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)

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\nPlatt scaling com base nos parâmetros do config.py para melhorar a calibração.\n