Initial Bashkir LID release
Browse files- META.json +14 -0
- README.md +60 -0
- __init__.py +5 -0
- lid.py +88 -0
- model.onnx +3 -0
- vectorizer.json +0 -0
META.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"id": "bashkir_lid",
|
| 3 |
+
"version": "v3",
|
| 4 |
+
"type": "language_identification",
|
| 5 |
+
"classes": ["ba", "ru"],
|
| 6 |
+
"artifacts": ["model.onnx", "vectorizer.json"],
|
| 7 |
+
"features": 100000,
|
| 8 |
+
"input_contract": "Unicode char_wb adapter; padded feature_ids and counts tensors",
|
| 9 |
+
"source_texts_distributed": false,
|
| 10 |
+
"sha256": {
|
| 11 |
+
"model.onnx": "9ddfdecf5679393da8943dc7b732ea937b5eaf4bfff2071c431cc7f9d1a0586f",
|
| 12 |
+
"vectorizer.json": "0413814d379b4e3c50fd324e5d87999642a9f267640bec9d5bd651a6f948cc38"
|
| 13 |
+
}
|
| 14 |
+
}
|
README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
language: [ba, ru]
|
| 3 |
+
license: apache-2.0
|
| 4 |
+
library_name: onnxruntime
|
| 5 |
+
tags:
|
| 6 |
+
- bashkir
|
| 7 |
+
- language-identification
|
| 8 |
+
- text-classification
|
| 9 |
+
- onnx
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# Bashkir LID
|
| 13 |
+
|
| 14 |
+
Компактная модель для определения того, является ли короткий текст
|
| 15 |
+
башкирским или русским. Модель ориентирована на фильтрацию башкирских
|
| 16 |
+
корпусов и локальные приложения, где важны скорость и небольшой размер.
|
| 17 |
+
|
| 18 |
+
## Использование
|
| 19 |
+
|
| 20 |
+
```python
|
| 21 |
+
from lid import LanguageIdentifier
|
| 22 |
+
|
| 23 |
+
lid = LanguageIdentifier()
|
| 24 |
+
print(lid.predict(["Мин башҡорт телен яратам."]))
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
Для запуска требуется `onnxruntime` и `numpy`.
|
| 28 |
+
|
| 29 |
+
## Модель
|
| 30 |
+
|
| 31 |
+
Используются символьные n-граммы TF-IDF (`char_wb`, 2–4) и линейный
|
| 32 |
+
классификатор, экспортированный в ONNX. Веса и словарь признаков поставляются
|
| 33 |
+
в репозитории; исходные тренировочные тексты не распространяются.
|
| 34 |
+
|
| 35 |
+
## Ограничения
|
| 36 |
+
|
| 37 |
+
Модель бинарная: `ba` против `ru`. Она не предназначена для надёжного
|
| 38 |
+
различения татарского, чувашского и других языков. Для многоязычного шлюза
|
| 39 |
+
используйте отдельную LID-модель. Короткие строки, имена, OCR и смешанный текст
|
| 40 |
+
могут классифицироваться неоднозначно.
|
| 41 |
+
|
| 42 |
+
## Лицензия и происхождение
|
| 43 |
+
|
| 44 |
+
Код и экспорт модели распространяются под Apache-2.0. Тренировочные тексты
|
| 45 |
+
остаются в исходном проекте и не включены в этот репозиторий. Если отдельный
|
| 46 |
+
источник имеет более строгие условия, применяются условия этого источника.
|
| 47 |
+
|
| 48 |
+
## Цитирование
|
| 49 |
+
|
| 50 |
+
```bibtex
|
| 51 |
+
@software{failed09_bashkir_lid,
|
| 52 |
+
title = {Bashkir LID},
|
| 53 |
+
author = {failed09},
|
| 54 |
+
year = {2026},
|
| 55 |
+
publisher = {Hugging Face},
|
| 56 |
+
url = {https://huggingface.co/failed09/bashkir-lid}
|
| 57 |
+
}
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
Башҡорт теле өсөн эшләнгән асыҡ сығанаҡлы инструмент.
|
__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Bashkir/Russian language identification."""
|
| 2 |
+
|
| 3 |
+
from .lid import LanguageIdentifier
|
| 4 |
+
|
| 5 |
+
__all__ = ["LanguageIdentifier"]
|
lid.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Portable Unicode char_wb preprocessing and sparse ONNX inference."""
|
| 2 |
+
import hashlib
|
| 3 |
+
import json
|
| 4 |
+
import re
|
| 5 |
+
from collections import Counter
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
ROOT = Path(__file__).resolve().parent
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def sha256(path):
|
| 14 |
+
with open(path, "rb") as stream:
|
| 15 |
+
return hashlib.file_digest(stream, "sha256").hexdigest()
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def char_wb(text, ngram_range):
|
| 19 |
+
# Match sklearn's _char_wb_ngrams, including short-word handling.
|
| 20 |
+
text = re.sub(r"\s\s+", " ", text.lower())
|
| 21 |
+
for word in text.split():
|
| 22 |
+
word = " " + word + " "
|
| 23 |
+
for n in range(ngram_range[0], ngram_range[1] + 1):
|
| 24 |
+
offset = 0
|
| 25 |
+
yield word[offset:offset + n]
|
| 26 |
+
while offset + n < len(word):
|
| 27 |
+
offset += 1
|
| 28 |
+
yield word[offset:offset + n]
|
| 29 |
+
if offset == 0:
|
| 30 |
+
break
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class LanguageIdentifier:
|
| 34 |
+
def __init__(self, model_dir=None, batch_size=256):
|
| 35 |
+
import onnxruntime as ort
|
| 36 |
+
|
| 37 |
+
self.model_dir = Path(model_dir) if model_dir else ROOT / "model"
|
| 38 |
+
self.meta = json.loads((self.model_dir / "META.json").read_text(encoding="utf-8"))
|
| 39 |
+
for name in ("model.onnx", "vectorizer.json"):
|
| 40 |
+
digest = self.meta["sha256"][name]
|
| 41 |
+
if sha256(self.model_dir / name) != digest:
|
| 42 |
+
raise ValueError(f"LID artifact checksum mismatch: {name}")
|
| 43 |
+
cfg = json.loads((self.model_dir / "vectorizer.json").read_text(encoding="utf-8"))
|
| 44 |
+
self.vocabulary = cfg["vocabulary"]
|
| 45 |
+
self.ngram_range = cfg["ngram_range"]
|
| 46 |
+
self.classes = np.asarray(self.meta["classes"])
|
| 47 |
+
if set(self.classes) != {"ba", "ru"}:
|
| 48 |
+
raise ValueError("Expected BA/RU model")
|
| 49 |
+
if batch_size < 1:
|
| 50 |
+
raise ValueError("batch_size must be positive")
|
| 51 |
+
self.batch_size = batch_size
|
| 52 |
+
options = ort.SessionOptions()
|
| 53 |
+
options.intra_op_num_threads = 1
|
| 54 |
+
self.session = ort.InferenceSession(str(self.model_dir / "model.onnx"), options,
|
| 55 |
+
providers=["CPUExecutionProvider"])
|
| 56 |
+
|
| 57 |
+
def features(self, texts):
|
| 58 |
+
rows = []
|
| 59 |
+
for text in texts:
|
| 60 |
+
if not isinstance(text, str):
|
| 61 |
+
raise TypeError("LID input must contain strings")
|
| 62 |
+
counts = Counter(self.vocabulary[g] for g in char_wb(text, self.ngram_range)
|
| 63 |
+
if g in self.vocabulary)
|
| 64 |
+
rows.append(counts)
|
| 65 |
+
width = max(1, max(map(len, rows), default=0))
|
| 66 |
+
ids = np.zeros((len(rows), width), dtype=np.int64)
|
| 67 |
+
counts = np.zeros((len(rows), width), dtype=np.float32)
|
| 68 |
+
for i, row in enumerate(rows):
|
| 69 |
+
if row:
|
| 70 |
+
ids[i, :len(row)] = list(row)
|
| 71 |
+
counts[i, :len(row)] = list(row.values())
|
| 72 |
+
return {"feature_ids": ids, "counts": counts}
|
| 73 |
+
|
| 74 |
+
def predict_proba(self, texts):
|
| 75 |
+
texts = list(texts)
|
| 76 |
+
results = []
|
| 77 |
+
for start in range(0, len(texts), self.batch_size):
|
| 78 |
+
results.append(self.session.run(["probabilities"],
|
| 79 |
+
self.features(texts[start:start + self.batch_size]))[0])
|
| 80 |
+
return np.concatenate(results) if results else np.empty((0, 2), dtype=np.float32)
|
| 81 |
+
|
| 82 |
+
def predict(self, texts):
|
| 83 |
+
return self.classes[self.predict_proba(texts).argmax(axis=1)]
|
| 84 |
+
|
| 85 |
+
def describe(self):
|
| 86 |
+
return {"name": self.meta["id"], "version": self.meta["version"],
|
| 87 |
+
"sha256": self.meta["sha256"], "backend": "onnxruntime",
|
| 88 |
+
"decision_rule": "argmax probabilities; classes ordered as in model passport"}
|
model.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9ddfdecf5679393da8943dc7b732ea937b5eaf4bfff2071c431cc7f9d1a0586f
|
| 3 |
+
size 800928
|
vectorizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|