A Japanese voice-cloning TTS model — Vyvo/Vyvo-Multilingual-v0.1 fine-tuned for a single conversational Japanese speaker. Give a short reference clip (+ its transcript) and a target text; the model speaks the text in that voice by pure language-model continuation.
Japanese Character Error Rate (CER — the standard metric for Japanese) on 100 evaluation texts, transcribed with Qwen3-ASR-1.7B (lower = better):
| Model | Decoding | CER ↓ | WER ↓ | Accuracy (1-CER) |
|---|---|---|---|---|
| Base (Vyvo-Multilingual-v0.1) | greedy | 0.395 | 0.426 | 60.5 % |
| This model | greedy | 0.338 | 0.375 | 66.2 % |
| This model | sampling + best-of-6 | 0.155 | 0.218 | 84.5 % |
Greedy decoding under-reports quality (occasional early end-of-speech); with sampling + best-of-N the model reaches ~84.5 % character accuracy. Most remaining errors are homophone / kanji-vs-kana substitutions that are phonetically correct.
The model is a transcript-conditioned TTS: always pass the reference clip's
transcript. For production use sampling (temperature ≈ 0.4–0.5, top_p = 0.9,
repetition_penalty = 1.1) and, when quality matters, best-of-N (generate
4–6 candidates and keep the one whose ASR transcript best matches the target).
import soundfile as sf
import torch
import torchaudio
from transformers import (AutoModelForCausalLM, AutoTokenizer,
AutoFeatureExtractor, MimiModel)
REPO = "Vyvo/Vyvo-Multilingual-JA-FT-v0.1"
DEVICE = "cuda"
BASE = 151669
NUM_CODEBOOKS, CODEBOOK_SIZE, AUDIO_OFFSET = 32, 2048, 10
SOS, EOS, SOH, EOH, SOA = 1, 2, 3, 4, 5
tokenizer = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForCausalLM.from_pretrained(REPO, dtype=torch.bfloat16).to(DEVICE).eval()
mimi = MimiModel.from_pretrained("kyutai/mimi").to(DEVICE).eval()
feature_extractor = AutoFeatureExtractor.from_pretrained("kyutai/mimi")
def encode_reference(path):
wav, sr = sf.read(path, dtype="float32", always_2d=False)
if wav.ndim > 1:
wav = wav.mean(axis=1)
target_sr = feature_extractor.sampling_rate
if sr != target_sr:
wav = torchaudio.functional.resample(
torch.from_numpy(wav).unsqueeze(0), sr, target_sr).squeeze(0).numpy()
inputs = feature_extractor(raw_audio=wav, sampling_rate=target_sr, return_tensors="pt")
codes = mimi.encode(inputs["input_values"].to(DEVICE),
num_quantizers=NUM_CODEBOOKS).audio_codes[0].cpu()
flat = codes.transpose(0, 1).reshape(-1).tolist()
return [c + AUDIO_OFFSET + (i % NUM_CODEBOOKS) * CODEBOOK_SIZE + BASE
for i, c in enumerate(flat)]
def build_prompt(ref_tokens, ref_text, target_text):
text_ids = tokenizer(ref_text + " " + target_text, add_special_tokens=False).input_ids
head = [BASE + SOH] + text_ids + [tokenizer.eos_token_id, BASE + EOH, BASE + SOA, BASE + SOS]
return head + ref_tokens
def decode_audio(generated_ids):
codes = []
for i, tok in enumerate(generated_ids):
v = tok - BASE - AUDIO_OFFSET - (i % NUM_CODEBOOKS) * CODEBOOK_SIZE
if 0 <= v < CODEBOOK_SIZE:
codes.append(v)
else:
break
frames = len(codes) // NUM_CODEBOOKS
codes = torch.tensor(codes[:frames * NUM_CODEBOOKS]).view(frames, NUM_CODEBOOKS)
codes = codes.t().unsqueeze(0).to(DEVICE)
return mimi.decode(codes).audio_values.squeeze().cpu().float().numpy()
reference_wav = "reference.wav"
reference_text = "リファレンス音声の書き起こし" # transcript of the reference clip
target_text = "こんにちは、これは音声合成モデルのテストです。"
prompt = build_prompt(encode_reference(reference_wav), reference_text, target_text)
input_ids = torch.tensor([prompt], device=DEVICE)
output = model.generate(
input_ids, attention_mask=torch.ones_like(input_ids),
max_new_tokens=9600, min_new_tokens=960,
do_sample=True, temperature=0.45, top_p=0.9, repetition_penalty=1.1,
eos_token_id=BASE + EOS, pad_token_id=tokenizer.eos_token_id)
audio = decode_audio(output[0, input_ids.shape[1]:].tolist())
sf.write("output.wav", audio, mimi.config.sampling_rate)
| Method | full fine-tuning (liger fused cross-entropy) |
| Learning rate | 1e-4, cosine, warmup 0.03 |
| Epochs / batch | 2 / 8 |
| Hardware | 1× H100 |
Recipe carried over from the best English fine-tune (full FT, lr 1e-4).
Fine-tuned from Vyvo/Vyvo-Multilingual-v0.1. Released under the MIT License.
2 commits
A Japanese voice-cloning TTS model — Vyvo/Vyvo-Multilingual-v0.1 fine-tuned for a single conversational Japanese speaker. Give a short reference clip (+ its transcript) and a target text; the model speaks the text in that voice by pure language-model continuation.
Japanese Character Error Rate (CER — the standard metric for Japanese) on 100 evaluation texts, transcribed with Qwen3-ASR-1.7B (lower = better):
| Model | Decoding | CER ↓ | WER ↓ | Accuracy (1-CER) |
|---|---|---|---|---|
| Base (Vyvo-Multilingual-v0.1) | greedy | 0.395 | 0.426 | 60.5 % |
| This model | greedy | 0.338 | 0.375 | 66.2 % |
| This model | sampling + best-of-6 | 0.155 | 0.218 | 84.5 % |
Greedy decoding under-reports quality (occasional early end-of-speech); with sampling + best-of-N the model reaches ~84.5 % character accuracy. Most remaining errors are homophone / kanji-vs-kana substitutions that are phonetically correct.
The model is a transcript-conditioned TTS: always pass the reference clip's
transcript. For production use sampling (temperature ≈ 0.4–0.5, top_p = 0.9,
repetition_penalty = 1.1) and, when quality matters, best-of-N (generate
4–6 candidates and keep the one whose ASR transcript best matches the target).
import soundfile as sf
import torch
import torchaudio
from transformers import (AutoModelForCausalLM, AutoTokenizer,
AutoFeatureExtractor, MimiModel)
REPO = "Vyvo/Vyvo-Multilingual-JA-FT-v0.1"
DEVICE = "cuda"
BASE = 151669
NUM_CODEBOOKS, CODEBOOK_SIZE, AUDIO_OFFSET = 32, 2048, 10
SOS, EOS, SOH, EOH, SOA = 1, 2, 3, 4, 5
tokenizer = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForCausalLM.from_pretrained(REPO, dtype=torch.bfloat16).to(DEVICE).eval()
mimi = MimiModel.from_pretrained("kyutai/mimi").to(DEVICE).eval()
feature_extractor = AutoFeatureExtractor.from_pretrained("kyutai/mimi")
def encode_reference(path):
wav, sr = sf.read(path, dtype="float32", always_2d=False)
if wav.ndim > 1:
wav = wav.mean(axis=1)
target_sr = feature_extractor.sampling_rate
if sr != target_sr:
wav = torchaudio.functional.resample(
torch.from_numpy(wav).unsqueeze(0), sr, target_sr).squeeze(0).numpy()
inputs = feature_extractor(raw_audio=wav, sampling_rate=target_sr, return_tensors="pt")
codes = mimi.encode(inputs["input_values"].to(DEVICE),
num_quantizers=NUM_CODEBOOKS).audio_codes[0].cpu()
flat = codes.transpose(0, 1).reshape(-1).tolist()
return [c + AUDIO_OFFSET + (i % NUM_CODEBOOKS) * CODEBOOK_SIZE + BASE
for i, c in enumerate(flat)]
def build_prompt(ref_tokens, ref_text, target_text):
text_ids = tokenizer(ref_text + " " + target_text, add_special_tokens=False).input_ids
head = [BASE + SOH] + text_ids + [tokenizer.eos_token_id, BASE + EOH, BASE + SOA, BASE + SOS]
return head + ref_tokens
def decode_audio(generated_ids):
codes = []
for i, tok in enumerate(generated_ids):
v = tok - BASE - AUDIO_OFFSET - (i % NUM_CODEBOOKS) * CODEBOOK_SIZE
if 0 <= v < CODEBOOK_SIZE:
codes.append(v)
else:
break
frames = len(codes) // NUM_CODEBOOKS
codes = torch.tensor(codes[:frames * NUM_CODEBOOKS]).view(frames, NUM_CODEBOOKS)
codes = codes.t().unsqueeze(0).to(DEVICE)
return mimi.decode(codes).audio_values.squeeze().cpu().float().numpy()
reference_wav = "reference.wav"
reference_text = "リファレンス音声の書き起こし" # transcript of the reference clip
target_text = "こんにちは、これは音声合成モデルのテストです。"
prompt = build_prompt(encode_reference(reference_wav), reference_text, target_text)
input_ids = torch.tensor([prompt], device=DEVICE)
output = model.generate(
input_ids, attention_mask=torch.ones_like(input_ids),
max_new_tokens=9600, min_new_tokens=960,
do_sample=True, temperature=0.45, top_p=0.9, repetition_penalty=1.1,
eos_token_id=BASE + EOS, pad_token_id=tokenizer.eos_token_id)
audio = decode_audio(output[0, input_ids.shape[1]:].tolist())
sf.write("output.wav", audio, mimi.config.sampling_rate)
| Method | full fine-tuning (liger fused cross-entropy) |
| Learning rate | 1e-4, cosine, warmup 0.03 |
| Epochs / batch | 2 / 8 |
| Hardware | 1× H100 |
Recipe carried over from the best English fine-tune (full FT, lr 1e-4).
Fine-tuned from Vyvo/Vyvo-Multilingual-v0.1. Released under the MIT License.
2 commits