Rishe compact models

These are the first model files for Rishe, a Persian NLP engine written in Rust. There are three tasks: part-of-speech tagging, named-entity recognition and ezafe detection. Each task is a separate ONNX file, so you only download the one you need.

The encoder is HooshvareLab/albert-fa-zwnj-base-v2. I fine-tuned one model per task and exported each to plain fp32 ONNX with batch size 1 and a dynamic sequence axis. You can run them with ONNX Runtime or with tract, which is what Rishe uses internally.

What is in the repository

task labels max length file size
POS 16 UPOS tags 192 44.8 MB
NER 21 BIO tags 192 44.8 MB
Ezafe NO_EZAFE, EZAFE 128 44.8 MB

Each directory has model.onnx, tokenizer.json, tokenizer_config.json, labels.json and metrics.json. The root manifest.json has the sha256 and size of every file.

The files are independent. Pulling all three costs about 134 MB; pulling one costs about 45 MB. There is no shared backbone in this release, and I am not pretending there is.

Results

I evaluated on the same test sets used in the Rishe benchmarks.

task dataset Rishe Shekar Hazm
POS micro F1 UD-Seraji 94.89 82.50 90.66
POS micro F1 UD-PerDT 96.80 97.39 84.90
NER exact-span F1 PEYMA test 94.05 57.14 N/A
NER entity F1 PEYMA+Arman mixed 92.48 N/A N/A
Ezafe F1 HomoRich held-out 94.86 N/A N/A

A few honest notes:

  • The POS model is the better generalist: it is 12.4 points ahead of Shekar on Seraji and 0.59 behind on PerDT. One model across both treebanks was the goal, so I did not fine-tune a PerDT-only variant.
  • NER is the clear win. The 94.05 is exact-span F1 on the PEYMA test file after stripping whitespace and ZWNJ, against 57.14 for Shekar's NER on the same file.
  • The NER taxonomy here is wider than Shekar's: 21 tags covering DAT, EVE, FAC, LOC, MON, ORG, PCT, PER, PRO and TIM.
  • Ezafe labels come from the 1 ezafe marker in HomoRich's Mapped Phoneme. Rows where the grapheme and phoneme token counts did not match were skipped. The 94.86 is on a held-out HomoRich split, not on the refined subset used by other ezafe models.

How I trained them

  • POS: UD Persian Seraji + PerDT train splits, evaluated on each test split separately.
  • NER: AliFartout/PEYMA-ARMAN-Mixed, using its train/validation/test splits, plus a PEYMA-only evaluation on the Rishe test file.
  • Ezafe: MahtaFetrat/HomoRich-G2P-Persian, shuffled, with the first 150k rows scanned and up to 100k kept for training.
  • 6 epochs for POS, 5 for NER, 3 for ezafe; learning rate 3e-5, seed 42, one GPU. The accuracy harness is in the Rishe repository under scripts/benchmark_models.py, the runtime benchmark under scripts/benchmark_modelpack.py, and the training notebook is not part of the public repo.

Using a model

The models are word-level. A word can be split into several subword pieces; the prediction for the word comes from the first piece.

import json
import numpy as np
import onnxruntime as ort
from tokenizers import Tokenizer

task = "pos"  # or "ner", "ezafe"
tokenizer = Tokenizer.from_file(f"{task}/tokenizer.json")
labels = json.load(open(f"{task}/labels.json"))["labels"]
session = ort.InferenceSession(f"{task}/model.onnx", providers=["CPUExecutionProvider"])

def encode(words, max_len=192):
    ids = [tokenizer.token_to_id("[CLS]")]
    positions = []
    for word in words:
        pieces = tokenizer.encode(word, add_special_tokens=False).ids
        if not pieces:
            pieces = [tokenizer.token_to_id("<unk>")]
        positions.append(len(ids))
        ids.extend(pieces)
        if len(ids) >= max_len - 1:
            break
    ids.append(tokenizer.token_to_id("[SEP]"))
    ids = ids[:max_len]
    return (
        np.array([ids], dtype=np.int64),
        np.ones((1, len(ids)), dtype=np.int64),
        positions,
    )

words = "کتاب‌های خوب را به دوستانم می‌دهم.".split()
input_ids, attention_mask, positions = encode(words)
logits = session.run(
    None, {"input_ids": input_ids, "attention_mask": attention_mask}
)[0][0]
print([
    (word, labels[int(logits[position].argmax())])
    for word, position in zip(words, positions)
    if position < logits.shape[0]
])

Using the packs with Rishe

Rishe 0.5.0 loads these packs directly; there is no ONNX Runtime install and no Python ML stack to set up.

rishe hub pull pos-compact
from rishe import ModelPack, hub

pos = ModelPack(hub.path_pack("pos-compact"))
pos.tag("کتاب‌های خوب را به دوستانم می‌دهم.")
# [('کتاب‌های', 'NOUN'), ('خوب', 'ADJ'), ('را', 'ADP'), ('به', 'ADP'),
#  ('دوستانم', 'NOUN'), ('می‌دهم', 'VERB'), ('.', 'PUNCT')]

The other pack names are ner-compact and ezafe-compact; use hub.pull_pack(name) to download and hub.path_pack(name) for an existing cache entry. From Rust, enable the models feature and call rishe::models::ModelPack::load(path)?.tag_text(text)?.

Limits

  • These are token classifiers, not sentence embeddings. Use the Rishe embedding engine for vectors.
  • The POS PerDT number is slightly behind Shekar; if that half-point matters, this pack is not the one for you.
  • The ezafe labels are derived from the HomoRich phoneme annotation, not hand-checked word by word.
  • The NER tag set is wider than the older five-class Persian NER benchmarks, so mixed-dataset comparisons need care.
  • fp32 only. A distilled or int8 variant would be a different release; tract's int8 path is not fast enough to ship.

Data and license

  • Base model: HooshvareLab/albert-fa-zwnj-base-v2, Apache-2.0.
  • UD Persian Seraji and PerDT: CC BY-SA 4.0.
  • AliFartout/PEYMA-ARMAN-Mixed: MIT. The Arman corpus behind it was released for academic research; check that provenance if you plan commercial use.
  • MahtaFetrat/HomoRich-G2P-Persian: CC0-1.0.

The fine-tuned weights are released under Apache-2.0, with attribution to the base model and datasets. See LICENSE and NOTICE.md.

Trained and published by Mahdi Alikhah.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for MahdiAlikhah/rishe-models

Quantized
(2)
this model

Datasets used to train MahdiAlikhah/rishe-models