自己教師あり音声特徴量に対する線形回帰のみで音声変換を行うLinearVCを動かす

初めに

LinearVC は、線形回帰のみで音声変換(Voice Conversion)を行う手法です。 Interspeech 2025 に採択された論文の実装になります。

github.com

arxiv.org

処理パイプラインは以下の3ステップで構成されています。

  1. WavLM Large(レイヤー6)で自己教師あり特徴量を抽出
  2. ソース話者とターゲット話者の特徴量から線形回帰で射影行列を学習
  3. 特徴量を変換し HiFiGAN で波形を生成

開発環境

項目 詳細
OS Windows 11
CPU AMD Ryzen 9 7950X
GPU NVIDIA GeForce RTX 4090
メモリ 128GB
Python 3.13.8
CUDA 12.6
uv 0.6.x

環境構築

fork したリポジトリを clone します。

github.com

git clone https://github.com/ayutaz/linearvc.git
cd linearvc

pyproject.toml は以下の内容です。 PyTorch を CUDA 12.6 対応版でインストールするため、explicit = true で専用のインデックスを指定しています。

[project]
name = "linearvc"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.13"
dependencies = [
    "celer>=0.7.4",
    "ipython>=9.10.0",
    "jupyter>=1.1.1",
    "numpy>=2.4.2",
    "torch>=2.10.0",
    "torchaudio>=2.10.0",
    "tqdm>=4.67.3",
]

[[tool.uv.index]]
name = "pytorch-cu126"
url = "https://download.pytorch.org/whl/cu126"
explicit = true

[tool.uv.sources]
torch = [{ index = "pytorch-cu126" }]
torchaudio = [{ index = "pytorch-cu126" }]

依存パッケージをインストールします。

uv sync

データの準備

音声変換には、ソース話者(変換元)とターゲット話者(変換先)の音声データが必要です。 今回はソース話者に LibriSpeech dev-clean の話者 1272(英語男性)、ターゲット話者につくよみちゃんコーパス Vol.1(日本語女性)を使用します。

LibriSpeech dev-clean のダウンロード

mkdir -p data
cd data
wget https://www.openslr.org/resources/12/dev-clean.tar.gz
tar xzf dev-clean.tar.gz
cd ..

展開すると以下のようなディレクトリ構成になります。

data/LibriSpeech/dev-clean/
├── 1272/          # 話者ID
│   ├── 128104/    # チャプターID
│   │   ├── 1272-128104-0000.flac
│   │   ├── 1272-128104-0001.flac
│   │   └── ...
│   └── 135031/
│       └── ...
└── ...

サブセットの作成

linearvc.py--extension オプションはソースとターゲット両方に適用されるため、拡張子を .wav に統一します。 LibriSpeech の .flac.wav に変換し、それぞれ数ファイルずつのサブセットを作成します。

import torchaudio
from pathlib import Path

src_dir = Path("data/LibriSpeech/dev-clean/1272")
wav_dir = Path("data/subset_libri1272")
wav_dir.mkdir(exist_ok=True)

for flac_file in sorted(src_dir.rglob("*.flac"))[:3]:
    wav, sr = torchaudio.load(flac_file)
    out_path = wav_dir / flac_file.name.replace(".flac", ".wav")
    torchaudio.save(str(out_path), wav, sr)

つくよみちゃんコーパスも同様に数ファイルコピーします。

mkdir -p data/subset_tsukuyomi
cp path/to/つくよみちゃんコーパス/02\ WAV(+12dB増幅)/VOICEACTRESS100_00{1,2,3}.wav data/subset_tsukuyomi/

実行

linearvc.py を実行して音声変換を行います。 ソース話者のディレクトリ、ターゲット話者のディレクトリ、変換したい入力音声ファイル、出力ファイル名を指定します。

文では非並列モードで話者あたり約3分以上のデータを推奨しています。そのため3-5分程度を最低限指定するようにしてください

パターン1: LibriSpeech 1272 → つくよみちゃん(英語男性 → 日本語女性)

uv run python linearvc.py \
    data/subset_libri1272 \
    data/subset_tsukuyomi \
    data/subset_libri1272/1272-128104-0000.wav \
    output.wav

実行すると以下のようなログが出力されます。

Reading from: data\subset_libri1272
Reading from: data\subset_tsukuyomi
Reading: data\subset_libri1272\1272-128104-0000.wav
Source features:
100%|██████████| 3/3 [00:00<00:00, 14.13it/s]
Target features:
100%|██████████| 3/3 [00:00<00:00, 21.18it/s]
Writing: output.wav

パターン2: つくよみちゃん → LibriSpeech 1272(日本語女性 → 英語男性)

uv run python linearvc.py \
    data/subset_tsukuyomi \
    data/subset_libri1272 \
    data/subset_tsukuyomi/VOICEACTRESS100_001.wav \
    output_tsukuyomi2libri.wav
Reading from: data\subset_tsukuyomi
Reading from: data\subset_libri1272
Reading: data\subset_tsukuyomi\VOICEACTRESS100_001.wav
Source features:
100%|██████████| 3/3 [00:00<00:00, 20.83it/s]
Target features:
100%|██████████| 3/3 [00:00<00:00, 36.68it/s]
Writing: output_tsukuyomi2libri.wav

パターン3: LibriSpeech 1272 → LibriSpeech 1462(英語男性 → 英語女性)

uv run python linearvc.py \
    data/subset_libri1272 \
    data/subset_libri1462 \
    data/subset_libri1272/1272-128104-0000.wav \
    output_libri2libri.wav
Reading from: data\subset_libri1272
Reading from: data\subset_libri1462
Reading: data\subset_libri1272\1272-128104-0000.wav
Source features:
100%|██████████| 3/3 [00:00<00:00, 20.81it/s]
Target features:
100%|██████████| 3/3 [00:00<00:00, 20.43it/s]
Writing: output_libri2libri.wav

各パターンとも output.wav が生成されていれば成功です。

精度はそれなりのものができてそうでした

参考