初めに
前回はUtterTuneをCLIおよびWebUIを作成して動かすところまで行いました。
今回は、UtterTuneのLoRAを独自の音声データを用いてLoRA学習を行っていきます
開発環境
- Windows 11
- uv 0.9.x
環境構築
環境構築は前回と同じです
git clone https://github.com/your-username/UtterTune.git cd UtterTune git submodule update --init --recursive git submodule update --init --recursive
モデルをダウンロードします
mkdir -p pretrained_models # Download CosyVoice2-0.5B git clone https://www.modelscope.cn/iic/CosyVoice2-0.5B.git pretrained_models/CosyVoice2-0.5B # Download LoRA weights git lfs install git clone https://huggingface.co/shuheikatoinfo/UtterTune-CosyVoice2-ja-JSUTJVS lora_weights/UtterTune-CosyVoice2-ja-JSUTJVS
環境構築と設定を行います
uv venv -p 3.10 .venv/bin/activate uv pip install -r submodules/CosyVoice/requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host=mirrors.aliyun.com # サブmoduleのパスを追加します python - <<'PY' import site, os sp = next(p for p in site.getsitepackages() if p.endswith("site-packages")) pth = os.path.join(sp, "cosyvoice_submodule.pth") with open(pth, "w", encoding="utf-8") as f: f.write(os.path.abspath("submodules/CosyVoice") + "\n") f.write(os.path.abspath("submodules/CosyVoice/third_party/Matcha-TTS") + "\n") print("Wrote:", pth) PY
データセットの構築
以下のように学習したい音声を以下の構造で配置します
UtterTune/
└── data/
└── corpora/
└── your_corpus/ # コーパス名(例: sad, my_voice等)
├── wav/ # 音声ファイル
│ ├── subdirA/ # サブディレクトリ(オプション)
│ │ ├── file001.wav
│ │ └── file002.wav
│ └── subdirB/
└── trans.txt # トランスクリプトファイル
文字お越しは以下のような構造で作成をします。<PHON_END>をつけるかどうかは自由で問題ないです。ただし<PHON_END>を使って推論したい場合は、少しは入れておくのがいいです
EMOTION100_001:<PHON_START>エッウソデショ。<PHON_END> EMOTION100_002:シュヴァイツァーは見習うべき人間です。 EMOTION100_003:デーヴィスさんはとても疲れているように見える。 EMOTION100_004:<PHON_START>スティーヴワジェーンカラテガミヲモラッタ。<PHON_END> E
記載するときには以下に気を付けます
FILE_IDは拡張子なしのファイル名と一致させる- 発音制御タグ(
<PHON_START>/<PHON_END>)は学習データの約20-30%に使用推奨 - 日本語の発音表記はカタカナで記述
- アクセント核:
'(例:レモ'ンティー) - 形態素境界:
/(例:チ'ミ/モーリョー)
前処理を実行
サブディレクトリが一つの場合は以下を実行します
python -m scripts.cv2.extract_speech_tokens \ --wav_root data/corpora/your_corpus/wav \ --out_dir data/speech_tokens/your_corpus \ --onnx_path pretrained_models/CosyVoice2-0.5B/speech_tokenizer_v2.onnx
サブディレクトリが複数ある場合は、以下を実行します
python -m scripts.cv2.extract_speech_tokens \ --wav_root data/corpora/your_corpus/wav/emotion \ --out_dir data/speech_tokens/your_corpus/emotion \ --onnx_path pretrained_models/CosyVoice2-0.5B/speech_tokenizer_v2.onnx python -m scripts.cv2.extract_speech_tokens \ --wav_root data/corpora/your_corpus/wav/recitation \ --out_dir data/speech_tokens/your_corpus/recitation \ --onnx_path pretrained_models/CosyVoice2-0.5B/speech_tokenizer_v2.onnx
実行することで、以下のような各WAVファイルに対応する.npyファイルが生成されます:
data/speech_tokens/your_corpus/
├── emotion/
│ ├── file001.npy
│ └── file002.npy
└── recitation/
├── file003.npy
└── file004.npy
マニフェストファイル生成
学習用のマニフェストファイル(4列TSV)を作成します。
マニフェスト形式
spk_id <TAB> text <TAB> token.npy <TAB> wav_path
生成スクリプトの例
scripts/cv2/prepare_manifest_custom.py
from pathlib import Path def prepare_manifest(corpus_root: Path, token_root: Path, output_file: Path): """ マニフェストファイルを生成 Args: corpus_root: data/corpora/your_corpus token_root: data/speech_tokens output_file: data/manifests/your_corpus.tsv """ trans_file = corpus_root / "trans.txt" trans_dict = {} # トランスクリプトを読み込み for line in trans_file.read_text("utf-8").splitlines(): file_id, text = line.split(":", 1) trans_dict[file_id] = text rows = [] spk_id = 0 # 単一話者の場合は0、複数話者の場合は話者ごとに異なるIDを割り当て # 音声トークンファイルをスキャン for token_file in sorted((token_root / "your_corpus").rglob("*.npy")): file_id = token_file.stem # 対応するWAVファイルを探す wav_path = None for wav_file in (corpus_root / "wav").rglob(f"{file_id}.wav"): wav_path = wav_file break if wav_path is None or file_id not in trans_dict: print(f"Warning: Missing data for {file_id}") continue text = trans_dict[file_id] rows.append([spk_id, text, str(token_file), str(wav_path)]) # TSVとして保存 output_file.parent.mkdir(parents=True, exist_ok=True) with open(output_file, "w", encoding="utf-8") as f: for row in rows: f.write("\t".join(map(str, row)) + "\n") print(f"Created manifest: {output_file} ({len(rows)} entries)") if __name__ == "__main__": corpus_root = Path("data/corpora/your_corpus") token_root = Path("data/speech_tokens") output_file = Path("data/manifests/your_corpus.tsv") prepare_manifest(corpus_root, token_root, output_file)
マニフェストの作成処理の実行
python scripts/cv2/prepare_manifest_custom.py
出力例
data/manifests/your_corpus.tsv
0 <PHON_START>エッウソデショ。<PHON_END> data/speech_tokens/your_corpus/emotion/EMOTION100_001.npy data/corpora/your_corpus/wav/emotion/EMOTION100_001.wav 0 シュヴァイツァーは見習うべき人間です。 data/speech_tokens/your_corpus/emotion/EMOTION100_002.npy data/corpora/your_corpus/wav/emotion/EMOTION100_002.wav
学習実行
データセットが揃ったので、学習を行っていきます
まずは学習のパラメータ設定を行います
configs/train/your_corpus.yaml で以下の設定ファイルを作成します
--- manifest: data/manifests/your_corpus.tsv base_model: pretrained_models/CosyVoice2-0.5B val_ratio: 0.05 lora: rank: 16 # LoRAのランク(4, 8, 16, 32等) alpha: 64 # LoRAのアルファ(通常はrank * 4) dropout: 0.05 # ドロップアウト率 bias: none # バイアスの学習(none, all, lora_only) target_modules: # LoRA適用対象レイヤー - q_proj - k_proj - v_proj - o_proj training: output_dir: experiments/cv2/your_corpus per_device_train_batch_size: 8 # GPUメモリに応じて調整(4, 8, 16) per_device_eval_batch_size: 8 seed: 42 eval_strategy: steps eval_steps: 1000 # 評価頻度 save_steps: 1000 # チェックポイント保存頻度 max_steps: 15000 # 総学習ステップ数 learning_rate: 1e-4 # 学習率 adam_beta1: 0.9 adam_beta2: 0.98 warmup_ratio: 0.05 # ウォームアップ比率 lr_scheduler_type: cosine # 学習率スケジューラ max_grad_norm: 1.0 # 勾配クリッピング fp16: true # 半精度学習(GPU推奨) logging_steps: 50 # ログ出力頻度 logging_dir: "${.output_dir}/tb" # TensorBoardログディレクトリ report_to: tensorboard
以下で学習を開始します
python -m scripts.cv2.train --config configs/train/your_corpus.yaml
特定のチェックポイントから再開する場合は以下です
python -m scripts.cv2.train \ --config configs/train/your_corpus.yaml \ --resume_from_checkpoint experiments/cv2/your_corpus/checkpoint-5000
推論実行
学習したLoRAモデルで音声合成をテストします。
基本的な推論
python -m scripts.cv2.infer \
--base_model pretrained_models/CosyVoice2-0.5B \
--lora_dir experiments/cv2/your_corpus \
--texts "テストテキスト" \
--prompt_wav data/corpora/your_corpus/wav/sample.wav \
--prompt_text "サンプル音声の書き起こし" \
--out_dir wavs_out/test
複数テキストの一括合成
python -m scripts.cv2.infer \
--base_model pretrained_models/CosyVoice2-0.5B \
--lora_dir experiments/cv2/your_corpus \
--texts "標準テキスト|<PHON_START>発音制御<PHON_END>テキスト|別のテキスト" \
--prompt_wav data/corpora/your_corpus/wav/sample.wav \
--prompt_text "サンプル音声の書き起こし" \
--out_dir wavs_out/test
パイプ(|)で区切って複数のテキストを指定可能
テキストファイルからの読み込み
# texts.txt 標準のテキストです。 <PHON_START>発音制御<PHON_END>を含むテキスト。 別のテキストです。
python -m scripts.cv2.infer \
--base_model pretrained_models/CosyVoice2-0.5B \
--lora_dir experiments/cv2/your_corpus \
--texts texts.txt \
--prompt_wav data/corpora/your_corpus/wav/sample.wav \
--prompt_text "サンプル音声の書き起こし" \
--out_dir wavs_out/test
出力音声のトリミング
python -m scripts.cv2.infer \
--base_model pretrained_models/CosyVoice2-0.5B \
--lora_dir experiments/cv2/your_corpus \
--texts "テキスト" \
--prompt_wav data/corpora/your_corpus/wav/sample.wav \
--prompt_text "書き起こし" \
--out_dir wavs_out/test \
--trim_out # 前後の無音をトリミング
学習したLoRAでWebUIを起動
python webui.py \
--base_model pretrained_models/CosyVoice2-0.5B \
--lora_dir experiments/cv2/your_corpus \
--port 7860
WebUIを実行すると前回同様以下が起動します
