Views
No views yet
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.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.| 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 |
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.[128259] "female1: <finglish text>" [128009] [128260]female1 is the only voice in the training corpus; other tags are untrained.1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
3from peft import PeftModel
4from snac import SNAC
5
6ADAPTER = "hofarah/orpheus-3b-persian-tts-lora"
7BASE = "unsloth/orpheus-3b-0.1-ft"
8
9# The adapter repo carries the resized tokenizer (base vocab + audio tokens),
10# so load the tokenizer from here, not from the base model.
11tok = AutoTokenizer.from_pretrained(ADAPTER)
12model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype=torch.bfloat16, device_map="cuda")
13if len(tok) > model.get_input_embeddings().weight.shape[0]:
14 model.resize_token_embeddings(len(tok))
15model = PeftModel.from_pretrained(model, ADAPTER).eval()
16snac = SNAC.from_pretrained("hubertsiuzdak/snac_24khz").eval()
17
18START_OF_HUMAN, END_OF_TEXT, END_OF_HUMAN = 128259, 128009, 128260
19START_OF_SPEECH, END_OF_SPEECH, AUDIO_BASE = 128257, 128258, 128266
20
21text = "salam, hale shoma chetore?" # Finglish, not Persian script
22ids = tok(f"female1: {text}", return_tensors="pt").input_ids
23ids = torch.cat([torch.tensor([[START_OF_HUMAN]]), ids,
24 torch.tensor([[END_OF_TEXT, END_OF_HUMAN]])], dim=1).to(model.device)
25
26with torch.inference_mode():
27 out = model.generate(input_ids=ids, attention_mask=torch.ones_like(ids),
28 max_new_tokens=1200, do_sample=True, temperature=0.6,
29 top_p=0.95, repetition_penalty=1.1,
30 eos_token_id=END_OF_SPEECH)
31
32# Crop to the generated speech span, then de-interleave 7-token groups into
33# SNAC's three codebook layers (L1: slot 0; L2: slots 1,4; L3: slots 2,3,5,6).
34row = out[0].cpu()
35hits = (row == START_OF_SPEECH).nonzero()
36row = row[int(hits[-1]) + 1:] if hits.numel() else row
37row = row[row != END_OF_SPEECH]
38row = row[row >= AUDIO_BASE] - AUDIO_BASE
39row = row[: (row.numel() // 7) * 7]
40
41l1, l2, l3 = [], [], []
42for i in range(row.numel() // 7):
43 g = row[i * 7:(i + 1) * 7].tolist()
44 if all(0 <= v < 4096 for v in (g[0], g[1] - 4096, g[4] - 4 * 4096,
45 g[2] - 2 * 4096, g[3] - 3 * 4096,
46 g[5] - 5 * 4096, g[6] - 6 * 4096)):
47 l1 += [g[0]]
48 l2 += [g[1] - 4096, g[4] - 4 * 4096]
49 l3 += [g[2] - 2 * 4096, g[3] - 3 * 4096, g[5] - 5 * 4096, g[6] - 6 * 4096]
50
51codes = [torch.tensor(x).unsqueeze(0) for x in (l1, l2, l3)]
52with torch.inference_mode():
53 wav = snac.decode(codes).squeeze().float().numpy() # 24 kHz mono| 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 |
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.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.1@misc{orpheus_persian_tts_lora,
2 title = {Orpheus-3B Persian TTS (LoRA)},
3 author = {hofarah},
4 year = {2026},
5 url = {https://huggingface.co/hofarah/orpheus-3b-persian-tts-lora}
6}