Downloads · 30 days
13
13% of all-time downloads
hofarah/orpheus-3b-persian-tts-lora
orpheus-3b-persian-tts-lora is a text-to-speech model from hofarah. Use it when you need text read aloud. It is set up for peft.
A LoRA adapter that adapts unsloth/orpheus-3b-0.1-ft — an English speech-LM built on Llama-3.2-3B that emits SNAC audio codec tokens — to Persian (Farsi) text-to-speech.
Downloads · 30 days
13
13% of all-time downloads
All-time downloads
104
Public
Repo size
610 MB
Likes
0
Public
Click a slice to open those files.
.safetensors389 MB · 93%
From the Hugging Face model README
A LoRA adapter that adapts unsloth/orpheus-3b-0.1-ft
— an English speech-LM built on Llama-3.2-3B that emits SNAC
audio codec tokens — to Persian (Farsi) text-to-speech.
The adapter takes Finglish input: Persian romanized into Latin script (salam, hale shoma chetore?),
not Persian script (سلام، حال شما چطوره؟). This was the deliberate design choice of the project — it
reuses the base model's existing Latin-script tokenization instead of forcing it to learn an unseen
writing system from ~50 hours of audio. See Input format.
Evaluated against 1,219 held-out sentences (in-domain + challenge sets), transcribed with Whisper for ASR-based error rates. Lower is better for CER/WER/RTF.
| System | CER ↓ | WER ↓ | Persian LID ↑ | Spk. sim ↑ | RTF ↓ |
|---|---|---|---|---|---|
| This model (Orpheus-3B + LoRA, Finglish) | 0.129 | 0.367 | 0.967 | 0.763 | 1.50 |
| SNAC codec reconstruction (quality ceiling) | 0.144 | — | — | 0.722 | — |
facebook/mms-tts-fas (VITS, 36M) | 0.152 | 0.449 | 0.980 | — | 0.01 |
| Qwen3-TTS-1.7B, full fine-tune | 0.230 | 0.610 | 0.576 | — | 0.51 |
| Orpheus-3B base (no adaptation) | 0.421 | 0.816 | 0.030 | 0.284 | 1.09 |
Split by evaluation set, CER is 0.103 in-domain and 0.267 on the challenge set. Failure rate was 0.0% — every one of the 1,219 prompts produced decodable audio.
Two things worth reading carefully:
mms-tts-fas is ~150× faster. This is an autoregressive 3B LM emitting 7 codec tokens per 85 ms
frame; it is not suited to low-latency streaming without further work.Speaker similarity is measured only on the single-speaker in-domain set. Persian LID is the fraction of outputs Whisper identifies as Persian.
The model was trained on Finglish and expects Finglish. Feeding it Persian script is an out-of-distribution condition that the source project measured as a separate, confounded ablation (see Limitations).
The prompt protocol is inherited from Orpheus — a voice tag, then the text, wrapped in special tokens:
[128259] "female1: <finglish text>" [128009] [128260]
female1 is the only voice in the training corpus; other tags are untrained.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
from snac import SNAC
ADAPTER = "hofarah/orpheus-3b-persian-tts-lora"
BASE = "unsloth/orpheus-3b-0.1-ft"
# The adapter repo carries the resized tokenizer (base vocab + audio tokens),
# so load the tokenizer from here, not from the base model.
tok = AutoTokenizer.from_pretrained(ADAPTER)
model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype=torch.bfloat16, device_map="cuda")
if len(tok) > model.get_input_embeddings().weight.shape[0]:
model.resize_token_embeddings(len(tok))
model = PeftModel.from_pretrained(model, ADAPTER).eval()
snac = SNAC.from_pretrained("hubertsiuzdak/snac_24khz").eval()
START_OF_HUMAN, END_OF_TEXT, END_OF_HUMAN = 128259, 128009, 128260
START_OF_SPEECH, END_OF_SPEECH, AUDIO_BASE = 128257, 128258, 128266
text = "salam, hale shoma chetore?" # Finglish, not Persian script
ids = tok(f"female1: {text}", return_tensors="pt").input_ids
ids = torch.cat([torch.tensor([[START_OF_HUMAN]]), ids,
torch.tensor([[END_OF_TEXT, END_OF_HUMAN]])], dim=1).to(model.device)
with torch.inference_mode():
out = model.generate(input_ids=ids, attention_mask=torch.ones_like(ids),
max_new_tokens=1200, do_sample=True, temperature=0.6,
top_p=0.95, repetition_penalty=1.1,
eos_token_id=END_OF_SPEECH)
# Crop to the generated speech span, then de-interleave 7-token groups into
# SNAC's three codebook layers (L1: slot 0; L2: slots 1,4; L3: slots 2,3,5,6).
row = out[0].cpu()
hits = (row == START_OF_SPEECH).nonzero()
row = row[int(hits[-1]) + 1:] if hits.numel() else row
row = row[row != END_OF_SPEECH]
row = row[row >= AUDIO_BASE] - AUDIO_BASE
row = row[: (row.numel() // 7) * 7]
l1, l2, l3 = [], [], []
for i in range(row.numel() // 7):
g = row[i * 7:(i + 1) * 7].tolist()
if all(0 <= v < 4096 for v in (g[0], g[1] - 4096, g[4] - 4 * 4096,
g[2] - 2 * 4096, g[3] - 3 * 4096,
g[5] - 5 * 4096, g[6] - 6 * 4096)):
l1 += [g[0]]
l2 += [g[1] - 4096, g[4] - 4 * 4096]
l3 += [g[2] - 2 * 4096, g[3] - 3 * 4096, g[5] - 5 * 4096, g[6] - 6 * 4096]
codes = [torch.tensor(x).unsqueeze(0) for x in (l1, l2, l3)]
with torch.inference_mode():
wav = snac.decode(codes).squeeze().float().numpy() # 24 kHz mono
A group containing an out-of-range index is a codec failure — drop it and count it rather than clamping, so the rate stays measurable.
| Base model | unsloth/orpheus-3b-0.1-ft (Llama-3.2-3B backbone, SNAC 24 kHz codec) |
| Method | LoRA, r=64, α=64, dropout 0, bias none |
| Target modules | q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj |
| Trainable params | ≈60M of 3.2B (~1.9%) |
| Schedule | 1 epoch, 4,865 steps, batch size 1, linear decay from 2e-4 |
| Training loss | 4.88 → 3.95 |
| Precision | bf16 against an unquantized base |
| Data | hofarah/Persian-tts-finglish-orpheus — 19,458 utterances, ≈50.8 h |
Inference was evaluated in bf16 rather than 4-bit, because the LoRA was trained against an unquantized base — bf16 is closer to the training condition.
trainer_state.json (full loss curve) is kept in this repo. Optimizer, scheduler and RNG state were
removed; this repo is for inference, not for resuming training.
female1) in the entire corpus. There is no speaker control, and the
speaker-similarity number describes consistency with that one voice.except: in the corpus's Finglish generation script
wrote raw Persian text into the Finglish column whenever the transliteration API call failed. The
composition is 86.73% clean Finglish, 10.46% pure Persian script, 2.04% mixed, 0.77% empty/single-char.
Consequence: the model saw ~2,400 Persian-script examples, so any apparent Persian-script ability
has a direct training-data explanation and is not zero-shot cross-script transfer.Not yet set. This is a LoRA adapter over unsloth/orpheus-3b-0.1-ft, which derives from Llama-3.2-3B —
any license here must be compatible with the Llama 3.2 Community License and with the terms of the
underlying speech corpus. Confirm both before citing or redistributing.
@misc{orpheus_persian_tts_lora,
title = {Orpheus-3B Persian TTS (LoRA)},
author = {hofarah},
year = {2026},
url = {https://huggingface.co/hofarah/orpheus-3b-persian-tts-lora}
}