Token Classification
Stanza
ONNX
Chagatai
sentence-boundary-detection
sentence-segmentation
charlm
bilstm
low-resource-nlp
historical-linguistics
chagatai
turkic
Eval Results (legacy)
Instructions to use chagatai-project/chagatai-sentence-segmentation with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Stanza
How to use chagatai-project/chagatai-sentence-segmentation with Stanza:
import stanza stanza.download("chagatai-sentence-segmentation") nlp = stanza.Pipeline("chagatai-sentence-segmentation") - Notebooks
- Google Colab
- Kaggle
Download inference.py from chagatai-project/chagatai-sentence-segmentation: direct link, hf CLI and curl.
- Browser
- Download file 5.37 kB
-
https://huggingface.co/chagatai-project/chagatai-sentence-segmentation/resolve/main/inference.py
- Command line
-
hf download hf://chagatai-project/chagatai-sentence-segmentation/inference.py
-
curl -L -o inference.py https://huggingface.co/chagatai-project/chagatai-sentence-segmentation/resolve/main/inference.py
5.37 kB
| """ | |
| Chagatai Sentence Boundary Detection (SBD) — Standalone ONNX Inference Engine. | |
| Zero-dependency inference module (requires only `numpy` and `onnxruntime`). | |
| Supports standalone SOTA and unified multi-model ensembles packaged into single .onnx files. | |
| """ | |
| import json | |
| import re | |
| from pathlib import Path | |
| from typing import List, Tuple, Dict, Any, Union | |
| import numpy as np | |
| import onnxruntime as ort | |
| NEWLINE_WHITESPACE_RE = re.compile(r'\n[\s\u0080-\u009f]*\n') | |
| WHITESPACE_RE = re.compile(r'[\s\u0080-\u009f]') | |
| NUMERIC_RE = re.compile(r'^[\d]+([,\.]+[\d]+)*[,\.]*$') | |
| class ChagataiSBD: | |
| """ | |
| High-performance, dependency-free Sentence Boundary Detector for Chagatai text. | |
| Loads a single unified ONNX model (either standalone or soft-voting ensemble) | |
| and predicts sentence boundaries directly on unsegmented text. | |
| """ | |
| def __init__( | |
| self, | |
| model_path: Union[str, Path] = "models/onnx/chagatai_sbd_tri_hybrid_stanza.onnx", | |
| vocab_path: Union[str, Path] = "models/onnx/vocab.json", | |
| providers: List[str] = None | |
| ): | |
| model_path = Path(model_path) | |
| vocab_path = Path(vocab_path) | |
| if not model_path.exists(): | |
| raise FileNotFoundError(f"Model file not found: {model_path}") | |
| if not vocab_path.exists(): | |
| raise FileNotFoundError(f"Vocab file not found: {vocab_path}") | |
| # Load vocabulary mapping | |
| with open(vocab_path, "r", encoding="utf-8") as f: | |
| v_data = json.load(f) | |
| self.unit2id: Dict[str, int] = v_data["unit2id"] | |
| self.pad_id: int = v_data["pad_id"] | |
| self.unk_id: int = v_data["unk_id"] | |
| # Initialize ONNX Runtime session | |
| if providers is None: | |
| providers = ['CPUExecutionProvider'] | |
| self.session = ort.InferenceSession(str(model_path), providers=providers) | |
| def _preprocess_chunk(self, chunk: str) -> Tuple[List[str], np.ndarray, np.ndarray]: | |
| # Substitute non-standard whitespaces | |
| chars = [WHITESPACE_RE.sub(' ', c) for c in chunk] | |
| # Collapse consecutive spaces (matches Stanza pipeline) | |
| filtered_chars: List[str] = [] | |
| for i, c in enumerate(chars): | |
| if i > 0 and c == ' ' and filtered_chars[-1] == ' ': | |
| continue | |
| filtered_chars.append(c) | |
| N = len(filtered_chars) | |
| char_ids = np.zeros(N + 1, dtype=np.int64) | |
| feats = np.zeros((N + 1, 5), dtype=np.float32) | |
| for i, c in enumerate(filtered_chars): | |
| char_ids[i] = self.unit2id.get(c, self.unk_id) | |
| feats[i, 0] = 1.0 if c.startswith(' ') else 0.0 | |
| feats[i, 1] = 1.0 if c[0].isupper() else 0.0 | |
| feats[i, 2] = 1.0 if NUMERIC_RE.match(c) else 0.0 | |
| feats[i, 3] = 1.0 if i == (N - 1) else 0.0 | |
| feats[i, 4] = 1.0 if i == 0 else 0.0 | |
| char_ids[N] = self.pad_id | |
| feats[N, :] = 0.0 | |
| return filtered_chars, char_ids, feats | |
| def segment(self, text: str) -> List[str]: | |
| """ | |
| Segment a document or multi-line text into individual sentences. | |
| Args: | |
| text: Raw Chagatai text string. | |
| Returns: | |
| List of segmented sentences. | |
| """ | |
| if not text or not text.strip(): | |
| return [] | |
| chunks = [pt.rstrip() for pt in NEWLINE_WHITESPACE_RE.split(text.rstrip()) if pt.rstrip()] | |
| all_sentences: List[str] = [] | |
| for chunk in chunks: | |
| filtered_chars, char_ids, feats = self._preprocess_chunk(chunk) | |
| N = len(filtered_chars) | |
| if N == 0: | |
| continue | |
| # Run ONNX inference | |
| c_input = char_ids[np.newaxis, :] # shape: [1, N + 1] | |
| f_input = feats[np.newaxis, :] # shape: [1, N + 1, 5] | |
| p_sent, is_tok, is_eos = self.session.run( | |
| None, | |
| {'char_ids': c_input, 'feats': f_input} | |
| ) | |
| eos_mask = is_eos[0, :N].copy() | |
| # Guarantee last non-space character in paragraph closes the sentence | |
| eos_mask[-1] = True | |
| curr_sent = [] | |
| for c, is_end in zip(filtered_chars, eos_mask): | |
| curr_sent.append(c) | |
| if is_end: | |
| s_str = "".join(curr_sent).strip() | |
| if s_str: | |
| all_sentences.append(s_str) | |
| curr_sent = [] | |
| if curr_sent: | |
| s_str = "".join(curr_sent).strip() | |
| if s_str: | |
| all_sentences.append(s_str) | |
| return all_sentences | |
| if __name__ == '__main__': | |
| # Smoke test on a sample Chagatai paragraph | |
| sbd = ChagataiSBD("models/onnx/chagatai_sbd_tri_hybrid_stanza.onnx", "models/onnx/vocab.json") | |
| sample_text = ( | |
| "سلطان ابوسعید میرزا شهادت تاپقاندین سونگ سمرقند ولایتینی میرزا احمد آلدی " | |
| "و هرات ولایتینی سلطان حسین میرزا ضبط قیلدی و اندیجان ولایتینی عمرشیخ میرزا توتتی" | |
| ) | |
| print("\nOriginal text:") | |
| print(sample_text) | |
| sentences = sbd.segment(sample_text) | |
| print(f"\nDetected {len(sentences)} sentences:") | |
| for idx, s in enumerate(sentences, 1): | |
| print(f" [{idx}] {s}") | |