Views
No views yet
Qwen3-TTS-12Hz-1.7B-Base) ships with a fixed set of languages in its codec token vocabulary. Arabic was not among them. Adding it required changes at three levels: the language embedding table, the input sequence format, and the model config.2072. Rather than initialising this embedding randomly, it was set to the mean of all existing language embeddings before training began:1ARABIC_LANG_ID = 2072
2codec_emb = qwen3tts.model.talker.model.codec_embedding
3existing_ids = [v for k, v in config.talker_config.codec_language_id.items() if k != 'arabic']
4avg = codec_emb.weight[existing_ids].float().mean(0)
5codec_emb.weight[ARABIC_LANG_ID] = avgpos 3: codec_think_id
pos 4: codec_think_bos_id
pos 5: lang_id ← Arabic token 2072 injected here
pos 6: codec_think_eos_id
pos 7: speaker embedding slot ← shifted by +1 vs. base modeldataset.py (the +9 vs. the original +8 offset) and setting codec_embedding_mask[7] = False so the speaker embedding at position 7 is injected directly from the speaker encoder rather than looked up from the embedding table.dataset.py detects Arabic automatically from Unicode range \u0600–\u06FF, so mixed-language datasets don't need an explicit language field per sample:1def _detect_language(self, text: str) -> str:
2 for c in text:
3 if '\u0600' <= c <= '\u06FF':
4 return 'arabic'
5 return 'english'3000) was registered for the Emirati voice. The speaker embedding is extracted from a reference audio clip by the frozen speaker encoder, then injected at position 7 of each sequence during the forward pass. At checkpoint save time the embedding is written directly into the safetensors weights so the saved model is fully self-contained.| Setting | Value |
|---|---|
| Base model | Qwen/Qwen3-TTS-12Hz-1.7B-Base |
| Optimizer | AdamW, lr=2e-6, weight decay=0.01 |
| Precision | bf16 mixed precision |
| Gradient accumulation | 4 steps (effective batch ~32) |
| Gradient clipping | 1.0 |
| Epochs | 10 |
| Loss | talker_loss + 0.3 × sub_talker_loss |
torch.no_grad()).infer.py to synthesize speech from any checkpoint.pip install qwen-tts soundfile torch1python infer.py \
2 --text "كيف كان يومك اليوم؟ إن شاء الله كان مليان خير." \
3 --output out.wav1python infer.py \
2 --text_file sentences.txt \
3 --output_dir outputs/1python infer.py \
2 --checkpoint output/checkpoint-epoch-9 \
3 --text "كيف كان يومك اليوم؟ إن شاء الله كان مليان خير." \
4 --output out.wav| Argument | Default | Description |
|---|---|---|
--checkpoint | vadimbelsky/qwen3.5-TTS-Emirati | HuggingFace model ID or local checkpoint path |
--text | — | Single text string to synthesize |
--text_file | — | Text file with one utterance per line |
--language | arabic | Language of the input text |
--speaker | emirati_speaker | Speaker name stored in the checkpoint |
--output | output.wav | Output path for single-utterance mode |
--output_dir | — | Output directory for multi-utterance mode |
--device | cuda:0 | Torch device |
--max_new_tokens | 2048 | Maximum codec tokens to generate (increase for longer texts) |
--temperature | 0.9 | Sampling temperature |
pip install qwen-tts first, then run the command below:git clone https://github.com/QwenLM/Qwen3-TTS.git
cd Qwen3-TTS/finetuningaudio: path to the target training audio (wav)text: transcript corresponding to audioref_audio: path to the reference speaker audio (wav)1{"audio":"./data/utt0001.wav","text":"其实我真的有发现,我是一个特别善于观察别人情绪的人。","ref_audio":"./data/ref.wav"}
2{"audio":"./data/utt0002.wav","text":"She said she would be here by noon.","ref_audio":"./data/ref.wav"}ref_audio recommendation:ref_audio for all samples.ref_audio identical across the dataset usually improves speaker consistency and stability during generation.audio_codes)train_raw.jsonl into a training JSONL that includes audio_codes:1python prepare_data.py \
2 --device cuda:0 \
3 --tokenizer_model_path Qwen/Qwen3-TTS-Tokenizer-12Hz \
4 --input_jsonl train_raw.jsonl \
5 --output_jsonl train_with_codes.jsonl1python sft_12hz.py \
2 --init_model_path Qwen/Qwen3-TTS-12Hz-1.7B-Base \
3 --output_model_path output \
4 --train_jsonl train_with_codes.jsonl \
5 --batch_size 32 \
6 --lr 2e-6 \
7 --num_epochs 10 \
8 --speaker_name speaker_testoutput/checkpoint-epoch-0output/checkpoint-epoch-1output/checkpoint-epoch-21import torch
2import soundfile as sf
3from qwen_tts import Qwen3TTSModel
4
5device = "cuda:0"
6tts = Qwen3TTSModel.from_pretrained(
7 "output/checkpoint-epoch-2",
8 device_map=device,
9 dtype=torch.bfloat16,
10 attn_implementation="flash_attention_2",
11)
12
13wavs, sr = tts.generate_custom_voice(
14 text="She said she would be here by noon.",
15 speaker="speaker_test",
16)
17sf.write("output.wav", wavs[0], sr)1#!/usr/bin/env bash
2set -e
3
4DEVICE="cuda:0"
5TOKENIZER_MODEL_PATH="Qwen/Qwen3-TTS-Tokenizer-12Hz"
6INIT_MODEL_PATH="Qwen/Qwen3-TTS-12Hz-1.7B-Base"
7
8RAW_JSONL="train_raw.jsonl"
9TRAIN_JSONL="train_with_codes.jsonl"
10OUTPUT_DIR="output"
11
12BATCH_SIZE=2
13LR=2e-5
14EPOCHS=3
15SPEAKER_NAME="speaker_1"
16
17python prepare_data.py \
18 --device ${DEVICE} \
19 --tokenizer_model_path ${TOKENIZER_MODEL_PATH} \
20 --input_jsonl ${RAW_JSONL} \
21 --output_jsonl ${TRAIN_JSONL}
22
23python sft_12hz.py \
24 --init_model_path ${INIT_MODEL_PATH} \
25 --output_model_path ${OUTPUT_DIR} \
26 --train_jsonl ${TRAIN_JSONL} \
27 --batch_size ${BATCH_SIZE} \
28 --lr ${LR} \
29 --num_epochs ${EPOCHS} \
30 --speaker_name ${SPEAKER_NAME}