RVCのモデルを日本語向けに事前学習する

こんにちは、[nadare](https://twitter.com/Py2K4)です。
機械学習エンジニアで、普段はレコメンド・検索関連のお仕事をしています。いろんな競技プログラミングが好きです。

最近はRetrieval-based-Voice-Conversion(以下RVC)という技術に関心を持ち、[本家Retrieval-based-Voice-Conversion-WebUI](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)や[ddPn08さん版RVC-WebUI](https://github.com/ddPn08/rvc-webui/tree/main)、[VC Client](https://github.com/w-okada/voice-changer)にPR投げつつ勉強しています。

本記事では、RVCのモデルで綺麗な日本語に変換するための学習テクニックを紹介します。

## 2023/05/24 追記
[続・RVCのモデルを日本語向けに事前学習する](https://qiita.com/nadare/items/18cd74e51c731904c3b0)を公開しました。最新の内容にアップデートしたので、こちらもご参照ください。

## 2023/05/14 16:20追記
これまではITAコーパス読み上げ音声を10~30epoch学習させたもので比較していて、その時点では事前学習済みモデルでないと滑らかな音声は発音できなかったのですが、
試しにJPHuBERTをphone embedderに指定して100epoch学習させてみたところ、デフォルトの重みからでも十分明瞭な音声が作れました。
学習データの量に依存するとは思いますが、引き続き検証を続けていきます。

# TL;DR

- [このデータセット](https://www.kaggle.com/datasets/nadare/rvc-webui-tuned-weights)から事前学習済みの重みをDLする
- [ddPn08さん版RVC-WebUI](https://github.com/ddPn08/rvc-webui/tree/main)で
  - `Target sampling rate`を`48k`にする
  - `f0 Model`を`Yes`にする
  - `Using phone embedder`を`hubert-japanese-base`にする
  - `Embedding channels`を`768`にする
- `Target sampling ratePre trained generator path`と`Pre trained discriminator path`にDLしたファイルのパスを指定して長めに学習する
  - 滑らかさ重視ならreazonspeech事前学習版(f0D48k768_jphubert_reazonspeech_tuned.pth + f0G48k768_jphubert_reazonspeech_tuned.pth)
  - 音声のきれいさ重視ならSHAREVOX:小春音アミチューニング事前学習版(f0D48k768_jphubert_tuned.pth + f0G48k768_jphubert_tuned.pth)

# なんで日本語が不安定なの？
## RVCの構成要素
RVCの日本語の発音が日本語の上手い英語話者っぽい感じであるのは、事前学習データに由来します。
RVCの構造は以下の図のような3パートに分かれています。
![RVC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237496/f4cacf3b-3e35-229f-340e-aaf38e3ef9fb.png)

- `phone embedder`は[HuBERT](https://arxiv.org/abs/2106.07447)というモデル構造を使った、[ContentVec](https://arxiv.org/abs/2204.09224)が使われています。[ContentVec](https://arxiv.org/abs/2204.09224)は[Librispeech](https://www.openslr.org/12)という約1000hの英語の音声データセットで学習された[HuBERT-base](https://github.com/facebookresearch/fairseq/blob/main/examples/hubert/README.md)を、同じく[Librispeech](https://www.openslr.org/12)を用いてさらに違う方式でトレーニングしなおしています。こちらはRVC中では学習を行わず、入力音声を中間のembedding(情報を持った密なベクトル)に変換します。
- `generator`はembeddingを音声の波形(wavファイル)に変換します。事前学習には[VCTK](https://datashare.ed.ac.uk/handle/10283/2950)という50hの英語のデータセットで事前学習されています。これは109人の英語のネイティブスピーカーが約400の調整された文を読み上げるデータセットで、事前学習時は話者のidをヒントとして与えてあげることでEmbeddingから話者以外の情報を読み取ろうと学習を行います。RVCでG経やgeneratorと呼ばれているのはここの部分です。
- `descriminator`は入力音声と出力音声のどちらが本物かの学習を行います。Generative Adversarial Network(敵対的生成ネットワーク)と呼ばれていて、これで音声の質を上げています。RVCでD経やdescriminatorと呼ばれていて、セットで事前学習されています。

この三つのパーツはそれぞれ英語のデータセットで学習されているため、日本語で追加学習を行うと英語訛りが出てしまいます。

## jp-hubert-baseの登場
これまではgeneratorとdescriminatorはまだしも、HuBERTを個人で学習するのは困難なためここがネックになっていました。しかし、2023.04.28に[rinna株式会社](https://rinna.co.jp/)が[日本語の音声に特化した事前学習モデルHuBERTを公開](https://rinna.co.jp/news/2023/04/20230428.html)しました。これは[ReazonSpeech](https://research.reazon.jp/projects/ReazonSpeech/index.html)という日本語の約19,000時間の商用利用可能な日本語音声コーパスで学習されていて、モデルも[Hugging Face](https://huggingface.co/rinna/japanese-hubert-base)に[Apache-2.0 License](https://www.apache.org/licenses/LICENSE-2.0)で公開されています。

これにより、日本語の特徴を多く含んだEmbeddingを得られるようになりました。ただgeneratorに入力するembeddingが大きく変化したため、generatorがその違いを学びきるのにはたくさんのデータと学習時間を必要するので、RVCでの実用まではもう一歩必要でした。
![jip-RVC.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237496/046ae529-b48e-524b-ed0c-ce21adab94bd.png)

# RVCを日本語で事前学習する2つのアプローチ
jp-hubert-baseのembeddingに対応させるため、generatorとdescriminatorを日本語の商用可能なデータセットで再トレーニングを行いました。jp-hubertで無理やり学習させた音声を聞いていると日本語の発音部分を学習しきれていないように感じたので、とにかくたくさんの種類の発音を学べるよう以下の2つのアプローチをとりました。

- [VOICEVOX](https://voicevox.hiroshiba.jp/)や[SHAREVOX](https://www.sharevox.app/)といった合成音声読み上げソフトのうち、合成音声を機械学習に利用可能なキャラクターに[ITAコーパス](https://github.com/mmorise/ita-corpus)や[JSUTコーパス](https://github.com/sarulab-speech/jsut-label)といった営利利用可能なコーパスを読み上げてもらい学習データに使うアプローチ
- [ReazonSpeech](https://research.reazon.jp/projects/ReazonSpeech/index.html)から変換したい音声に似た声を[PyAnnoteのembeddingモデル](https://huggingface.co/pyannote/embedding)で取得し、[Ultimate Vocal Remover](https://ultimatevocalremover.com/)で声を抽出してから学習させるアプローチ

## 合成音声読み上げソフトを用いるアプローチ
[ITAコーパス](https://github.com/mmorise/ita-corpus)の読み上げ音声(424文)の音声はそれなりに公開されていますが、generatorの学習には不十分でした。そこで[JSUTコーパスのbasic5000](https://github.com/sarulab-speech/jsut-label)と合わせた5424文を、[SHAREVOX](https://www.sharevox.app/)の[小春音アミさん](https://amitaro.net/utau/)の4つのstyle(ノーマルv2、喜びv2、怒りv2、悲しみv2)に読ませた21969文(約10GB)を作成しました。(作成スクリプト: https://www.kaggle.com/datasets/nadare/rvc-webui-tuned-weights?select=generate_random_voice.py)

こちらの方法は元の音声が綺麗なので音がクリアになりますが、合成音声の機械らしさが若干残ります。

## ReazonSpeechを用いるアプローチ
合成音声読み上げソフトの音声を事前学習に用いた場合、合成音声の機械らしさが若干残るのが課題でした。そこで[ReazonSpeech](https://research.reazon.jp/projects/ReazonSpeech/index.html)の音声から、[PyAnnoteのembeddingモデル](https://huggingface.co/pyannote/embedding)を用いて変換したい声に近い音声を集めて事前学習に用いました。(抽出用notebook: https://github.com/nadare881/voice-changer-vector-search/blob/main/notebook/ReazonSpeechSimilarVoiceRetrieval.ipynb)
[ReazonSpeech](https://research.reazon.jp/projects/ReazonSpeech/index.html)はワンセグ放送にアノテーション付けたデータセットなのでBGMや効果音が多く入っていたものの、[Ultimate Vocal Remover](https://ultimatevocalremover.com/)でボーカルのみを抽出し、学習を行いました。

こちらの方法はワンセグの音声かつ16000Hzの音声でチューニングした影響か若干音が曇るものの、滑らかな日本語を発音できるようになりました。

# 日本語チューニング版を上手く使うコツ
私が公開しているjp-hubert-base対応版の重みは暫定的なもので、単一話者で学習している影響か、元の話者の影響が残ってしまいます。これはnpyファイルとindexファイルを指定することで改善します。
この辺りは少し古めですが[Realtime Voice Changer Client for RVC チュートリアル](https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_ja_latest.md)として書いたので参考にしてください。

# まとめ
RVCを日本語向けに商用利用可能なライセンスのデータセットでチューニングした話を書きました。
今後も事前学習モデルの改善に加え、[RVC-WebUI](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)や[VC Client](https://github.com/w-okada/voice-changer)の改善もできる範囲で貢献したいなーと思うので応援のほどお願いします。

また、[RVC等ボイスチェンジャー系AIで使える音声を探すためのリポジトリ](https://github.com/nadare881/voice-changer-vector-search)も作成中です。こちらも応援していただければ幸いです。