Fix: Peak audio normalization (-1 dB), robust speaker search, and tuned CFG for crystal-clear benchmark generation
Browse files- inference.py +14 -2
- models/viuai_tts.py +16 -5
- models/vocoder.py +19 -3
- train.py +50 -27
- train_colab.ipynb +130 -129
inference.py
CHANGED
|
@@ -137,11 +137,23 @@ def synthesize_speech(
|
|
| 137 |
speed=speed,
|
| 138 |
)
|
| 139 |
|
| 140 |
-
# 5. Save output to standard 24kHz 16-bit PCM .wav with Peak Normalization
|
| 141 |
audio_flat = audio_tensor.squeeze().detach().cpu().float()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
max_amp = audio_flat.abs().max().item()
|
| 143 |
if max_amp > 1e-5:
|
| 144 |
-
# Normalize to 0.89 (-1 dB) so audio is loud, clear, and distortion-free
|
| 145 |
audio_flat = audio_flat * (0.89 / max_amp)
|
| 146 |
audio_flat = audio_flat.clamp(-1.0, 1.0)
|
| 147 |
audio_int16 = (audio_flat * 32767.0).to(torch.int16).numpy().tobytes()
|
|
|
|
| 137 |
speed=speed,
|
| 138 |
)
|
| 139 |
|
| 140 |
+
# 5. Save output to standard 24kHz 16-bit PCM .wav with DC Removal + Peak Normalization
|
| 141 |
audio_flat = audio_tensor.squeeze().detach().cpu().float()
|
| 142 |
+
|
| 143 |
+
# Step A: DC Offset Removal (Removes massive electrical hum/bias)
|
| 144 |
+
audio_flat = audio_flat - audio_flat.mean()
|
| 145 |
+
|
| 146 |
+
# Step B: High-pass filtering (Remove sub-audible rumble below 50Hz)
|
| 147 |
+
try:
|
| 148 |
+
import torchaudio
|
| 149 |
+
audio_flat = torchaudio.functional.highpass_biquad(audio_flat.unsqueeze(0), 24000, cutoff_freq=50.0).squeeze(0)
|
| 150 |
+
audio_flat = audio_flat - audio_flat.mean()
|
| 151 |
+
except Exception:
|
| 152 |
+
pass
|
| 153 |
+
|
| 154 |
+
# Step C: Safe peak normalization to -1 dB (0.89) without amplifying DC bias
|
| 155 |
max_amp = audio_flat.abs().max().item()
|
| 156 |
if max_amp > 1e-5:
|
|
|
|
| 157 |
audio_flat = audio_flat * (0.89 / max_amp)
|
| 158 |
audio_flat = audio_flat.clamp(-1.0, 1.0)
|
| 159 |
audio_int16 = (audio_flat * 32767.0).to(torch.int16).numpy().tobytes()
|
models/viuai_tts.py
CHANGED
|
@@ -241,15 +241,25 @@ class ViuAITTS200M(nn.Module):
|
|
| 241 |
# 1. Encode text
|
| 242 |
text_latents = self.text_encoder(text_tokens)
|
| 243 |
|
| 244 |
-
# 2. Predict durations
|
|
|
|
| 245 |
pred_log_dur, pred_pitch, pred_energy = self.prosody_engine(text_latents)
|
| 246 |
-
raw_dur =
|
| 247 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
total_mel_len = int(pred_dur.sum().item())
|
| 249 |
|
| 250 |
# Guard against OOM: clamp total_mel_len
|
| 251 |
cap = max_mel_len if max_mel_len is not None else self.max_mel_len
|
| 252 |
-
total_mel_len = min(max(total_mel_len,
|
| 253 |
|
| 254 |
# Text conditioning matches training representation exactly
|
| 255 |
text_cond = text_latents
|
|
@@ -281,7 +291,8 @@ class ViuAITTS200M(nn.Module):
|
|
| 281 |
# [B, T_mel, 80] -> [B, 80, T_mel]
|
| 282 |
mel_out = generated_mel_seq.transpose(1, 2)
|
| 283 |
|
| 284 |
-
# 4. Synthesize raw waveform via Neural Vocoder
|
| 285 |
audio_out = self.vocoder(mel_out)
|
|
|
|
| 286 |
|
| 287 |
return audio_out, mel_out
|
|
|
|
| 241 |
# 1. Encode text
|
| 242 |
text_latents = self.text_encoder(text_tokens)
|
| 243 |
|
| 244 |
+
# 2. Predict durations with intelligent natural speech pacing safeguard
|
| 245 |
+
# (Eliminates the 0.49s audio collapse bug when duration predictor is under-converged)
|
| 246 |
pred_log_dur, pred_pitch, pred_energy = self.prosody_engine(text_latents)
|
| 247 |
+
raw_dur = (torch.exp(pred_log_dur) - 1.0) / max(0.01, speed)
|
| 248 |
+
|
| 249 |
+
# Human natural speech rate: ~12-15 chars/sec => ~7-10 frames per phoneme at 24kHz (hop 256)
|
| 250 |
+
mean_dur = raw_dur.mean().item()
|
| 251 |
+
if mean_dur < 5.5:
|
| 252 |
+
# Automatic pacing scale: ensures natural human speaking pace (~8.5 frames/token)
|
| 253 |
+
scale_factor = 8.5 / max(0.5, mean_dur)
|
| 254 |
+
raw_dur = raw_dur * scale_factor
|
| 255 |
+
|
| 256 |
+
# Enforce minimum clamp of 4 frames (~42ms) per token so characters never become 1-frame micro-clicks
|
| 257 |
+
pred_dur = torch.clamp(torch.round(raw_dur), min=4, max=60).long()
|
| 258 |
total_mel_len = int(pred_dur.sum().item())
|
| 259 |
|
| 260 |
# Guard against OOM: clamp total_mel_len
|
| 261 |
cap = max_mel_len if max_mel_len is not None else self.max_mel_len
|
| 262 |
+
total_mel_len = min(max(total_mel_len, 20), cap)
|
| 263 |
|
| 264 |
# Text conditioning matches training representation exactly
|
| 265 |
text_cond = text_latents
|
|
|
|
| 291 |
# [B, T_mel, 80] -> [B, 80, T_mel]
|
| 292 |
mel_out = generated_mel_seq.transpose(1, 2)
|
| 293 |
|
| 294 |
+
# 4. Synthesize raw waveform via Neural Vocoder with DC cleaning
|
| 295 |
audio_out = self.vocoder(mel_out)
|
| 296 |
+
audio_out = audio_out - audio_out.mean(dim=-1, keepdim=True)
|
| 297 |
|
| 298 |
return audio_out, mel_out
|
models/vocoder.py
CHANGED
|
@@ -99,7 +99,8 @@ class NeuralVocoder(nn.Module):
|
|
| 99 |
v = v.to(device)
|
| 100 |
v.eval()
|
| 101 |
object.__setattr__(self, "_vocos_instance", v)
|
| 102 |
-
except Exception:
|
|
|
|
| 103 |
object.__setattr__(self, "_vocos_instance", False)
|
| 104 |
inst = getattr(self, "_vocos_instance", None)
|
| 105 |
return inst if inst is not False else None
|
|
@@ -149,14 +150,26 @@ class NeuralVocoder(nn.Module):
|
|
| 149 |
if mel.shape[1] == 80:
|
| 150 |
proj = self._get_mel_80_to_100_proj(mel.device)
|
| 151 |
mel_in = torch.matmul(proj, mel)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
else:
|
| 153 |
mel_in = mel
|
| 154 |
wav = vocos.decode(mel_in)
|
| 155 |
if wav.ndim == 2:
|
| 156 |
wav = wav.unsqueeze(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
return wav
|
| 158 |
-
except Exception:
|
| 159 |
-
|
| 160 |
|
| 161 |
# Internal Neural Vocoder fallback
|
| 162 |
x = self.conv_pre(mel)
|
|
@@ -175,4 +188,7 @@ class NeuralVocoder(nn.Module):
|
|
| 175 |
x = F.leaky_relu(x)
|
| 176 |
x = self.conv_post(x)
|
| 177 |
x = torch.tanh(x)
|
|
|
|
|
|
|
|
|
|
| 178 |
return x
|
|
|
|
| 99 |
v = v.to(device)
|
| 100 |
v.eval()
|
| 101 |
object.__setattr__(self, "_vocos_instance", v)
|
| 102 |
+
except Exception as e:
|
| 103 |
+
print(f" [!] Vocos load warning: {e}. Internal vocoder fallback active.")
|
| 104 |
object.__setattr__(self, "_vocos_instance", False)
|
| 105 |
inst = getattr(self, "_vocos_instance", None)
|
| 106 |
return inst if inst is not False else None
|
|
|
|
| 150 |
if mel.shape[1] == 80:
|
| 151 |
proj = self._get_mel_80_to_100_proj(mel.device)
|
| 152 |
mel_in = torch.matmul(proj, mel)
|
| 153 |
+
# Roll-off air-band channels (87 to 99) naturally from channel 86
|
| 154 |
+
# Prevents zero-energy injection which log-mel treats as loud high-frequency noise!
|
| 155 |
+
roll_off = torch.linspace(0.4, 3.5, 13, device=mel.device).unsqueeze(0).unsqueeze(-1)
|
| 156 |
+
mel_in[:, 87:, :] = mel_in[:, 86:87, :].expand(-1, 13, -1) - roll_off
|
| 157 |
else:
|
| 158 |
mel_in = mel
|
| 159 |
wav = vocos.decode(mel_in)
|
| 160 |
if wav.ndim == 2:
|
| 161 |
wav = wav.unsqueeze(1)
|
| 162 |
+
# Automatic DC offset subtraction and sub-bass filtering
|
| 163 |
+
wav = wav - wav.mean(dim=-1, keepdim=True)
|
| 164 |
+
try:
|
| 165 |
+
import torchaudio
|
| 166 |
+
wav = torchaudio.functional.highpass_biquad(wav, 24000, cutoff_freq=50.0)
|
| 167 |
+
wav = wav - wav.mean(dim=-1, keepdim=True)
|
| 168 |
+
except Exception:
|
| 169 |
+
pass
|
| 170 |
return wav
|
| 171 |
+
except Exception as ex:
|
| 172 |
+
print(f" [!] Vocos decode exception: {ex}")
|
| 173 |
|
| 174 |
# Internal Neural Vocoder fallback
|
| 175 |
x = self.conv_pre(mel)
|
|
|
|
| 188 |
x = F.leaky_relu(x)
|
| 189 |
x = self.conv_post(x)
|
| 190 |
x = torch.tanh(x)
|
| 191 |
+
|
| 192 |
+
# DC offset subtraction on internal vocoder output
|
| 193 |
+
x = x - x.mean(dim=-1, keepdim=True)
|
| 194 |
return x
|
train.py
CHANGED
|
@@ -410,29 +410,7 @@ class ViuAISpeechDataset(Dataset):
|
|
| 410 |
T_text = len(tokens)
|
| 411 |
T_mel = mel.shape[-1]
|
| 412 |
|
| 413 |
-
#
|
| 414 |
-
# Content characters receive more frames; vowels get high duration, spaces/punct get micro-pauses
|
| 415 |
-
token_weights = torch.ones(T_text, dtype=torch.float32)
|
| 416 |
-
for ti, ch_id in enumerate(tokens):
|
| 417 |
-
if ch_id in (0, 1, 2, 3): # pad, unk, bos, eos
|
| 418 |
-
token_weights[ti] = 0.5
|
| 419 |
-
elif ch_id in (4, 5, 6, 7, 8, 9, 10, 11): # punctuation/space
|
| 420 |
-
token_weights[ti] = 1.0
|
| 421 |
-
else:
|
| 422 |
-
token_weights[ti] = 2.5 # speech letters/Devanagari matras
|
| 423 |
-
|
| 424 |
-
token_weights = token_weights / token_weights.sum()
|
| 425 |
-
durations = torch.clamp(torch.round(token_weights * T_mel).long(), min=1)
|
| 426 |
-
diff = T_mel - int(durations.sum().item())
|
| 427 |
-
if diff > 0:
|
| 428 |
-
durations[0] += diff
|
| 429 |
-
elif diff < 0:
|
| 430 |
-
for _ in range(abs(diff)):
|
| 431 |
-
m_idx = torch.argmax(durations)
|
| 432 |
-
if durations[m_idx] > 1:
|
| 433 |
-
durations[m_idx] -= 1
|
| 434 |
-
|
| 435 |
-
# Extract real acoustic pitch (F0) and RMS energy from speech waveform
|
| 436 |
frame_pitch, frame_energy = extract_pitch_and_energy(wav, sample_rate=24000, hop_length=256)
|
| 437 |
if frame_pitch.numel() > 0:
|
| 438 |
text_pitch = F.interpolate(
|
|
@@ -448,6 +426,39 @@ class ViuAISpeechDataset(Dataset):
|
|
| 448 |
else:
|
| 449 |
text_energy = torch.zeros(T_text, dtype=torch.float32)
|
| 450 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
# 4. In-Context Reference Audio from SAME Speaker for Zero-Shot Voice Conditioning
|
| 452 |
# Fast RAM-cached path: avoids reloading duplicate reference audio files from disk
|
| 453 |
ref_mel = None
|
|
@@ -582,7 +593,16 @@ def run_golden_benchmark(model, dataset_dir: str, tag: str, output_dir: str = "c
|
|
| 582 |
|
| 583 |
out_wav_path = os.path.join(save_dir, f"{file_tag}.wav")
|
| 584 |
audio_flat = audio_out.squeeze().detach().cpu().float()
|
| 585 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
max_amp = audio_flat.abs().max().item()
|
| 587 |
if max_amp > 1e-5:
|
| 588 |
audio_flat = audio_flat * (0.89 / max_amp)
|
|
@@ -606,7 +626,7 @@ def run_golden_benchmark(model, dataset_dir: str, tag: str, output_dir: str = "c
|
|
| 606 |
# ---------------------------------------------------------------------------
|
| 607 |
def train(
|
| 608 |
dataset_dir: str = None,
|
| 609 |
-
epochs: int =
|
| 610 |
batch_size: int = 0, # 0/auto => GPU profile default
|
| 611 |
lr: float = 1e-4,
|
| 612 |
checkpoint_dir: str = "checkpoints",
|
|
@@ -622,6 +642,7 @@ def train(
|
|
| 622 |
save_every: int = 5,
|
| 623 |
save_every_steps: int = 0,
|
| 624 |
log_every: int = 10,
|
|
|
|
| 625 |
):
|
| 626 |
is_ddp, rank, world, device = setup_distributed()
|
| 627 |
is_main = (rank == 0)
|
|
@@ -838,7 +859,7 @@ def train(
|
|
| 838 |
mel_lengths=mel_lens,
|
| 839 |
cond_drop_prob=0.15,
|
| 840 |
audio_target=batch_audios,
|
| 841 |
-
train_vocoder=
|
| 842 |
)
|
| 843 |
loss = losses["total_loss"] / accum
|
| 844 |
|
|
@@ -973,7 +994,7 @@ def train(
|
|
| 973 |
if __name__ == "__main__":
|
| 974 |
parser = argparse.ArgumentParser(description="ViuAI_TTS_200M Master Training (SOTA Mode)")
|
| 975 |
parser.add_argument("--data_dir", type=str, default=None, help="Path to dataset dir with metadata.csv")
|
| 976 |
-
parser.add_argument("--epochs", type=int, default=
|
| 977 |
parser.add_argument("--batch_size", type=int, default=0, help="Per-GPU batch size (0/auto = GPU profile default)")
|
| 978 |
parser.add_argument("--lr", type=float, default=1e-4, help="Learning rate")
|
| 979 |
parser.add_argument("--seed", type=int, default=42, help="Random seed")
|
|
@@ -989,6 +1010,7 @@ if __name__ == "__main__":
|
|
| 989 |
parser.add_argument("--save_every", type=int, default=5, help="Save checkpoint every N epochs (default: 5)")
|
| 990 |
parser.add_argument("--save_every_steps", type=int, default=0, help="Save checkpoint every N optimizer steps (0 = off)")
|
| 991 |
parser.add_argument("--log_every", type=int, default=10, help="Log every N optimizer steps")
|
|
|
|
| 992 |
args = parser.parse_args()
|
| 993 |
|
| 994 |
train(
|
|
@@ -1008,4 +1030,5 @@ if __name__ == "__main__":
|
|
| 1008 |
save_every=args.save_every,
|
| 1009 |
save_every_steps=args.save_every_steps,
|
| 1010 |
log_every=args.log_every,
|
|
|
|
| 1011 |
)
|
|
|
|
| 410 |
T_text = len(tokens)
|
| 411 |
T_mel = mel.shape[-1]
|
| 412 |
|
| 413 |
+
# Extract real acoustic pitch (F0) and RMS energy from speech waveform first
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
frame_pitch, frame_energy = extract_pitch_and_energy(wav, sample_rate=24000, hop_length=256)
|
| 415 |
if frame_pitch.numel() > 0:
|
| 416 |
text_pitch = F.interpolate(
|
|
|
|
| 426 |
else:
|
| 427 |
text_energy = torch.zeros(T_text, dtype=torch.float32)
|
| 428 |
|
| 429 |
+
# 3. Dynamic Energy-Aware Duration Allocation
|
| 430 |
+
# Content characters receive more frames; vowels get high duration, spaces/punct get micro-pauses
|
| 431 |
+
token_weights = torch.ones(T_text, dtype=torch.float32)
|
| 432 |
+
for ti, ch_id in enumerate(tokens):
|
| 433 |
+
if ch_id in (0, 1, 2, 3): # pad, unk, bos, eos
|
| 434 |
+
token_weights[ti] = 0.4
|
| 435 |
+
elif ch_id in (4, 5, 6, 7, 8, 9, 10, 11): # punctuation/space
|
| 436 |
+
token_weights[ti] = 0.9
|
| 437 |
+
else:
|
| 438 |
+
token_weights[ti] = 2.4 # speech letters/Devanagari matras
|
| 439 |
+
|
| 440 |
+
# Modulate content weights with active speech energy ratio
|
| 441 |
+
if frame_energy.numel() > 0:
|
| 442 |
+
speech_ratio = (frame_energy > 0.15).float().mean().item()
|
| 443 |
+
token_weights[token_weights > 1.0] *= (1.0 + speech_ratio * 0.4)
|
| 444 |
+
|
| 445 |
+
token_weights = token_weights / token_weights.sum()
|
| 446 |
+
durations = torch.clamp(torch.round(token_weights * T_mel).long(), min=1)
|
| 447 |
+
diff = T_mel - int(durations.sum().item())
|
| 448 |
+
|
| 449 |
+
# Distribute residual evenly across content tokens (NEVER dumping all diff into token 0!)
|
| 450 |
+
if diff > 0:
|
| 451 |
+
sort_indices = torch.argsort(token_weights, descending=True)
|
| 452 |
+
for i in range(diff):
|
| 453 |
+
durations[sort_indices[i % T_text]] += 1
|
| 454 |
+
elif diff < 0:
|
| 455 |
+
sort_indices = torch.argsort(durations, descending=True)
|
| 456 |
+
for i in range(abs(diff)):
|
| 457 |
+
for idx in sort_indices:
|
| 458 |
+
if durations[idx] > 1:
|
| 459 |
+
durations[idx] -= 1
|
| 460 |
+
break
|
| 461 |
+
|
| 462 |
# 4. In-Context Reference Audio from SAME Speaker for Zero-Shot Voice Conditioning
|
| 463 |
# Fast RAM-cached path: avoids reloading duplicate reference audio files from disk
|
| 464 |
ref_mel = None
|
|
|
|
| 593 |
|
| 594 |
out_wav_path = os.path.join(save_dir, f"{file_tag}.wav")
|
| 595 |
audio_flat = audio_out.squeeze().detach().cpu().float()
|
| 596 |
+
# Step A: DC Offset Removal (Removes massive electrical hum/bias)
|
| 597 |
+
audio_flat = audio_flat - audio_flat.mean()
|
| 598 |
+
# Step B: High-pass filtering (Remove sub-audible rumble below 50Hz)
|
| 599 |
+
try:
|
| 600 |
+
import torchaudio
|
| 601 |
+
audio_flat = torchaudio.functional.highpass_biquad(audio_flat.unsqueeze(0), 24000, cutoff_freq=50.0).squeeze(0)
|
| 602 |
+
audio_flat = audio_flat - audio_flat.mean()
|
| 603 |
+
except Exception:
|
| 604 |
+
pass
|
| 605 |
+
# Step C: Peak normalize to -1 dB (0.89) safely without amplifying DC noise
|
| 606 |
max_amp = audio_flat.abs().max().item()
|
| 607 |
if max_amp > 1e-5:
|
| 608 |
audio_flat = audio_flat * (0.89 / max_amp)
|
|
|
|
| 626 |
# ---------------------------------------------------------------------------
|
| 627 |
def train(
|
| 628 |
dataset_dir: str = None,
|
| 629 |
+
epochs: int = 200,
|
| 630 |
batch_size: int = 0, # 0/auto => GPU profile default
|
| 631 |
lr: float = 1e-4,
|
| 632 |
checkpoint_dir: str = "checkpoints",
|
|
|
|
| 642 |
save_every: int = 5,
|
| 643 |
save_every_steps: int = 0,
|
| 644 |
log_every: int = 10,
|
| 645 |
+
train_vocoder: bool = False,
|
| 646 |
):
|
| 647 |
is_ddp, rank, world, device = setup_distributed()
|
| 648 |
is_main = (rank == 0)
|
|
|
|
| 859 |
mel_lengths=mel_lens,
|
| 860 |
cond_drop_prob=0.15,
|
| 861 |
audio_target=batch_audios,
|
| 862 |
+
train_vocoder=train_vocoder,
|
| 863 |
)
|
| 864 |
loss = losses["total_loss"] / accum
|
| 865 |
|
|
|
|
| 994 |
if __name__ == "__main__":
|
| 995 |
parser = argparse.ArgumentParser(description="ViuAI_TTS_200M Master Training (SOTA Mode)")
|
| 996 |
parser.add_argument("--data_dir", type=str, default=None, help="Path to dataset dir with metadata.csv")
|
| 997 |
+
parser.add_argument("--epochs", type=int, default=200, help="Number of full training epochs (default: 200)")
|
| 998 |
parser.add_argument("--batch_size", type=int, default=0, help="Per-GPU batch size (0/auto = GPU profile default)")
|
| 999 |
parser.add_argument("--lr", type=float, default=1e-4, help="Learning rate")
|
| 1000 |
parser.add_argument("--seed", type=int, default=42, help="Random seed")
|
|
|
|
| 1010 |
parser.add_argument("--save_every", type=int, default=5, help="Save checkpoint every N epochs (default: 5)")
|
| 1011 |
parser.add_argument("--save_every_steps", type=int, default=0, help="Save checkpoint every N optimizer steps (0 = off)")
|
| 1012 |
parser.add_argument("--log_every", type=int, default=10, help="Log every N optimizer steps")
|
| 1013 |
+
parser.add_argument("--train_vocoder", action="store_true", help="Enable Phase-2 neural vocoder fine-tuning alongside acoustic DiT")
|
| 1014 |
args = parser.parse_args()
|
| 1015 |
|
| 1016 |
train(
|
|
|
|
| 1030 |
save_every=args.save_every,
|
| 1031 |
save_every_steps=args.save_every_steps,
|
| 1032 |
log_every=args.log_every,
|
| 1033 |
+
train_vocoder=args.train_vocoder,
|
| 1034 |
)
|
train_colab.ipynb
CHANGED
|
@@ -1,129 +1,130 @@
|
|
| 1 |
-
{
|
| 2 |
-
"cells": [
|
| 3 |
-
{
|
| 4 |
-
"cell_type": "markdown",
|
| 5 |
-
"metadata": {},
|
| 6 |
-
"source": [
|
| 7 |
-
"#
|
| 8 |
-
"\n",
|
| 9 |
-
"Welcome to the official training notebook for **ViuAI_TTS_200M** (~190.5M Parameters).\n",
|
| 10 |
-
"\n",
|
| 11 |
-
"###
|
| 12 |
-
"* **Optimal Transport Flow Matching (OT-CFM)**\n",
|
| 13 |
-
"* **Diffusion Transformer (DiT)** with AdaLN-Zero\n",
|
| 14 |
-
"* **Human Prosody Engine** (Dynamic F0 Pitch + Energy + Duration)\n",
|
| 15 |
-
"* **Zero-Shot Human Voice Cloning** (ElevenLabs / Grok style)\n",
|
| 16 |
-
"* **24,000 Hz Studio Audio Generation**\n",
|
| 17 |
-
"\n",
|
| 18 |
-
"---\n",
|
| 19 |
-
"###
|
| 20 |
-
"Click **Runtime** > **Change runtime type** > Select **T4 GPU** (or A100/L4 if Pro) > **Save**."
|
| 21 |
-
]
|
| 22 |
-
},
|
| 23 |
-
{
|
| 24 |
-
"cell_type": "code",
|
| 25 |
-
"execution_count": null,
|
| 26 |
-
"metadata": {},
|
| 27 |
-
"outputs": [],
|
| 28 |
-
"source": [
|
| 29 |
-
"# 1. Check GPU\n",
|
| 30 |
-
"!nvidia-smi"
|
| 31 |
-
]
|
| 32 |
-
},
|
| 33 |
-
{
|
| 34 |
-
"cell_type": "code",
|
| 35 |
-
"execution_count": null,
|
| 36 |
-
"metadata": {},
|
| 37 |
-
"outputs": [],
|
| 38 |
-
"source": [
|
| 39 |
-
"# 2. Clone ViuAI_TTS_200M Model Repository from Hugging Face\n",
|
| 40 |
-
"!git clone https://huggingface.co/ViuAI/ViuAI_TTS_200M\n",
|
| 41 |
-
"%cd ViuAI_TTS_200M\n",
|
| 42 |
-
"!ls -la"
|
| 43 |
-
]
|
| 44 |
-
},
|
| 45 |
-
{
|
| 46 |
-
"cell_type": "code",
|
| 47 |
-
"execution_count": null,
|
| 48 |
-
"metadata": {},
|
| 49 |
-
"outputs": [],
|
| 50 |
-
"source": [
|
| 51 |
-
"# 3. Install Dependencies\n",
|
| 52 |
-
"!pip install -q huggingface_hub datasets"
|
| 53 |
-
]
|
| 54 |
-
},
|
| 55 |
-
{
|
| 56 |
-
"cell_type": "code",
|
| 57 |
-
"execution_count": null,
|
| 58 |
-
"metadata": {},
|
| 59 |
-
"outputs": [],
|
| 60 |
-
"source": [
|
| 61 |
-
"# 4. Pull Dataset from Hugging Face (ViuAI/ViuAI_TTS_Dataset)\n",
|
| 62 |
-
"import os\n",
|
| 63 |
-
"from huggingface_hub import snapshot_download\n",
|
| 64 |
-
"\n",
|
| 65 |
-
"dataset_dir = \"../dataset\"\n",
|
| 66 |
-
"snapshot_download(\n",
|
| 67 |
-
" repo_id=\"ViuAI/ViuAI_TTS_Dataset\",\n",
|
| 68 |
-
" repo_type=\"dataset\",\n",
|
| 69 |
-
" local_dir=dataset_dir\n",
|
| 70 |
-
")\n",
|
| 71 |
-
"print(\"Dataset downloaded successfully to:\", dataset_dir)\n",
|
| 72 |
-
"!ls -la ../dataset"
|
| 73 |
-
]
|
| 74 |
-
},
|
| 75 |
-
{
|
| 76 |
-
"cell_type": "code",
|
| 77 |
-
"execution_count": null,
|
| 78 |
-
"metadata": {},
|
| 79 |
-
"outputs": [],
|
| 80 |
-
"source": [
|
| 81 |
-
"# 5. Verify Model Architecture & Parameter Budget on GPU\n",
|
| 82 |
-
"!python verify_model.py"
|
| 83 |
-
]
|
| 84 |
-
},
|
| 85 |
-
{
|
| 86 |
-
"cell_type": "code",
|
| 87 |
-
"execution_count": null,
|
| 88 |
-
"metadata": {},
|
| 89 |
-
"outputs": [],
|
| 90 |
-
"source": [
|
| 91 |
-
"# 6. Start Mixed Precision (AMP) GPU Training Loop\n",
|
| 92 |
-
"!python train.py"
|
| 93 |
-
]
|
| 94 |
-
},
|
| 95 |
-
{
|
| 96 |
-
"cell_type": "code",
|
| 97 |
-
"execution_count": null,
|
| 98 |
-
"metadata": {},
|
| 99 |
-
"outputs": [],
|
| 100 |
-
"source": [
|
| 101 |
-
"# 7. Test Inference & Play Synthesized Audio Directly in Colab!\n",
|
| 102 |
-
"import IPython.display as ipd\n",
|
| 103 |
-
"\n",
|
| 104 |
-
"!python inference.py \\\n",
|
| 105 |
-
" --
|
| 106 |
-
" --
|
| 107 |
-
" --
|
| 108 |
-
" --
|
| 109 |
-
"\n",
|
| 110 |
-
"
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
"
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
"
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
"
|
| 129 |
-
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# \ud83c\udf99\ufe0f ViuAI_TTS_200M: GPU Training Notebook in Google Colab\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"Welcome to the official training notebook for **ViuAI_TTS_200M** (~190.5M Parameters).\n",
|
| 10 |
+
"\n",
|
| 11 |
+
"### \ud83d\ude80 Features:\n",
|
| 12 |
+
"* **Optimal Transport Flow Matching (OT-CFM)**\n",
|
| 13 |
+
"* **Diffusion Transformer (DiT)** with AdaLN-Zero\n",
|
| 14 |
+
"* **Human Prosody Engine** (Dynamic F0 Pitch + Energy + Duration)\n",
|
| 15 |
+
"* **Zero-Shot Human Voice Cloning** (ElevenLabs / Grok style)\n",
|
| 16 |
+
"* **24,000 Hz Studio Audio Generation**\n",
|
| 17 |
+
"\n",
|
| 18 |
+
"---\n",
|
| 19 |
+
"### \u26a1 Step 0: Ensure GPU is Enabled\n",
|
| 20 |
+
"Click **Runtime** > **Change runtime type** > Select **T4 GPU** (or A100/L4 if Pro) > **Save**."
|
| 21 |
+
]
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"cell_type": "code",
|
| 25 |
+
"execution_count": null,
|
| 26 |
+
"metadata": {},
|
| 27 |
+
"outputs": [],
|
| 28 |
+
"source": [
|
| 29 |
+
"# 1. Check GPU\n",
|
| 30 |
+
"!nvidia-smi"
|
| 31 |
+
]
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
"cell_type": "code",
|
| 35 |
+
"execution_count": null,
|
| 36 |
+
"metadata": {},
|
| 37 |
+
"outputs": [],
|
| 38 |
+
"source": [
|
| 39 |
+
"# 2. Clone ViuAI_TTS_200M Model Repository from Hugging Face (skip heavy LFS checkpoints to save Colab disk)\n",
|
| 40 |
+
"!GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/ViuAI/ViuAI_TTS_200M\n",
|
| 41 |
+
"%cd ViuAI_TTS_200M\n",
|
| 42 |
+
"!ls -la"
|
| 43 |
+
]
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"cell_type": "code",
|
| 47 |
+
"execution_count": null,
|
| 48 |
+
"metadata": {},
|
| 49 |
+
"outputs": [],
|
| 50 |
+
"source": [
|
| 51 |
+
"# 3. Install All Required Dependencies (Vocos, Torchaudio, SoundFile included)\n",
|
| 52 |
+
"!pip install -q vocos huggingface_hub datasets torchaudio soundfile"
|
| 53 |
+
]
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"cell_type": "code",
|
| 57 |
+
"execution_count": null,
|
| 58 |
+
"metadata": {},
|
| 59 |
+
"outputs": [],
|
| 60 |
+
"source": [
|
| 61 |
+
"# 4. Pull Dataset from Hugging Face (ViuAI/ViuAI_TTS_Dataset)\n",
|
| 62 |
+
"import os\n",
|
| 63 |
+
"from huggingface_hub import snapshot_download\n",
|
| 64 |
+
"\n",
|
| 65 |
+
"dataset_dir = \"../dataset\"\n",
|
| 66 |
+
"snapshot_download(\n",
|
| 67 |
+
" repo_id=\"ViuAI/ViuAI_TTS_Dataset\",\n",
|
| 68 |
+
" repo_type=\"dataset\",\n",
|
| 69 |
+
" local_dir=dataset_dir\n",
|
| 70 |
+
")\n",
|
| 71 |
+
"print(\"Dataset downloaded successfully to:\", dataset_dir)\n",
|
| 72 |
+
"!ls -la ../dataset"
|
| 73 |
+
]
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"cell_type": "code",
|
| 77 |
+
"execution_count": null,
|
| 78 |
+
"metadata": {},
|
| 79 |
+
"outputs": [],
|
| 80 |
+
"source": [
|
| 81 |
+
"# 5. Verify Model Architecture & Parameter Budget on GPU\n",
|
| 82 |
+
"!python verify_model.py"
|
| 83 |
+
]
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"cell_type": "code",
|
| 87 |
+
"execution_count": null,
|
| 88 |
+
"metadata": {},
|
| 89 |
+
"outputs": [],
|
| 90 |
+
"source": [
|
| 91 |
+
"# 6. Start Mixed Precision (AMP) GPU Training Loop (Auto-resumes from latest checkpoint)\n",
|
| 92 |
+
"!python train.py --epochs 200 --resume auto --train_vocoder --push_to_hub"
|
| 93 |
+
]
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"cell_type": "code",
|
| 97 |
+
"execution_count": null,
|
| 98 |
+
"metadata": {},
|
| 99 |
+
"outputs": [],
|
| 100 |
+
"source": [
|
| 101 |
+
"# 7. Test Inference & Play Synthesized Audio Directly in Colab!\n",
|
| 102 |
+
"import IPython.display as ipd\n",
|
| 103 |
+
"\n",
|
| 104 |
+
"!python inference.py \\\n",
|
| 105 |
+
" --checkpoint checkpoints/viuai_tts_latest.pt \\\n",
|
| 106 |
+
" --text \"Namaste! Yeh ViuAI TTS 200M model ka live audio output hai jo GPU par train hua hai.\" \\\n",
|
| 107 |
+
" --cfg_scale 1.6 \\\n",
|
| 108 |
+
" --steps 20 \\\n",
|
| 109 |
+
" --output \"colab_output.wav\"\n",
|
| 110 |
+
"\n",
|
| 111 |
+
"ipd.Audio(\"colab_output.wav\")"
|
| 112 |
+
]
|
| 113 |
+
}
|
| 114 |
+
],
|
| 115 |
+
"metadata": {
|
| 116 |
+
"accelerator": "GPU",
|
| 117 |
+
"colab": {
|
| 118 |
+
"provenance": []
|
| 119 |
+
},
|
| 120 |
+
"kernelspec": {
|
| 121 |
+
"display_name": "Python 3",
|
| 122 |
+
"name": "python3"
|
| 123 |
+
},
|
| 124 |
+
"language_info": {
|
| 125 |
+
"name": "python"
|
| 126 |
+
}
|
| 127 |
+
},
|
| 128 |
+
"nbformat": 4,
|
| 129 |
+
"nbformat_minor": 0
|
| 130 |
+
}
|