DOKはコンテナー型のGPUサービスで、NVIDIA V100やH100を実行時間課金で利用できるサービスです。
コンテナー型GPUクラウドサービス 高火力 DOK(ドック) | さくらインターネット
今回はこのDOKを使って、OpenVoiceにて自分の声を使った音声クローンを行ってみます。
結果
結果の音声です。日本語はもちろん、英語(インド風、オーストラリア風など)も生成できます。
- 日本語
https://s3.isk01.sakurastorage.jp/mg-sd-demo/openvoice/002_jp.wav - 英語(US)
https://s3.isk01.sakurastorage.jp/mg-sd-demo/openvoice/6415b7f3-4b95-41bd-b0f1-8597418bcdbd_en-us.wav - 英語(インド)
https://s3.isk01.sakurastorage.jp/mg-sd-demo/openvoice/6415b7f3-4b95-41bd-b0f1-8597418bcdbd_en-india.wav - 英語(オーストラリア)
https://s3.isk01.sakurastorage.jp/mg-sd-demo/openvoice/6415b7f3-4b95-41bd-b0f1-8597418bcdbd_en-au.wav - 英語(ブラジル)
https://s3.isk01.sakurastorage.jp/mg-sd-demo/openvoice/6415b7f3-4b95-41bd-b0f1-8597418bcdbd_en-br.wav - 英語(デフォルト)
https://s3.isk01.sakurastorage.jp/mg-sd-demo/openvoice/6415b7f3-4b95-41bd-b0f1-8597418bcdbd_en-default.wav
参考
myshell-ai/OpenVoiceにあるUSAGE.mdを参考に、v2で行っています。
とりあえず試す
学習済みのデータを使って実行してみたい方は、DOKにて新しいタスクを作成し、以下の情報を入力してください。
項目 | 設定 |
---|---|
イメージ | dok-handson.sakuracr.jp/openvoice |
環境変数 | TEXT = "Now, here is today's news. It was a nice day today." LANG = EN |
TEXTは読み上げる文章です。LANGは言語を指定します。ENは英語、JAは日本語です。この場合はリファレンスの音声は指定していないので、デフォルトのexample_reference.mp3が利用されます。
自分の声など、特定の音声を指定してクローンを作りたい場合には、参考にする音声のMP3ファイルをどこかにアップロードし、そのURLを
REFERENCE
という環境変数で指定してください。
その音声は、長い方が良いようです。3分程度、適当な文章(なるべくさまざまな単語が含まれるもの)を読み、MP3ファイルにしてください
なお、プランはv100-32gbで大丈夫そうです。試してみた限りですが、v100-32gbで2分、h100-80gbで1分10秒でした(リファレンスあり)。なお、Dockerコンテナのダウンロードや初期化には時間がかかります(10分弱)。
コンテナイメージの作成と登録
上記タスクで利用したDockerイメージを作成する手順は以下の通りです。完成版はgoofmint/dok-openvoiceにありますので、実装時の参考にしてください。
ベースになるリポジトリ
ベースはmyshell-ai/OpenVoice: Instant voice cloning by MIT and MyShell. Audio foundation model.を利用します。
git clone https://github.com/myshell-ai/OpenVoice.git
cd OpenVoice
この中に必要なファイルを作成していきます。
checkpoint のダウンロードと解凍
checkpointをダウンロードし、 checkpoints_v2
ディレクトリに解凍します。
wget https://myshell-public-repo-host.s3.amazonaws.com/openvoice/checkpoints_v2_0417.zip
unzip checkpoints_v2_0417.zip
Dockerfile の作成
ここからはDockerfileの中で行っていきます。
ベースイメージ
ベースは FROM continuumio/miniconda3
です。
FROM continuumio/miniconda3
conda の設定
condaの設定を行います。
RUN conda create -n openvoice python==3.9
SHELL ["conda", "run", "-n", "openvoice", "/bin/bash", "-c"]
必要なライブラリのインストール
OpenVoiceはffmpegを使っているので、それをインストールします。 /opt/artifact
はDOKのアウトプット先のディレクトリです。
RUN apt-get update && \
apt-get install -y ffmpeg && \
mkdir /app /opt/artifact
必要なPythonライブラリのインストール
OpenVoiceのリポジトリのファイルをすべてコピーします。そして、OpenVoice用にMeloTTSをインストールします。argparseやboto3はCLI実行用にインストールします。
WORKDIR /app
COPY . .
RUN pip install -e . && \
pip install argparse && \
pip install boto3 && \
pip install git+https://github.com/myshell-ai/MeloTTS.git && \
python -m unidic download
docker-entrypoint.sh の準備
Docker実行の際に呼び出す docker-entrypoint.sh
(内容は後述)を準備します。
RUN chmod +x /app/docker-entrypoint.sh
# Dockerコンテナー起動時に実行するスクリプトを指定して実行
CMD ["/bin/bash", "/app/docker-entrypoint.sh"]
Dockerfile全体
Dockerfileの全体は以下の通りです。
FROM continuumio/miniconda3
RUN conda create -n openvoice python==3.9
SHELL ["conda", "run", "-n", "openvoice", "/bin/bash", "-c"]
RUN apt-get update && \
apt-get install -y ffmpeg && \
mkdir /app /opt/artifact
WORKDIR /app
COPY . .
RUN pip install -e . && \
pip install argparse && \
pip install boto3 && \
pip install git+https://github.com/myshell-ai/MeloTTS.git && \
python -m unidic download
RUN chmod +x /app/docker-entrypoint.sh
# Dockerコンテナー起動時に実行するスクリプトを指定して実行
CMD ["/bin/bash", "/app/docker-entrypoint.sh"]
docker-entrypoint.sh の作成
docker-entrypoint.sh
はDockerコンテナー起動時に実行するスクリプトです。ここでは環境変数をチェックして、 runner.py
を呼び出します。以下の内容で作成します。
#!/bin/bash
set -ue
shopt -s nullglob
export TZ=${TZ:-Asia/Tokyo}
# アウトプット先ディレクトリ(自動付与) /opt/artifact固定です
if [ -z "${SAKURA_ARTIFACT_DIR:-}" ]; then
echo "Environment variable SAKURA_ARTIFACT_DIR is not set" >&2
exit 1
fi
# DOKのタスクID(自動付与)
if [ -z "${SAKURA_TASK_ID:-}" ]; then
echo "Environment variable SAKURA_TASK_ID is not set" >&2
exit 1
fi
# 読み上げるテキスト(環境変数で指定)
if [ -z "${TEXT:-}" ]; then
echo "Environment variable PROMPT is not set" >&2
exit 1
fi
# 言語
if [ -z "${LANG:-}" ]; then
echo "Environment variable LANG is not set" >&2
exit 1
fi
# リファレンスの音声(オプション)
if [ -z "${REFERENCE:-}" ]; then
REFERENCE="resources/example_reference.mp3"
else
wget $REFERENCE -O /tmp/reference.mp3
REFERENCE="/tmp/reference.mp3"
fi
# S3_はすべてboto3用の環境変数です
cd /app
conda run -n openvoice python3 runner.py \
--id="${SAKURA_TASK_ID}" \
--output="${SAKURA_ARTIFACT_DIR}" \
--text="${TEXT}" \
--lang="${LANG}" \
--reference="${REFERENCE}" \
--s3-bucket="${S3_BUCKET:-}" \
--s3-endpoint="${S3_ENDPOINT:-}" \
--s3-secret="${S3_SECRET:-}" \
--s3-token="${S3_TOKEN:-}"
runner.py の作成
runner.py は実際に処理を行うスクリプトです。まず必要なライブラリをインポートします。
import os
import torch
from openvoice import se_extractor
from openvoice.api import ToneColorConverter
from melo.api import TTS
import argparse
import nltk
import boto3
nltk.download('averaged_perceptron_tagger_eng')
パラメータの取得
docker-entrypoint.sh
から渡されたパラメータを取得します。
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument(
'--output',
default='/opt/artifact',
help='出力先ディレクトリを指定します。',
)
arg_parser.add_argument(
'--text',
default='',
help='読み上げる文章',
)
arg_parser.add_argument(
'--id',
default='',
help='タスクIDを指定します。',
)
arg_parser.add_argument(
'--lang',
default='JP',
help='読み上げる言語を指定',
)
arg_parser.add_argument(
'--reference',
default='resources/example_reference.mp3',
help='リファレンスの音声',
)
arg_parser.add_argument('--s3-bucket', help='S3のバケットを指定します。')
arg_parser.add_argument('--s3-endpoint', help='S3互換エンドポイントのURLを指定します。')
arg_parser.add_argument('--s3-secret', help='S3のシークレットアクセスキーを指定します。')
arg_parser.add_argument('--s3-token', help='S3のアクセスキーIDを指定します。')
args = arg_parser.parse_args()
オブジェクトストレージ用のオブジェクトを準備
S3_ではじまる環境変数があれば、それを使ってS3オブジェクトを作成します。
s3 = None
if args.s3_token and args.s3_secret and args.s3_bucket:
# S3クライアントの作成
s3 = boto3.client(
's3',
endpoint_url=args.s3_endpoint if args.s3_endpoint else None,
aws_access_key_id=args.s3_token,
aws_secret_access_key=args.s3_secret)
OpenVoiceの設定
OpenVoice/demo_part3.ipynb at main · myshell-ai/OpenVoiceの内容に沿って、OpenVoiceを準備します。
ckpt_converter = 'checkpoints_v2/converter'
device = "cuda:0" if torch.cuda.is_available() else "cpu"
output_dir = args.output
tone_color_converter = ToneColorConverter(f'{ckpt_converter}/config.json', device=device)
tone_color_converter.load_ckpt(f'{ckpt_converter}/checkpoint.pth')
os.makedirs(output_dir, exist_ok=True)
reference_speaker = args.reference
target_se, audio_name = se_extractor.get_se(reference_speaker, tone_color_converter, vad=True)
from melo.api import TTS
src_path = f'{output_dir}/tmp.wav'
speed = 1.0
model = TTS(language=args.lang, device=device)
speaker_ids = model.hps.data.spk2id
音声の生成とアップロード
音声を生成し、アップロードします。たとえば EN
を指定した場合、インド風英語やオーストラリア風英語など複数のキーが speaker_ids
に入ってきます。
for speaker_key in speaker_ids.keys():
speaker_id = speaker_ids[speaker_key]
speaker_key = speaker_key.lower().replace('_', '-')
source_se = torch.load(f'checkpoints_v2/base_speakers/ses/{speaker_key}.pth', map_location=device)
model.tts_to_file(args.text, speaker_id, src_path, speed=speed)
save_path = f'{args.output}/{args.id}_{speaker_key}.wav'
# Run the tone color converter
encode_message = "@MyShell"
tone_color_converter.convert(
audio_src_path=src_path,
src_se=source_se,
tgt_se=target_se,
output_path=save_path,
message=encode_message)
# オブジェクトストレージにアップロード
if s3 is not None:
s3.upload_file(
Filename=save_path,
Bucket=args.s3_bucket,
Key=os.path.basename(save_path))
全体の処理
runner.py
の全体の処理は以下の通りです。
import os
import torch
from openvoice import se_extractor
from openvoice.api import ToneColorConverter
from melo.api import TTS
import argparse
import nltk
import boto3
nltk.download('averaged_perceptron_tagger_eng')
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument(
'--output',
default='/opt/artifact',
help='出力先ディレクトリを指定します。',
)
arg_parser.add_argument(
'--text',
default='',
help='読み上げる文章',
)
arg_parser.add_argument(
'--id',
default='',
help='タスクIDを指定します。',
)
arg_parser.add_argument(
'--lang',
default='JP',
help='読み上げる言語を指定',
)
arg_parser.add_argument(
'--reference',
default='resources/example_reference.mp3',
help='リファレンスの音声',
)
arg_parser.add_argument('--s3-bucket', help='S3のバケットを指定します。')
arg_parser.add_argument('--s3-endpoint', help='S3互換エンドポイントのURLを指定します。')
arg_parser.add_argument('--s3-secret', help='S3のシークレットアクセスキーを指定します。')
arg_parser.add_argument('--s3-token', help='S3のアクセスキーIDを指定します。')
args = arg_parser.parse_args()
s3 = None
if args.s3_token and args.s3_secret and args.s3_bucket:
# S3クライアントの作成
s3 = boto3.client(
's3',
endpoint_url=args.s3_endpoint if args.s3_endpoint else None,
aws_access_key_id=args.s3_token,
aws_secret_access_key=args.s3_secret)
ckpt_converter = 'checkpoints_v2/converter'
device = "cuda:0" if torch.cuda.is_available() else "cpu"
output_dir = args.output
tone_color_converter = ToneColorConverter(f'{ckpt_converter}/config.json', device=device)
tone_color_converter.load_ckpt(f'{ckpt_converter}/checkpoint.pth')
os.makedirs(output_dir, exist_ok=True)
reference_speaker = args.reference
target_se, audio_name = se_extractor.get_se(reference_speaker, tone_color_converter, vad=True)
src_path = f'{output_dir}/tmp.wav'
speed = 1.0
model = TTS(language=args.lang, device=device)
speaker_ids = model.hps.data.spk2id
for speaker_key in speaker_ids.keys():
speaker_id = speaker_ids[speaker_key]
speaker_key = speaker_key.lower().replace('_', '-')
source_se = torch.load(f'checkpoints_v2/base_speakers/ses/{speaker_key}.pth', map_location=device)
model.tts_to_file(args.text, speaker_id, src_path, speed=speed)
save_path = f'{args.output}/{args.id}_{speaker_key}.wav'
# Run the tone color converter
encode_message = "@MyShell"
tone_color_converter.convert(
audio_src_path=src_path,
src_se=source_se,
tgt_se=target_se,
output_path=save_path,
message=encode_message)
if s3 is not None:
s3.upload_file(
Filename=save_path,
Bucket=args.s3_bucket,
Key=os.path.basename(save_path))
Dockerイメージのビルド
上記の内容で、Dockerイメージをビルドします。Linux環境などで行います。
コンテナレジストリの用意
Dockerイメージを登録するコンテナレジストリを作成します。さくらのクラウドではLAB機能で、コンテナレジストリを提供しています。さくらのクラウドにログインしたら さくらのクラウド
を選択します。
左側のメニューの LAB
の中にある コンテナレジストリ
を選択します。
追加
を押して、コンテナレジストリを作成します。最低限、以下の入力が必要です。
項目 | 設定 |
---|---|
名前 | 分かりやすい、任意の名前を入力してください |
コンテナレジストリ名 | ドメイン名に使われます。以下では、 EXAMPLE.sakuracr.jp として説明します |
公開設定 | Pullのみとします |
ユーザーの作成
コンテナレジストリを作成したら、作成したコンテナレジストリを一覧でダブルクリックします。
詳細表示にて、ユーザータブをクリックします。
追加ボタンを押し、ユーザーを作成します。 YOUR_USER_NAME
と PASSWORD
は任意のものを指定してください。
項目 | 設定 |
---|---|
ユーザー名 | YOUR_USER_NAME |
パスワード | YOUR_PASSWORD |
ユーザ権限設定 | All |
Dockerイメージのビルド
DockerイメージはLinuxで行います。今回はUbuntu 24.04を使っています。Dockerが使える環境であれば、Windows + WSL2でも問題ありません。macOSの場合、アーキテクチャが異なるので動かせないかも知れません(未検証です)。
EXAMPLE.sakuracr.jp
の部分は、作成したコンテナレジストリのドメイン名に置き換えてください。また、 openvoice
は任意の名前で大丈夫です(以下はその名称で読み替えてください)。
sudo docker build -t EXAMPLE.sakuracr.jp/openvoice:latest .
コンテナレジストリへのログイン
作成したコンテナレジストリにログインします。ログインIDとパスワードが求められるので、作成したものを入力してください。
sudo docker login EXAMPLE.sakuracr.jp
イメージのプッシュ
作成したイメージをコンテナレジストリにプッシュします。イメージサイズが大きいので、数十分かかります。
sudo docker push EXAMPLE.sakuracr.jp/openvoice:latest
タスクを作成する
後は最初と同じようにDOKでタスクを作成、実行します。
項目 | 設定 |
---|---|
イメージ | dok-handson.sakuracr.jp/openvoice |
環境変数 | TEXT = "Now, here is today's news. It was a nice day today." LANG = EN |
音声クローンを行う際には、リファレンスの音声が必要です。ボイスレコーダーのようなアプリで自分の声を録音(2〜3分程度)し、それを任意の場所にアップロードします。たとえばさくらのオブジェクトストレージを使った場合、以下のようなURLになります(パブリックリードを指定します)。
https://s3.isk01.sakurastorage.jp/YOUR_BACKET_NAME/YOUR_REFERENCE.mp3
まとめ
今回はOpenVoiceを使って、DOK上で音声クローンを行ってみました。まずは実行できるのみ、次にDockerイメージの作成と段階的に進められるようにしています。ぜひ、試してみてください。
DOKはタスクを多数立ち上げて、後は結果を待つのみと言った使い方ができます。ぜひAI・機械学習に活用してください。