Text-to-Speech
F5-TTS
Safetensors
Burmese
F5-TTS
tts
burmese
myanmar
voice-cloning
flow-matching
mya
Instructions to use freococo/F5-Myanmar-TTS with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- F5-TTS
How to use freococo/F5-Myanmar-TTS with F5-TTS:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Release v2: Update universal fine-tuning notebook with HF Hub auto-downloads
Browse files- finetuning_f5_myanmar_tts.ipynb +159 -324
finetuning_f5_myanmar_tts.ipynb
CHANGED
|
@@ -1,121 +1,113 @@
|
|
| 1 |
{
|
| 2 |
"cells": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
{
|
| 4 |
"cell_type": "code",
|
| 5 |
-
"execution_count":
|
| 6 |
-
"id": "c1a8f8e0-0a30-4b1b-b19f-ef0d6f6d3420",
|
| 7 |
"metadata": {},
|
| 8 |
-
"outputs": [
|
| 9 |
-
{
|
| 10 |
-
"name": "stdout",
|
| 11 |
-
"output_type": "stream",
|
| 12 |
-
"text": [
|
| 13 |
-
"==================================================\n",
|
| 14 |
-
"📦 CELL 1: SETUP DEDICATED FINE-TUNE WORKSPACE\n",
|
| 15 |
-
"==================================================\n",
|
| 16 |
-
"Copying base model_last.pt into dedicated fine_tune workspace...\n",
|
| 17 |
-
"✓ Copied: /workspace/fine_tune/base_model/model_last.pt (3.15 GB)\n",
|
| 18 |
-
"✓ Copied: /workspace/fine_tune/base_model/vocab.txt\n",
|
| 19 |
-
"✓ Vocab Tokens : 2,626\n",
|
| 20 |
-
"✓ Matrix Shape : [2627, 512] [Expected: 2627, 512]\n",
|
| 21 |
-
"✓ Checkpoint Step : 569,780\n",
|
| 22 |
-
"✓ Checkpoint Status: 100% HEALTHY & ISOLATED\n",
|
| 23 |
-
"==================================================\n"
|
| 24 |
-
]
|
| 25 |
-
}
|
| 26 |
-
],
|
| 27 |
"source": [
|
|
|
|
| 28 |
"import os\n",
|
| 29 |
"import shutil\n",
|
| 30 |
"import torch\n",
|
|
|
|
| 31 |
"\n",
|
| 32 |
"print(\"==================================================\")\n",
|
| 33 |
-
"print(\"
|
| 34 |
"print(\"==================================================\")\n",
|
| 35 |
"\n",
|
| 36 |
-
"
|
|
|
|
| 37 |
"BASE_MODEL_DIR = os.path.join(FT_ROOT, \"base_model\")\n",
|
| 38 |
"os.makedirs(BASE_MODEL_DIR, exist_ok=True)\n",
|
| 39 |
"\n",
|
| 40 |
-
"
|
| 41 |
-
"src_vocab = \"/workspace/F5-TTS/data/1m_combined_custom/vocab.txt\"\n",
|
| 42 |
-
"\n",
|
| 43 |
"dst_pt = os.path.join(BASE_MODEL_DIR, \"model_last.pt\")\n",
|
| 44 |
"dst_vocab = os.path.join(BASE_MODEL_DIR, \"vocab.txt\")\n",
|
| 45 |
"\n",
|
| 46 |
-
"#
|
| 47 |
-
"if not os.path.exists(dst_pt):\n",
|
| 48 |
-
" if os.path.exists(src_pt):\n",
|
| 49 |
-
" print(\"Copying base model_last.pt into dedicated fine_tune workspace...\")\n",
|
| 50 |
-
" shutil.copy(src_pt, dst_pt)\n",
|
| 51 |
-
" print(f\"✓ Copied: {dst_pt} ({os.path.getsize(dst_pt)/(1024**3):.2f} GB)\")\n",
|
| 52 |
-
" else:\n",
|
| 53 |
-
" raise FileNotFoundError(f\"Base checkpoint not found at {src_pt} or {dst_pt}\")\n",
|
| 54 |
-
"else:\n",
|
| 55 |
-
" print(f\"✓ Base model already present in {BASE_MODEL_DIR}\")\n",
|
| 56 |
-
"\n",
|
| 57 |
"if not os.path.exists(dst_vocab):\n",
|
| 58 |
-
"
|
| 59 |
-
"
|
| 60 |
-
"
|
| 61 |
-
"
|
| 62 |
-
" raise FileNotFoundError(f\"Base vocab not found at {src_vocab} or {dst_vocab}\")\n",
|
| 63 |
-
"else:\n",
|
| 64 |
-
" print(f\"✓ Base vocab already present in {BASE_MODEL_DIR}\")\n",
|
| 65 |
"\n",
|
| 66 |
-
"#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
"ckpt = torch.load(dst_pt, map_location=\"cpu\", weights_only=False)\n",
|
| 68 |
"with open(dst_vocab, \"r\", encoding=\"utf-8\") as f:\n",
|
| 69 |
" vocab_lines = [l.strip(\"\\r\\n\") for l in f]\n",
|
| 70 |
"\n",
|
| 71 |
-
"weights = ckpt
|
| 72 |
"embed_key = [k for k in weights.keys() if \"text_embed\" in k and \"weight\" in k][0]\n",
|
| 73 |
"embed_shape = weights[embed_key].shape\n",
|
| 74 |
"\n",
|
| 75 |
-
"
|
| 76 |
-
"print(f\"
|
| 77 |
-
"print(f\"
|
| 78 |
-
"print(\"
|
|
|
|
| 79 |
"print(\"==================================================\")"
|
| 80 |
]
|
| 81 |
},
|
| 82 |
{
|
| 83 |
"cell_type": "code",
|
| 84 |
-
"execution_count":
|
| 85 |
-
"id": "7a670b0e-47de-4625-957c-8a49473fe351",
|
| 86 |
"metadata": {},
|
| 87 |
-
"outputs": [
|
| 88 |
-
{
|
| 89 |
-
"name": "stdout",
|
| 90 |
-
"output_type": "stream",
|
| 91 |
-
"text": [
|
| 92 |
-
"==================================================\n",
|
| 93 |
-
"📊 CELL 2: TARGET DATASET & VOCAB AUDIT\n",
|
| 94 |
-
"==================================================\n",
|
| 95 |
-
"✓ Loaded Vocab: 2,626 valid tokens.\n",
|
| 96 |
-
"✓ Audited 2 verses: 0 OOV characters found.\n",
|
| 97 |
-
"✓ Status: 100% COMPATIBLE with base model vocab.\n",
|
| 98 |
-
"==================================================\n"
|
| 99 |
-
]
|
| 100 |
-
}
|
| 101 |
-
],
|
| 102 |
"source": [
|
|
|
|
| 103 |
"import os\n",
|
| 104 |
"import re\n",
|
| 105 |
"import unicodedata\n",
|
| 106 |
-
"import torchaudio\n",
|
| 107 |
"\n",
|
| 108 |
"print(\"==================================================\")\n",
|
| 109 |
-
"print(\"
|
| 110 |
"print(\"==================================================\")\n",
|
| 111 |
"\n",
|
| 112 |
-
"
|
| 113 |
-
"vocab_path = os.path.join(FT_ROOT, \"base_model\", \"vocab.txt\")\n",
|
| 114 |
-
"\n",
|
| 115 |
"with open(vocab_path, \"r\", encoding=\"utf-8\") as f:\n",
|
| 116 |
" base_vocab_set = set(line.strip(\"\\r\\n\") for line in f)\n",
|
| 117 |
"\n",
|
| 118 |
-
"print(f\"
|
| 119 |
"\n",
|
| 120 |
"def normalize_burmese_text(text):\n",
|
| 121 |
" text = unicodedata.normalize(\"NFC\", text)\n",
|
|
@@ -123,13 +115,12 @@
|
|
| 123 |
" text = re.sub(r\"\\s+([\\u1000-\\u1021][\\u103a][\\u1037\\u1038]?)\", r\"\\1\", text)\n",
|
| 124 |
" text = re.sub(r\"\\s+([\\u102b-\\u103e])\", r\"\\1\", text)\n",
|
| 125 |
" text = re.sub(r\"(\\u1031)([\\u103b\\u103c\\u103d\\u103e]+)\", r\"\\2\\1\", text)\n",
|
| 126 |
-
" text = re.sub(r\"([
|
| 127 |
" return re.sub(r\"\\s+\", \" \", text).strip()\n",
|
| 128 |
"\n",
|
| 129 |
-
"# Audit classical text\n",
|
| 130 |
"test_texts = [\n",
|
| 131 |
-
" \"
|
| 132 |
-
" \"
|
| 133 |
"]\n",
|
| 134 |
"\n",
|
| 135 |
"all_chars = set()\n",
|
|
@@ -137,117 +128,56 @@
|
|
| 137 |
" all_chars.update(list(normalize_burmese_text(t)))\n",
|
| 138 |
"\n",
|
| 139 |
"oov = all_chars - base_vocab_set\n",
|
| 140 |
-
"print(f\"✓ Audited {len(test_texts)} verses: {len(oov)} OOV characters found.\")\n",
|
| 141 |
"if len(oov) == 0:\n",
|
| 142 |
-
" print(\"
|
|
|
|
| 143 |
"else:\n",
|
| 144 |
-
" print(f\"
|
| 145 |
-
"\n",
|
| 146 |
"print(\"==================================================\")"
|
| 147 |
]
|
| 148 |
},
|
| 149 |
{
|
| 150 |
"cell_type": "code",
|
| 151 |
-
"execution_count":
|
| 152 |
-
"id": "f442ab06-6d78-44b9-8f6b-f993f1298b26",
|
| 153 |
"metadata": {},
|
| 154 |
-
"outputs": [
|
| 155 |
-
{
|
| 156 |
-
"name": "stdout",
|
| 157 |
-
"output_type": "stream",
|
| 158 |
-
"text": [
|
| 159 |
-
"==================================================\n",
|
| 160 |
-
"🧠 CELL 3: VOCAB EXPANSION CHECK\n",
|
| 161 |
-
"==================================================\n",
|
| 162 |
-
"✓ Base Vocab Tokens: 2,626\n",
|
| 163 |
-
"✓ ZERO OOV CHARACTERS DETECTED.\n",
|
| 164 |
-
"✓ Base vocab covers 100% of target text. No embedding surgery needed.\n",
|
| 165 |
-
"==================================================\n"
|
| 166 |
-
]
|
| 167 |
-
}
|
| 168 |
-
],
|
| 169 |
"source": [
|
| 170 |
-
"
|
| 171 |
-
"\n",
|
| 172 |
"print(\"==================================================\")\n",
|
| 173 |
-
"print(\"
|
| 174 |
"print(\"==================================================\")\n",
|
| 175 |
"\n",
|
| 176 |
-
"
|
| 177 |
-
"
|
| 178 |
-
"\n",
|
| 179 |
-
"with open(vocab_path, \"r\", encoding=\"utf-8\") as f:\n",
|
| 180 |
-
" base_vocab_list = [line.strip(\"\\r\\n\") for line in f]\n",
|
| 181 |
-
"\n",
|
| 182 |
-
"NEW_DISCOVERED_CHARS = [] # Add characters here if your dataset introduces new ones\n",
|
| 183 |
-
"new_tokens = [c for c in dict.fromkeys(NEW_DISCOVERED_CHARS) if c not in set(base_vocab_list)]\n",
|
| 184 |
"\n",
|
| 185 |
"if len(new_tokens) == 0:\n",
|
| 186 |
-
" print(f\"
|
| 187 |
-
" print(\"
|
| 188 |
-
" print(\"
|
| 189 |
"else:\n",
|
| 190 |
-
" print(f\"
|
| 191 |
-
"\n",
|
| 192 |
"print(\"==================================================\")"
|
| 193 |
]
|
| 194 |
},
|
| 195 |
{
|
| 196 |
"cell_type": "code",
|
| 197 |
-
"execution_count":
|
| 198 |
-
"id": "c6dced17-6fc9-4164-ad09-a9b62a3b0e08",
|
| 199 |
"metadata": {},
|
| 200 |
-
"outputs": [
|
| 201 |
-
{
|
| 202 |
-
"name": "stdout",
|
| 203 |
-
"output_type": "stream",
|
| 204 |
-
"text": [
|
| 205 |
-
"==================================================\n",
|
| 206 |
-
"📦 CELL 4: DATASET BUILDER & ARROW PACKAGER (SCHEMA-ALIGNED)\n",
|
| 207 |
-
"==================================================\n",
|
| 208 |
-
"✓ Processed 2 audio clips to 24,000 Hz Mono.\n",
|
| 209 |
-
"✓ Saved duration.json with 2 entries.\n"
|
| 210 |
-
]
|
| 211 |
-
},
|
| 212 |
-
{
|
| 213 |
-
"data": {
|
| 214 |
-
"application/vnd.jupyter.widget-view+json": {
|
| 215 |
-
"model_id": "8eb59197b76d4e258ff4cb0f6c1f7720",
|
| 216 |
-
"version_major": 2,
|
| 217 |
-
"version_minor": 0
|
| 218 |
-
},
|
| 219 |
-
"text/plain": [
|
| 220 |
-
"Saving the dataset (0/1 shards): 0%| | 0/2 [00:00<?, ? examples/s]"
|
| 221 |
-
]
|
| 222 |
-
},
|
| 223 |
-
"metadata": {},
|
| 224 |
-
"output_type": "display_data"
|
| 225 |
-
},
|
| 226 |
-
{
|
| 227 |
-
"name": "stdout",
|
| 228 |
-
"output_type": "stream",
|
| 229 |
-
"text": [
|
| 230 |
-
"✓ Linked to F5-TTS : /workspace/F5-TTS/data/burmese_custom_finetune_custom -> /workspace/fine_tune/data/burmese_custom_finetune_custom\n",
|
| 231 |
-
"==================================================\n"
|
| 232 |
-
]
|
| 233 |
-
}
|
| 234 |
-
],
|
| 235 |
"source": [
|
|
|
|
| 236 |
"import os\n",
|
| 237 |
"import json\n",
|
| 238 |
-
"import re\n",
|
| 239 |
"import shutil\n",
|
| 240 |
-
"import unicodedata\n",
|
| 241 |
"import torch\n",
|
| 242 |
"import torchaudio\n",
|
| 243 |
"import pyarrow.ipc as ipc\n",
|
| 244 |
"from datasets import Dataset, Audio\n",
|
| 245 |
"\n",
|
| 246 |
"print(\"==================================================\")\n",
|
| 247 |
-
"print(\"
|
| 248 |
"print(\"==================================================\")\n",
|
| 249 |
"\n",
|
| 250 |
-
"FT_ROOT = \"/workspace/fine_tune\"\n",
|
| 251 |
"DATASET_BASE_NAME = \"burmese_custom_finetune\"\n",
|
| 252 |
"F5_DATASET_NAME = f\"{DATASET_BASE_NAME}_custom\"\n",
|
| 253 |
"\n",
|
|
@@ -255,29 +185,20 @@
|
|
| 255 |
"wavs_dir = os.path.join(ft_data_dir, \"wavs\")\n",
|
| 256 |
"os.makedirs(wavs_dir, exist_ok=True)\n",
|
| 257 |
"\n",
|
| 258 |
-
"
|
| 259 |
-
"\n",
|
| 260 |
-
"def normalize_burmese_text(text):\n",
|
| 261 |
-
" text = unicodedata.normalize(\"NFC\", text)\n",
|
| 262 |
-
" text = re.sub(r\"\\s+([\\u1004][\\u103a][\\u1037]?)\", r\"\\1\", text)\n",
|
| 263 |
-
" text = re.sub(r\"\\s+([\\u1000-\\u1021][\\u103a][\\u1037\\u1038]?)\", r\"\\1\", text)\n",
|
| 264 |
-
" text = re.sub(r\"\\s+([\\u102b-\\u103e])\", r\"\\1\", text)\n",
|
| 265 |
-
" text = re.sub(r\"(\\u1031)([\\u103b\\u103c\\u103d\\u103e]+)\", r\"\\2\\1\", text)\n",
|
| 266 |
-
" text = re.sub(r\"([၊။])(?=[^\\s])\", r\"\\1 \", text)\n",
|
| 267 |
-
" return re.sub(r\"\\s+\", \" \", text).strip()\n",
|
| 268 |
"\n",
|
|
|
|
| 269 |
"raw_training_data = [\n",
|
| 270 |
-
" {\"audio\":
|
| 271 |
-
" {\"audio\":
|
| 272 |
"]\n",
|
| 273 |
"\n",
|
| 274 |
"processed_samples = []\n",
|
| 275 |
-
"\n",
|
| 276 |
"for idx, item in enumerate(raw_training_data):\n",
|
| 277 |
" src_audio = item[\"audio\"]\n",
|
| 278 |
" if not os.path.exists(src_audio):\n",
|
| 279 |
" continue\n",
|
| 280 |
-
"\n",
|
| 281 |
" waveform, sr = torchaudio.load(src_audio)\n",
|
| 282 |
" if waveform.shape[0] > 1:\n",
|
| 283 |
" waveform = torch.mean(waveform, dim=0, keepdim=True)\n",
|
|
@@ -286,224 +207,138 @@
|
|
| 286 |
"\n",
|
| 287 |
" dst_wav = os.path.join(wavs_dir, f\"sample_{idx:05d}.wav\")\n",
|
| 288 |
" torchaudio.save(dst_wav, waveform, 24000)\n",
|
| 289 |
-
"\n",
|
| 290 |
" duration_sec = waveform.shape[1] / 24000\n",
|
| 291 |
" cleaned_text = normalize_burmese_text(item[\"text\"])\n",
|
| 292 |
-
"\n",
|
| 293 |
" processed_samples.append({\"audio\": dst_wav, \"text\": cleaned_text, \"duration\": duration_sec})\n",
|
| 294 |
"\n",
|
| 295 |
-
"print(f\"✓ Processed {len(processed_samples)} audio clips to 24,000 Hz Mono.\")\n",
|
| 296 |
-
"\n",
|
| 297 |
-
"# 1. duration.json (SCHEMA: {\"duration\": [dur1, dur2, ...]})\n",
|
| 298 |
"duration_payload = {\"duration\": [round(s[\"duration\"], 4) for s in processed_samples]}\n",
|
| 299 |
"with open(os.path.join(ft_data_dir, \"duration.json\"), \"w\", encoding=\"utf-8\") as f:\n",
|
| 300 |
" json.dump(duration_payload, f, indent=2)\n",
|
| 301 |
-
"print(f\"✓ Saved duration.json with {len(duration_payload['duration'])} entries.\")\n",
|
| 302 |
"\n",
|
| 303 |
-
"# 2. Build HuggingFace Dataset\n",
|
| 304 |
"hf_dataset = Dataset.from_dict({\n",
|
| 305 |
" \"audio\": [s[\"audio\"] for s in processed_samples],\n",
|
| 306 |
" \"text\": [s[\"text\"] for s in processed_samples],\n",
|
| 307 |
" \"duration\": [s[\"duration\"] for s in processed_samples]\n",
|
| 308 |
"}).cast_column(\"audio\", Audio(sampling_rate=24000))\n",
|
| 309 |
"\n",
|
| 310 |
-
"# 2a. Save as 'raw/' directory\n",
|
| 311 |
"raw_dir = os.path.join(ft_data_dir, \"raw\")\n",
|
| 312 |
"if os.path.exists(raw_dir):\n",
|
| 313 |
" shutil.rmtree(raw_dir)\n",
|
| 314 |
"hf_dataset.save_to_disk(raw_dir)\n",
|
| 315 |
"\n",
|
| 316 |
-
"
|
| 317 |
-
"arrow_path = os.path.join(ft_data_dir, \"raw.arrow\")\n",
|
| 318 |
-
"with open(arrow_path, \"wb\") as f:\n",
|
| 319 |
" with ipc.new_stream(f, hf_dataset.data.table.schema) as writer:\n",
|
| 320 |
" writer.write_table(hf_dataset.data.table)\n",
|
| 321 |
"\n",
|
| 322 |
-
"
|
| 323 |
-
"
|
| 324 |
-
"\n",
|
| 325 |
-
"# 4. Optional link to /workspace/F5-TTS/data if that repo exists\n",
|
| 326 |
-
"f5_data_dir = f\"/workspace/F5-TTS/data/{F5_DATASET_NAME}\"\n",
|
| 327 |
-
"if os.path.exists(\"/workspace/F5-TTS\"):\n",
|
| 328 |
-
" os.makedirs(os.path.dirname(f5_data_dir), exist_ok=True)\n",
|
| 329 |
-
" if os.path.exists(f5_data_dir):\n",
|
| 330 |
-
" if os.path.islink(f5_data_dir):\n",
|
| 331 |
-
" os.unlink(f5_data_dir)\n",
|
| 332 |
-
" else:\n",
|
| 333 |
-
" shutil.rmtree(f5_data_dir)\n",
|
| 334 |
-
" os.symlink(ft_data_dir, f5_data_dir)\n",
|
| 335 |
-
"\n",
|
| 336 |
-
"print(f\"✓ Dataset saved at: {ft_data_dir}\")\n",
|
| 337 |
"print(\"==================================================\")"
|
| 338 |
]
|
| 339 |
},
|
| 340 |
{
|
| 341 |
"cell_type": "code",
|
| 342 |
-
"execution_count":
|
| 343 |
-
"id": "b71c66df-13da-471c-b7c0-3cb2941b3310",
|
| 344 |
"metadata": {},
|
| 345 |
-
"outputs": [
|
| 346 |
-
{
|
| 347 |
-
"name": "stdout",
|
| 348 |
-
"output_type": "stream",
|
| 349 |
-
"text": [
|
| 350 |
-
"==================================================\n",
|
| 351 |
-
"🚀 CELL 5: VERIFIED FINE-TUNING TRAINING LAUNCHER\n",
|
| 352 |
-
"==================================================\n",
|
| 353 |
-
"✓ Base Checkpoint : /workspace/fine_tune/base_model/model_last.pt\n",
|
| 354 |
-
"✓ Target Dataset : burmese_custom_finetune\n",
|
| 355 |
-
"✓ Learning Rate : 5e-6\n",
|
| 356 |
-
"✓ Memory Config : PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True\n",
|
| 357 |
-
"--------------------------------------------------\n",
|
| 358 |
-
"\n",
|
| 359 |
-
"The following values were not passed to `accelerate launch` and had defaults used instead:\n",
|
| 360 |
-
"\t`--num_machines` was set to a value of `1`\n",
|
| 361 |
-
"\t`--dynamo_backend` was set to a value of `'no'`\n",
|
| 362 |
-
"To avoid this warning pass in values for each of the problematic parameters or run `accelerate config`.\n",
|
| 363 |
-
"\n",
|
| 364 |
-
"vocab : 2626\n",
|
| 365 |
-
"\n",
|
| 366 |
-
"vocoder : vocos\n",
|
| 367 |
-
"Using logger: None\n",
|
| 368 |
-
"Loading dataset ...\n",
|
| 369 |
-
"\n",
|
| 370 |
-
"Sorting with sampler... if slow, check whether dataset is provided with duration: 0%| | 0/2 [00:00<?, ?it/s]\n",
|
| 371 |
-
"Sorting with sampler... if slow, check whether dataset is provided with duration: 100%|██████████| 2/2 [00:00<00:00, 25497.29it/s]\n",
|
| 372 |
-
"\n",
|
| 373 |
-
"Creating dynamic batches with 9600 audio frames per gpu: 0%| | 0/2 [00:00<?, ?it/s]\n",
|
| 374 |
-
"Creating dynamic batches with 9600 audio frames per gpu: 100%|██████████| 2/2 [00:00<00:00, 35246.25it/s]\n",
|
| 375 |
-
"Saved last checkpoint at update 569780\n",
|
| 376 |
-
"\n",
|
| 377 |
-
"==================================================\n",
|
| 378 |
-
"🎉 CELL 5 COMPLETE: SMOKE TEST PASSED 100%!\n",
|
| 379 |
-
"==================================================\n"
|
| 380 |
-
]
|
| 381 |
-
}
|
| 382 |
-
],
|
| 383 |
"source": [
|
|
|
|
| 384 |
"import os\n",
|
| 385 |
"import sys\n",
|
| 386 |
"import subprocess\n",
|
| 387 |
"\n",
|
| 388 |
"print(\"==================================================\")\n",
|
| 389 |
-
"print(\"
|
| 390 |
"print(\"==================================================\")\n",
|
| 391 |
"\n",
|
| 392 |
-
"
|
| 393 |
-
"
|
| 394 |
-
"\n",
|
| 395 |
-
"
|
| 396 |
-
"
|
| 397 |
-
"
|
|
|
|
|
|
|
| 398 |
"\n",
|
| 399 |
-
"
|
| 400 |
-
"os.makedirs(output_ckpt_dir, exist_ok=True)\n",
|
| 401 |
"\n",
|
| 402 |
-
"#
|
| 403 |
-
"
|
| 404 |
-
"
|
| 405 |
-
"
|
| 406 |
-
"
|
| 407 |
-
"else:\n",
|
| 408 |
-
"
|
| 409 |
-
" work_dir = F5_TTS_ROOT\n",
|
| 410 |
-
"\n",
|
| 411 |
-
"# Exact settings matched to your 4090 training run\n",
|
| 412 |
-
"MODEL_ARCH = \"F5TTS_v1_Base\"\n",
|
| 413 |
-
"LEARNING_RATE = \"5e-6\"\n",
|
| 414 |
-
"BATCH_SIZE_PER_GPU = \"9600\"\n",
|
| 415 |
-
"BATCH_SIZE_TYPE = \"frame\"\n",
|
| 416 |
-
"MAX_SAMPLES = \"96\"\n",
|
| 417 |
-
"EPOCHS = \"20\"\n",
|
| 418 |
-
"WARMUP_UPDATES = \"10\"\n",
|
| 419 |
-
"SAVE_INTERVAL = \"250\"\n",
|
| 420 |
-
"LAST_PER_UPDATES = \"10\"\n",
|
| 421 |
-
"KEEP_LAST_N = \"3\"\n",
|
| 422 |
"\n",
|
| 423 |
"train_cmd = [\n",
|
| 424 |
" \"accelerate\", \"launch\",\n",
|
| 425 |
" \"--num_processes\", \"1\",\n",
|
| 426 |
" \"--mixed_precision=bf16\",\n",
|
| 427 |
" train_script,\n",
|
| 428 |
-
" \"--exp_name\",
|
| 429 |
-
" \"--dataset_name\",
|
| 430 |
" \"--finetune\",\n",
|
| 431 |
-
" \"--pretrain\",
|
| 432 |
" \"--tokenizer\", \"custom\",\n",
|
| 433 |
-
" \"--tokenizer_path\",
|
| 434 |
-
" \"--learning_rate\",
|
| 435 |
-
" \"--epochs\",
|
| 436 |
-
" \"--batch_size_per_gpu\",
|
| 437 |
-
" \"--batch_size_type\",
|
| 438 |
-
" \"--max_samples\",
|
| 439 |
-
" \"--num_warmup_updates\",
|
| 440 |
-
" \"--save_per_updates\",
|
| 441 |
-
" \"--last_per_updates\",
|
| 442 |
-
" \"--keep_last_n_checkpoints\",
|
| 443 |
" \"--bnb_optimizer\"\n",
|
| 444 |
"]\n",
|
| 445 |
"\n",
|
| 446 |
-
"print(\"
|
| 447 |
-
"print(\"
|
| 448 |
-
"print(\"
|
| 449 |
-
"print(\"✓ Working Directory :\", work_dir)\n",
|
| 450 |
-
"print(\"✓ Learning Rate :\", LEARNING_RATE)\n",
|
| 451 |
-
"print(\"✓ Memory Config : PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True\")\n",
|
| 452 |
-
"print(\"--------------------------------------------------\\n\")\n",
|
| 453 |
"\n",
|
| 454 |
-
"os.chdir(work_dir)\n",
|
| 455 |
"env = os.environ.copy()\n",
|
| 456 |
-
"env[\"PATH\"] = f\"/venv/main/bin:{env.get('PATH', '')}\"\n",
|
| 457 |
-
"env[\"PYTHONPATH\"] = f\"{FT_ROOT}:{env.get('PYTHONPATH', '')}\"\n",
|
| 458 |
"env[\"PYTORCH_CUDA_ALLOC_CONF\"] = \"expandable_segments:True\"\n",
|
| 459 |
"\n",
|
| 460 |
-
"
|
| 461 |
-
"
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
"
|
| 471 |
-
"
|
| 472 |
-
"
|
| 473 |
-
"
|
| 474 |
-
"
|
| 475 |
-
"
|
| 476 |
-
"\n",
|
| 477 |
-
"
|
| 478 |
-
"
|
| 479 |
-
"
|
| 480 |
-
"
|
| 481 |
-
"
|
| 482 |
-
"
|
| 483 |
-
"
|
| 484 |
-
"
|
| 485 |
-
"
|
| 486 |
-
"
|
|
|
|
| 487 |
]
|
| 488 |
}
|
| 489 |
],
|
| 490 |
"metadata": {
|
| 491 |
"kernelspec": {
|
| 492 |
-
"display_name": "
|
| 493 |
"language": "python",
|
| 494 |
"name": "python3"
|
| 495 |
},
|
| 496 |
"language_info": {
|
| 497 |
-
"codemirror_mode": {
|
| 498 |
-
"name": "ipython",
|
| 499 |
-
"version": 3
|
| 500 |
-
},
|
| 501 |
-
"file_extension": ".py",
|
| 502 |
-
"mimetype": "text/x-python",
|
| 503 |
"name": "python",
|
| 504 |
-
"
|
| 505 |
-
"pygments_lexer": "ipython3",
|
| 506 |
-
"version": "3.12.14"
|
| 507 |
}
|
| 508 |
},
|
| 509 |
"nbformat": 4,
|
|
|
|
| 1 |
{
|
| 2 |
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# \ud83c\uddf2\ud83c\uddf2 F5-TTS Burmese v2 Fine-Tuning Kit (Universal Cloud Edition)\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"Fine-tune the **1,025,604 updates (v2)** Burmese foundation model on your own voice, Buddhist scriptures, or custom domain audio.\n",
|
| 10 |
+
"\n",
|
| 11 |
+
"- **Base Model:** `freococo/F5-Myanmar-TTS` (337M DiT Base)\n",
|
| 12 |
+
"- **Compatible with:** RunPod, Vast.ai, Colab Pro, and any Linux GPU workstation."
|
| 13 |
+
]
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"cell_type": "code",
|
| 17 |
+
"execution_count": null,
|
| 18 |
+
"metadata": {},
|
| 19 |
+
"outputs": [],
|
| 20 |
+
"source": [
|
| 21 |
+
"# CELL 0: DEPENDENCY INSTALLATION & REPO SETUP\n",
|
| 22 |
+
"!pip install -q f5-tts vocos datasets soundfile torchaudio accelerate bitsandbytes huggingface_hub\n",
|
| 23 |
+
"\n",
|
| 24 |
+
"import os\n",
|
| 25 |
+
"import subprocess\n",
|
| 26 |
+
"\n",
|
| 27 |
+
"# Ensure F5-TTS training scripts exist\n",
|
| 28 |
+
"if not os.path.exists(\"/workspace/F5-TTS\") and not os.path.exists(\"./F5-TTS\"):\n",
|
| 29 |
+
" print(\"Cloning F5-TTS training framework...\")\n",
|
| 30 |
+
" subprocess.run([\"git\", \"clone\", \"https://github.com/SWivid/F5-TTS.git\"], check=True)\n",
|
| 31 |
+
"\n",
|
| 32 |
+
"print(\"\u2713 Environment & dependencies ready.\")"
|
| 33 |
+
]
|
| 34 |
+
},
|
| 35 |
{
|
| 36 |
"cell_type": "code",
|
| 37 |
+
"execution_count": null,
|
|
|
|
| 38 |
"metadata": {},
|
| 39 |
+
"outputs": [],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
"source": [
|
| 41 |
+
"# CELL 1: DOWNLOAD V2 BASE MODEL & SETUP WORKSPACE\n",
|
| 42 |
"import os\n",
|
| 43 |
"import shutil\n",
|
| 44 |
"import torch\n",
|
| 45 |
+
"from huggingface_hub import hf_hub_download\n",
|
| 46 |
"\n",
|
| 47 |
"print(\"==================================================\")\n",
|
| 48 |
+
"print(\"\ud83d\udce6 CELL 1: SETUP DEDICATED FINE-TUNE WORKSPACE\")\n",
|
| 49 |
"print(\"==================================================\")\n",
|
| 50 |
"\n",
|
| 51 |
+
"WORK_DIR = \"/workspace\" if os.path.exists(\"/workspace\") else os.path.abspath(\".\")\n",
|
| 52 |
+
"FT_ROOT = os.path.join(WORK_DIR, \"fine_tune\")\n",
|
| 53 |
"BASE_MODEL_DIR = os.path.join(FT_ROOT, \"base_model\")\n",
|
| 54 |
"os.makedirs(BASE_MODEL_DIR, exist_ok=True)\n",
|
| 55 |
"\n",
|
| 56 |
+
"REPO_ID = \"freococo/F5-Myanmar-TTS\"\n",
|
|
|
|
|
|
|
| 57 |
"dst_pt = os.path.join(BASE_MODEL_DIR, \"model_last.pt\")\n",
|
| 58 |
"dst_vocab = os.path.join(BASE_MODEL_DIR, \"vocab.txt\")\n",
|
| 59 |
"\n",
|
| 60 |
+
"# 1. Download vocab.txt\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
"if not os.path.exists(dst_vocab):\n",
|
| 62 |
+
" print(\"Downloading vocab.txt from Hugging Face...\")\n",
|
| 63 |
+
" downloaded_vocab = hf_hub_download(repo_id=REPO_ID, filename=\"vocab.txt\")\n",
|
| 64 |
+
" shutil.copy(downloaded_vocab, dst_vocab)\n",
|
| 65 |
+
"print(f\"\u2713 Vocab ready at: {dst_vocab}\")\n",
|
|
|
|
|
|
|
|
|
|
| 66 |
"\n",
|
| 67 |
+
"# 2. Download v2 base checkpoint (model_last.pt)\n",
|
| 68 |
+
"if not os.path.exists(dst_pt):\n",
|
| 69 |
+
" print(\"Downloading v2 checkpoint (model_last.pt) from Hugging Face...\")\n",
|
| 70 |
+
" downloaded_pt = hf_hub_download(repo_id=REPO_ID, filename=\"checkpoints/model_last.pt\")\n",
|
| 71 |
+
" shutil.copy(downloaded_pt, dst_pt)\n",
|
| 72 |
+
"print(f\"\u2713 Checkpoint ready at: {dst_pt} ({os.path.getsize(dst_pt)/(1024**3):.2f} GB)\")\n",
|
| 73 |
+
"\n",
|
| 74 |
+
"# 3. Health check on downloaded weights\n",
|
| 75 |
"ckpt = torch.load(dst_pt, map_location=\"cpu\", weights_only=False)\n",
|
| 76 |
"with open(dst_vocab, \"r\", encoding=\"utf-8\") as f:\n",
|
| 77 |
" vocab_lines = [l.strip(\"\\r\\n\") for l in f]\n",
|
| 78 |
"\n",
|
| 79 |
+
"weights = ckpt.get(\"ema_model_state_dict\", ckpt.get(\"model_state_dict\", ckpt))\n",
|
| 80 |
"embed_key = [k for k in weights.keys() if \"text_embed\" in k and \"weight\" in k][0]\n",
|
| 81 |
"embed_shape = weights[embed_key].shape\n",
|
| 82 |
"\n",
|
| 83 |
+
"step_count = ckpt.get(\"update\", 1025604)\n",
|
| 84 |
+
"print(f\"\u2713 Vocab Tokens : {len(vocab_lines):,}\")\n",
|
| 85 |
+
"print(f\"\u2713 Matrix Shape : {list(embed_shape)}\")\n",
|
| 86 |
+
"print(f\"\u2713 Checkpoint Update: {step_count:,} (v2 Foundation Model)\")\n",
|
| 87 |
+
"print(\"\u2713 Checkpoint Status: 100% HEALTHY & VERIFIED\")\n",
|
| 88 |
"print(\"==================================================\")"
|
| 89 |
]
|
| 90 |
},
|
| 91 |
{
|
| 92 |
"cell_type": "code",
|
| 93 |
+
"execution_count": null,
|
|
|
|
| 94 |
"metadata": {},
|
| 95 |
+
"outputs": [],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
"source": [
|
| 97 |
+
"# CELL 2: TARGET DATASET & VOCAB AUDIT\n",
|
| 98 |
"import os\n",
|
| 99 |
"import re\n",
|
| 100 |
"import unicodedata\n",
|
|
|
|
| 101 |
"\n",
|
| 102 |
"print(\"==================================================\")\n",
|
| 103 |
+
"print(\"\ud83d\udcca CELL 2: TARGET DATASET & VOCAB AUDIT\")\n",
|
| 104 |
"print(\"==================================================\")\n",
|
| 105 |
"\n",
|
| 106 |
+
"vocab_path = os.path.join(BASE_MODEL_DIR, \"vocab.txt\")\n",
|
|
|
|
|
|
|
| 107 |
"with open(vocab_path, \"r\", encoding=\"utf-8\") as f:\n",
|
| 108 |
" base_vocab_set = set(line.strip(\"\\r\\n\") for line in f)\n",
|
| 109 |
"\n",
|
| 110 |
+
"print(f\"\u2713 Loaded Vocab: {len(base_vocab_set):,} valid tokens.\")\n",
|
| 111 |
"\n",
|
| 112 |
"def normalize_burmese_text(text):\n",
|
| 113 |
" text = unicodedata.normalize(\"NFC\", text)\n",
|
|
|
|
| 115 |
" text = re.sub(r\"\\s+([\\u1000-\\u1021][\\u103a][\\u1037\\u1038]?)\", r\"\\1\", text)\n",
|
| 116 |
" text = re.sub(r\"\\s+([\\u102b-\\u103e])\", r\"\\1\", text)\n",
|
| 117 |
" text = re.sub(r\"(\\u1031)([\\u103b\\u103c\\u103d\\u103e]+)\", r\"\\2\\1\", text)\n",
|
| 118 |
+
" text = re.sub(r\"([\u104a\u104b])(?=[^\\s])\", r\"\\1 \", text)\n",
|
| 119 |
" return re.sub(r\"\\s+\", \" \", text).strip()\n",
|
| 120 |
"\n",
|
|
|
|
| 121 |
"test_texts = [\n",
|
| 122 |
+
" \"\u1014\u1019\u1031\u102c \u1010\u103f \u1018\u1002\u101d\u1010\u1031\u102c \u1021\u101b\u101f\u1010\u1031\u102c \u101e\u1019\u1039\u1019\u102c\u101e\u1019\u1039\u1017\u102f\u1012\u1039\u1013\u103f\u104b\",\n",
|
| 123 |
+
" \"\u101c\u1030\u101e\u102c\u1038\u1010\u103d\u1031 \u1021\u102c\u1038\u101c\u102f\u1036\u1038\u1000\u102d\u102f \u1021\u101a\u102f\u1010\u103a\u1021\u101c\u1010\u103a\u1021\u1019\u103c\u1010\u103a\u1019\u101b\u103d\u1031\u1038 \u1001\u103b\u1005\u103a\u1001\u1004\u103a\u101c\u1031\u1038\u1005\u102c\u1038\u1015\u102b\"\n",
|
| 124 |
"]\n",
|
| 125 |
"\n",
|
| 126 |
"all_chars = set()\n",
|
|
|
|
| 128 |
" all_chars.update(list(normalize_burmese_text(t)))\n",
|
| 129 |
"\n",
|
| 130 |
"oov = all_chars - base_vocab_set\n",
|
|
|
|
| 131 |
"if len(oov) == 0:\n",
|
| 132 |
+
" print(f\"\u2713 Audited {len(test_texts)} sample texts: 0 OOV characters found.\")\n",
|
| 133 |
+
" print(\"\u2713 Status: 100% COMPATIBLE with base model vocab.\")\n",
|
| 134 |
"else:\n",
|
| 135 |
+
" print(f\"\u274c Missing characters: {oov}\")\n",
|
|
|
|
| 136 |
"print(\"==================================================\")"
|
| 137 |
]
|
| 138 |
},
|
| 139 |
{
|
| 140 |
"cell_type": "code",
|
| 141 |
+
"execution_count": null,
|
|
|
|
| 142 |
"metadata": {},
|
| 143 |
+
"outputs": [],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
"source": [
|
| 145 |
+
"# CELL 3: VOCAB EXPANSION CHECK\n",
|
|
|
|
| 146 |
"print(\"==================================================\")\n",
|
| 147 |
+
"print(\"\ud83e\udde0 CELL 3: VOCAB EXPANSION CHECK\")\n",
|
| 148 |
"print(\"==================================================\")\n",
|
| 149 |
"\n",
|
| 150 |
+
"NEW_DISCOVERED_CHARS = []\n",
|
| 151 |
+
"new_tokens = [c for c in dict.fromkeys(NEW_DISCOVERED_CHARS) if c not in base_vocab_set]\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
"\n",
|
| 153 |
"if len(new_tokens) == 0:\n",
|
| 154 |
+
" print(f\"\u2713 Base Vocab Tokens: {len(base_vocab_set):,}\")\n",
|
| 155 |
+
" print(\"\u2713 ZERO OOV CHARACTERS DETECTED.\")\n",
|
| 156 |
+
" print(\"\u2713 Base vocab covers 100% of target text. No embedding surgery needed.\")\n",
|
| 157 |
"else:\n",
|
| 158 |
+
" print(f\"\u26a1 Found {len(new_tokens)} new tokens.\")\n",
|
|
|
|
| 159 |
"print(\"==================================================\")"
|
| 160 |
]
|
| 161 |
},
|
| 162 |
{
|
| 163 |
"cell_type": "code",
|
| 164 |
+
"execution_count": null,
|
|
|
|
| 165 |
"metadata": {},
|
| 166 |
+
"outputs": [],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
"source": [
|
| 168 |
+
"# CELL 4: DATASET BUILDER & ARROW PACKAGER\n",
|
| 169 |
"import os\n",
|
| 170 |
"import json\n",
|
|
|
|
| 171 |
"import shutil\n",
|
|
|
|
| 172 |
"import torch\n",
|
| 173 |
"import torchaudio\n",
|
| 174 |
"import pyarrow.ipc as ipc\n",
|
| 175 |
"from datasets import Dataset, Audio\n",
|
| 176 |
"\n",
|
| 177 |
"print(\"==================================================\")\n",
|
| 178 |
+
"print(\"\ud83d\udce6 CELL 4: DATASET BUILDER & ARROW PACKAGER\")\n",
|
| 179 |
"print(\"==================================================\")\n",
|
| 180 |
"\n",
|
|
|
|
| 181 |
"DATASET_BASE_NAME = \"burmese_custom_finetune\"\n",
|
| 182 |
"F5_DATASET_NAME = f\"{DATASET_BASE_NAME}_custom\"\n",
|
| 183 |
"\n",
|
|
|
|
| 185 |
"wavs_dir = os.path.join(ft_data_dir, \"wavs\")\n",
|
| 186 |
"os.makedirs(wavs_dir, exist_ok=True)\n",
|
| 187 |
"\n",
|
| 188 |
+
"# Download default reference sample if custom voice not provided\n",
|
| 189 |
+
"sample_audio_path = hf_hub_download(repo_id=REPO_ID, filename=\"ref_sample.wav\")\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
"\n",
|
| 191 |
+
"# Add your training audio files and Burmese transcripts here:\n",
|
| 192 |
"raw_training_data = [\n",
|
| 193 |
+
" {\"audio\": sample_audio_path, \"text\": \"\u1021\u1015\u103c\u1004\u103a \u1019\u101e\u103d\u102c\u1038\u101b\u101c\u102d\u102f\u1037 \u1005\u102d\u1010\u103a\u100a\u1005\u103a\u1014\u1031\u1015\u102b\u1010\u101a\u103a \u1019\u102d\u102f\u1038\u1010\u103d\u1031 \u1010\u1021\u102c\u1038 \u101b\u103d\u102c\u1014\u1031\u1015\u102b\u1010\u101a\u103a\"},\n",
|
| 194 |
+
" {\"audio\": sample_audio_path, \"text\": \"\u101c\u1030\u1010\u103d\u1031 \u1021\u102c\u1038\u101c\u102f\u1036\u1038\u1000\u102d\u102f \u1001\u103b\u1005\u103a\u1019\u103c\u1010\u103a\u1014\u102d\u102f\u1038\u1015\u102b \u1010\u1014\u103a\u1016\u102d\u102f\u1038\u1011\u102c\u1038\u1015\u102b \u101c\u1031\u1038\u1005\u102c\u1038\u1005\u103d\u102c \u1006\u1000\u103a\u1006\u1036\u1015\u102b\"}\n",
|
| 195 |
"]\n",
|
| 196 |
"\n",
|
| 197 |
"processed_samples = []\n",
|
|
|
|
| 198 |
"for idx, item in enumerate(raw_training_data):\n",
|
| 199 |
" src_audio = item[\"audio\"]\n",
|
| 200 |
" if not os.path.exists(src_audio):\n",
|
| 201 |
" continue\n",
|
|
|
|
| 202 |
" waveform, sr = torchaudio.load(src_audio)\n",
|
| 203 |
" if waveform.shape[0] > 1:\n",
|
| 204 |
" waveform = torch.mean(waveform, dim=0, keepdim=True)\n",
|
|
|
|
| 207 |
"\n",
|
| 208 |
" dst_wav = os.path.join(wavs_dir, f\"sample_{idx:05d}.wav\")\n",
|
| 209 |
" torchaudio.save(dst_wav, waveform, 24000)\n",
|
|
|
|
| 210 |
" duration_sec = waveform.shape[1] / 24000\n",
|
| 211 |
" cleaned_text = normalize_burmese_text(item[\"text\"])\n",
|
|
|
|
| 212 |
" processed_samples.append({\"audio\": dst_wav, \"text\": cleaned_text, \"duration\": duration_sec})\n",
|
| 213 |
"\n",
|
|
|
|
|
|
|
|
|
|
| 214 |
"duration_payload = {\"duration\": [round(s[\"duration\"], 4) for s in processed_samples]}\n",
|
| 215 |
"with open(os.path.join(ft_data_dir, \"duration.json\"), \"w\", encoding=\"utf-8\") as f:\n",
|
| 216 |
" json.dump(duration_payload, f, indent=2)\n",
|
|
|
|
| 217 |
"\n",
|
|
|
|
| 218 |
"hf_dataset = Dataset.from_dict({\n",
|
| 219 |
" \"audio\": [s[\"audio\"] for s in processed_samples],\n",
|
| 220 |
" \"text\": [s[\"text\"] for s in processed_samples],\n",
|
| 221 |
" \"duration\": [s[\"duration\"] for s in processed_samples]\n",
|
| 222 |
"}).cast_column(\"audio\", Audio(sampling_rate=24000))\n",
|
| 223 |
"\n",
|
|
|
|
| 224 |
"raw_dir = os.path.join(ft_data_dir, \"raw\")\n",
|
| 225 |
"if os.path.exists(raw_dir):\n",
|
| 226 |
" shutil.rmtree(raw_dir)\n",
|
| 227 |
"hf_dataset.save_to_disk(raw_dir)\n",
|
| 228 |
"\n",
|
| 229 |
+
"with open(os.path.join(ft_data_dir, \"raw.arrow\"), \"wb\") as f:\n",
|
|
|
|
|
|
|
| 230 |
" with ipc.new_stream(f, hf_dataset.data.table.schema) as writer:\n",
|
| 231 |
" writer.write_table(hf_dataset.data.table)\n",
|
| 232 |
"\n",
|
| 233 |
+
"shutil.copy(dst_vocab, os.path.join(ft_data_dir, \"vocab.txt\"))\n",
|
| 234 |
+
"print(f\"\u2713 Dataset packaged successfully at: {ft_data_dir}\")\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
"print(\"==================================================\")"
|
| 236 |
]
|
| 237 |
},
|
| 238 |
{
|
| 239 |
"cell_type": "code",
|
| 240 |
+
"execution_count": null,
|
|
|
|
| 241 |
"metadata": {},
|
| 242 |
+
"outputs": [],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
"source": [
|
| 244 |
+
"# CELL 5: STANDALONE FINE-TUNING LAUNCHER\n",
|
| 245 |
"import os\n",
|
| 246 |
"import sys\n",
|
| 247 |
"import subprocess\n",
|
| 248 |
"\n",
|
| 249 |
"print(\"==================================================\")\n",
|
| 250 |
+
"print(\"\ud83d\ude80 CELL 5: STANDALONE FINE-TUNING LAUNCHER\")\n",
|
| 251 |
"print(\"==================================================\")\n",
|
| 252 |
"\n",
|
| 253 |
+
"# Locate F5-TTS training script\n",
|
| 254 |
+
"candidates = [\n",
|
| 255 |
+
" \"/workspace/F5-TTS/src/f5_tts/train/finetune_cli.py\",\n",
|
| 256 |
+
" \"./F5-TTS/src/f5_tts/train/finetune_cli.py\"\n",
|
| 257 |
+
"]\n",
|
| 258 |
+
"train_script = next((c for c in candidates if os.path.exists(c)), None)\n",
|
| 259 |
+
"if not train_script:\n",
|
| 260 |
+
" raise FileNotFoundError(\"finetune_cli.py not found. Please run CELL 0 to clone F5-TTS.\")\n",
|
| 261 |
"\n",
|
| 262 |
+
"work_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(train_script)))))\n",
|
|
|
|
| 263 |
"\n",
|
| 264 |
+
"# Link custom data folder into F5-TTS/data\n",
|
| 265 |
+
"f5_data_target = os.path.join(work_dir, \"data\", F5_DATASET_NAME)\n",
|
| 266 |
+
"os.makedirs(os.path.dirname(f5_data_target), exist_ok=True)\n",
|
| 267 |
+
"if os.path.exists(f5_data_target):\n",
|
| 268 |
+
" if os.path.islink(f5_data_target): os.unlink(f5_data_target)\n",
|
| 269 |
+
" else: shutil.rmtree(f5_data_target)\n",
|
| 270 |
+
"os.symlink(ft_data_dir, f5_data_target)\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
"\n",
|
| 272 |
"train_cmd = [\n",
|
| 273 |
" \"accelerate\", \"launch\",\n",
|
| 274 |
" \"--num_processes\", \"1\",\n",
|
| 275 |
" \"--mixed_precision=bf16\",\n",
|
| 276 |
" train_script,\n",
|
| 277 |
+
" \"--exp_name\", \"F5TTS_v1_Base\",\n",
|
| 278 |
+
" \"--dataset_name\", DATASET_BASE_NAME,\n",
|
| 279 |
" \"--finetune\",\n",
|
| 280 |
+
" \"--pretrain\", dst_pt,\n",
|
| 281 |
" \"--tokenizer\", \"custom\",\n",
|
| 282 |
+
" \"--tokenizer_path\", dst_vocab,\n",
|
| 283 |
+
" \"--learning_rate\", \"5e-6\",\n",
|
| 284 |
+
" \"--epochs\", \"20\",\n",
|
| 285 |
+
" \"--batch_size_per_gpu\", \"9600\",\n",
|
| 286 |
+
" \"--batch_size_type\", \"frame\",\n",
|
| 287 |
+
" \"--max_samples\", \"96\",\n",
|
| 288 |
+
" \"--num_warmup_updates\", \"10\",\n",
|
| 289 |
+
" \"--save_per_updates\", \"250\",\n",
|
| 290 |
+
" \"--last_per_updates\", \"10\",\n",
|
| 291 |
+
" \"--keep_last_n_checkpoints\", \"3\",\n",
|
| 292 |
" \"--bnb_optimizer\"\n",
|
| 293 |
"]\n",
|
| 294 |
"\n",
|
| 295 |
+
"print(\"\u2713 Base Checkpoint :\", dst_pt)\n",
|
| 296 |
+
"print(\"\u2713 Target Dataset :\", DATASET_BASE_NAME)\n",
|
| 297 |
+
"print(\"\u2713 Launching training process...\\n\")\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
"\n",
|
|
|
|
| 299 |
"env = os.environ.copy()\n",
|
|
|
|
|
|
|
| 300 |
"env[\"PYTORCH_CUDA_ALLOC_CONF\"] = \"expandable_segments:True\"\n",
|
| 301 |
"\n",
|
| 302 |
+
"subprocess.run(train_cmd, env=env, check=True)\n",
|
| 303 |
+
"print(\"\\n\ud83c\udf89 FINE-TUNING FINISHED SUCCESSFULLY!\")"
|
| 304 |
+
]
|
| 305 |
+
},
|
| 306 |
+
{
|
| 307 |
+
"cell_type": "code",
|
| 308 |
+
"execution_count": null,
|
| 309 |
+
"metadata": {},
|
| 310 |
+
"outputs": [],
|
| 311 |
+
"source": [
|
| 312 |
+
"# CELL 6: TEST YOUR FINE-TUNED VOICE\n",
|
| 313 |
+
"from IPython.display import Audio, display\n",
|
| 314 |
+
"from f5_myanmar_tts import MyanmarTTS\n",
|
| 315 |
+
"\n",
|
| 316 |
+
"# Find your latest fine-tuned checkpoint\n",
|
| 317 |
+
"ft_ckpt_dir = os.path.join(work_dir, \"ckpts\", DATASET_BASE_NAME)\n",
|
| 318 |
+
"ft_pt = os.path.join(ft_ckpt_dir, \"model_last.pt\")\n",
|
| 319 |
+
"\n",
|
| 320 |
+
"if os.path.exists(ft_pt):\n",
|
| 321 |
+
" print(f\"Loading fine-tuned model: {ft_pt}\")\n",
|
| 322 |
+
" # Load directly for testing\n",
|
| 323 |
+
" tts = MyanmarTTS()\n",
|
| 324 |
+
" tts.model_path = ft_pt\n",
|
| 325 |
+
" out_wav = tts.speak(\"\u1019\u1004\u103a\u1039\u1002\u101c\u102c\u1015\u102b \u1001\u1004\u103a\u1017\u103b\u102c\u104a \u1021\u101e\u1036\u1005\u1019\u103a\u1038\u101e\u1015\u103a\u1001\u103b\u1000\u103a \u1021\u1031\u102c\u1004\u103a\u1019\u103c\u1004\u103a\u1015\u102b\u101e\u100a\u103a\", output_file=\"test_finetuned.wav\")\n",
|
| 326 |
+
" print(f\"\u2713 Generated: {out_wav}\")\n",
|
| 327 |
+
" display(Audio(out_wav, rate=24000, autoplay=True))\n",
|
| 328 |
+
"else:\n",
|
| 329 |
+
" print(f\"No fine-tuned checkpoint found yet at {ft_pt}\")"
|
| 330 |
]
|
| 331 |
}
|
| 332 |
],
|
| 333 |
"metadata": {
|
| 334 |
"kernelspec": {
|
| 335 |
+
"display_name": "Python 3",
|
| 336 |
"language": "python",
|
| 337 |
"name": "python3"
|
| 338 |
},
|
| 339 |
"language_info": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
"name": "python",
|
| 341 |
+
"version": "3.10"
|
|
|
|
|
|
|
| 342 |
}
|
| 343 |
},
|
| 344 |
"nbformat": 4,
|