Download runtime.py from failed09/bashkir-roberta-ner: direct link, hf CLI and curl.
- Browser
- Download file 3.94 kB
-
https://huggingface.co/failed09/bashkir-roberta-ner/resolve/main/runtime.py
- Command line
-
hf download hf://failed09/bashkir-roberta-ner/runtime.py
-
curl -L -o runtime.py https://huggingface.co/failed09/bashkir-roberta-ner/resolve/main/runtime.py
3.94 kB
| """Standalone ONNX inference for Bashkir person-name recognition. | |
| The graph returns logits at SentencePiece positions. Training assigned one BIO | |
| label to the first piece of each original word, so this runtime does the same. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import re | |
| from pathlib import Path | |
| import numpy as np | |
| import onnxruntime as ort | |
| import sentencepiece as spm | |
| TAGS = ("O", "B-PER", "I-PER") | |
| WORD_RE = re.compile(r"[A-Za-zА-Яа-яЁёӘәҒғҘҙҠҡҢңӨөҪҫҮүҺһҚқІі][\w'\-]*", re.UNICODE) | |
| class BashkirPersonNER: | |
| def __init__(self, model_dir: str | Path = ".", precision: str = "int8", | |
| providers: list[str] | None = None): | |
| self.directory = Path(model_dir) | |
| config = json.loads((self.directory / "config.json").read_text(encoding="utf-8")) | |
| if precision not in config["models"]: | |
| raise ValueError(f"precision must be one of {sorted(config['models'])}") | |
| self.max_length = int(config["max_position_embeddings"]) | |
| self.bos_id = int(config["bos_token_id"]) | |
| self.eos_id = int(config["eos_token_id"]) | |
| self.sp = spm.SentencePieceProcessor( | |
| model_file=str(self.directory / "spm_bashkir_bert_16k.model") | |
| ) | |
| model = self.directory / config["models"][precision] | |
| self.session = ort.InferenceSession( | |
| str(model), providers=providers or ["CPUExecutionProvider"] | |
| ) | |
| def predict_tokens(self, tokens: list[str]) -> tuple[list[str], bool]: | |
| """Return word-level BIO tags and whether input exceeded the context limit. | |
| The caller supplies the same token boundaries used for gold evaluation. | |
| On truncation, the returned tag list covers only its leading words. | |
| """ | |
| if not tokens: | |
| return [], False | |
| ids = [self.bos_id] | |
| positions = [] | |
| for word in tokens: | |
| pieces = list(self.sp.encode(word, out_type=int)) or [self.sp.unk_id()] | |
| if len(ids) + len(pieces) + 1 > self.max_length: | |
| break | |
| positions.append(len(ids)) | |
| ids.extend(pieces) | |
| if not positions: | |
| return [], True | |
| ids.append(self.eos_id) | |
| logits = self.session.run( | |
| ["logits"], {"input_ids": np.asarray([ids], dtype=np.int64)} | |
| )[0][0] | |
| return [TAGS[int(logits[pos].argmax())] for pos in positions], len(positions) < len(tokens) | |
| def predict(self, text: str) -> dict: | |
| """Return person spans with character offsets into the original text.""" | |
| if not isinstance(text, str): | |
| raise TypeError("text must be a string") | |
| matches = list(WORD_RE.finditer(text)) | |
| tags, truncated = self.predict_tokens([m.group() for m in matches]) | |
| entities = [] | |
| start = None | |
| end = None | |
| for match, tag in zip(matches, tags): | |
| if tag == "B-PER": | |
| if start is not None: | |
| entities.append({"text": text[start:end], "start": start, "end": end}) | |
| start, end = match.span() | |
| elif tag == "I-PER": | |
| # Current benchmark convention: an orphan I-PER starts a span. | |
| if start is None: | |
| start = match.start() | |
| end = match.end() | |
| elif start is not None: | |
| entities.append({"text": text[start:end], "start": start, "end": end}) | |
| start = end = None | |
| if start is not None: | |
| entities.append({"text": text[start:end], "start": start, "end": end}) | |
| return {"entities": entities, "truncated": truncated} | |
| if __name__ == "__main__": | |
| import sys | |
| if hasattr(sys.stdout, "reconfigure"): | |
| sys.stdout.reconfigure(encoding="utf-8") | |
| ner = BashkirPersonNER(Path(__file__).resolve().parent) | |
| for sample in sys.argv[1:]: | |
| print(json.dumps(ner.predict(sample), ensure_ascii=False)) | |