Spaces:
Running on Zero
Running on Zero
Add BERTimbau NER Gradio app for pt-ner
Browse files- README.md +50 -13
- app.py +68 -0
- requirements.txt +3 -0
README.md
CHANGED
|
@@ -1,13 +1,50 @@
|
|
| 1 |
-
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom: blue
|
| 5 |
-
colorTo: green
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: pt-ner BERTimbau NER
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.50.0
|
| 8 |
+
app_file: app.py
|
| 9 |
+
short_description: Portuguese NER API for the pt-ner application
|
| 10 |
+
python_version: "3.12"
|
| 11 |
+
startup_duration_timeout: 30m
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# pt-ner BERTimbau NER Space
|
| 15 |
+
|
| 16 |
+
Public HTTP API backing [pt-ner](https://github.com/sillohq/starter): Portuguese named-entity recognition with [`marquesafonso/bertimbau-large-ner-total`](https://huggingface.co/marquesafonso/bertimbau-large-ner-total).
|
| 17 |
+
|
| 18 |
+
## Deploy
|
| 19 |
+
|
| 20 |
+
Create the Space (ZeroGPU, public):
|
| 21 |
+
|
| 22 |
+
```bash
|
| 23 |
+
hf repos create YOUR_USER/pt-ner-bertimbau --type space --space-sdk gradio \
|
| 24 |
+
--flavor zero-a10g --public --exist-ok
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
Upload this directory:
|
| 28 |
+
|
| 29 |
+
```bash
|
| 30 |
+
hf upload space/. --repo-type space --repo YOUR_USER/pt-ner-bertimbau \
|
| 31 |
+
--exclude "**/__pycache__/**"
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
When the Space is running, set in the pt-ner app (Docker `.env`):
|
| 35 |
+
|
| 36 |
+
```bash
|
| 37 |
+
NER_BACKEND=space
|
| 38 |
+
NER_SPACE_URL=https://YOUR_USER-pt-ner-bertimbau.hf.space
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
## API
|
| 42 |
+
|
| 43 |
+
Gradio exposes `predict(text) -> list[entity]`. Each entity has:
|
| 44 |
+
|
| 45 |
+
- `label` — entity type
|
| 46 |
+
- `text` — surface form
|
| 47 |
+
- `start` / `end` — character offsets in the input text
|
| 48 |
+
- `score` — model confidence
|
| 49 |
+
|
| 50 |
+
The pt-ner application calls this endpoint over HTTP; it does not load transformers locally.
|
app.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Gradio ZeroGPU Space — Portuguese NER API for pt-ner."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import spaces
|
| 6 |
+
import gradio as gr
|
| 7 |
+
import torch
|
| 8 |
+
from transformers import pipeline
|
| 9 |
+
|
| 10 |
+
MODEL_ID = "marquesafonso/bertimbau-large-ner-total"
|
| 11 |
+
|
| 12 |
+
ner = pipeline(
|
| 13 |
+
"ner",
|
| 14 |
+
model=MODEL_ID,
|
| 15 |
+
aggregation_strategy="simple",
|
| 16 |
+
device=0,
|
| 17 |
+
torch_dtype=torch.bfloat16,
|
| 18 |
+
)
|
| 19 |
+
ner.model.to("cuda")
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _serialize(raw: list[dict]) -> list[dict]:
|
| 23 |
+
entities: list[dict] = []
|
| 24 |
+
for item in raw:
|
| 25 |
+
start = int(item["start"])
|
| 26 |
+
end = int(item["end"])
|
| 27 |
+
entities.append(
|
| 28 |
+
{
|
| 29 |
+
"label": str(item.get("entity_group") or item.get("entity") or "MISC"),
|
| 30 |
+
"text": str(item.get("word") or ""),
|
| 31 |
+
"start": start,
|
| 32 |
+
"end": end,
|
| 33 |
+
"score": float(item["score"]) if item.get("score") is not None else None,
|
| 34 |
+
}
|
| 35 |
+
)
|
| 36 |
+
return entities
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@spaces.GPU(duration=120)
|
| 40 |
+
def predict(text: str) -> list[dict]:
|
| 41 |
+
"""Extract named entities from Portuguese text with character offsets."""
|
| 42 |
+
cleaned = (text or "").strip()
|
| 43 |
+
if not cleaned:
|
| 44 |
+
return []
|
| 45 |
+
return _serialize(ner(cleaned))
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
EXAMPLES = [
|
| 49 |
+
["João Silva mora em Lisboa."],
|
| 50 |
+
["Maria Santos trabalha no Banco de Portugal, no Porto."],
|
| 51 |
+
]
|
| 52 |
+
|
| 53 |
+
demo = gr.Interface(
|
| 54 |
+
fn=predict,
|
| 55 |
+
inputs=gr.Textbox(label="Texto", lines=6, placeholder="Cole texto em português…"),
|
| 56 |
+
outputs=gr.JSON(label="Entidades"),
|
| 57 |
+
title="BERTimbau NER — pt-ner",
|
| 58 |
+
description=(
|
| 59 |
+
"API Space for the pt-ner application. "
|
| 60 |
+
f"Model: [{MODEL_ID}](https://huggingface.co/{MODEL_ID})."
|
| 61 |
+
),
|
| 62 |
+
examples=EXAMPLES,
|
| 63 |
+
cache_examples=True,
|
| 64 |
+
cache_mode="lazy",
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
if __name__ == "__main__":
|
| 68 |
+
demo.launch(mcp_server=True)
|
requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
transformers
|
| 2 |
+
accelerate
|
| 3 |
+
sentencepiece
|