「CosyVoice2」を試す
GitHubレポジトリ
👉🏻 CosyVoice 👈🏻
CosyVoice 2.0:デモ・論文・Modelscope・HuggingFace
CosyVoice 1.0:デモ・論文・Modelscope
ハイライト🔥
CosyVoice 2.0がリリースされました!バージョン1.0と比べて、音声生成の精度、安定性、速度、品質が大幅に向上しています。
多言語対応
- 対応言語:中国語、英語、日本語、韓国語、中国語方言(広東語、四川語、上海語、天津語、武漢語など)
- 言語横断およびミックス言語対応:ゼロショット音声クローンによるクロスリンガル・コードスイッチングシナリオをサポート。
超低遅延
- 双方向ストリーミング対応:CosyVoice 2.0 はオフラインおよびストリーミングモデリング技術を統合。
- 初期パケット高速合成:高音質を保ちながら150msという低遅延を実現。
高精度
- 発音精度の向上:CosyVoice 1.0 と比べて発音エラーを30~50%削減。
- ベンチマーク達成:Seed-TTS評価セットの難易度の高いテストセットにて最小の文字誤り率を達成。
高い安定性
- 音色の一貫性:ゼロショットおよび多言語音声合成において信頼性のある音色一貫性を保証。
- 多言語合成:バージョン1.0と比較して大幅に改善。
自然な体験
- プロソディおよび音質の強化:合成音声の整合性が改善され、MOS評価スコアが5.4から5.53に向上。
- 感情および方言の柔軟性:より細かい感情制御やアクセント調整をサポート。
ロードマップ
2024/12
- 25hz cosyvoice 2.0 リリース
2024/09
- 25hz cosyvoice ベースモデル
- 25hz cosyvoice 音声変換モデル
2024/08
- Repetition Aware Sampling(RAS)による LLM 安定性推論
- kvキャッシュおよびsdpaによるRTF最適化を含むストリーミング推論モードのサポート
2024/07
- Flow Matching トレーニングサポート
- ttsfrdが未使用の場合のWeTextProcessingサポート
- FastAPIサーバとクライアント
日本語含むマルチリンガルで高速性を謳っているのは期待できそう。コードのライセンスはApache-2.0で、モデルについては明記がないのだが、以下を見る限りはモデルも同じっぽい。
論文についてはAlphAxivのまとめを参照
最初はColaboratoryで試していたのだけども・・・
- モデルはHuggingFaceとModelscopeで公開されているのだが・・・
- Modelscopeはダウンロードがめちゃめちゃ遅い
- HuggingFaceにあるものは、Modelscopeにあるものより古い、もしくは、一部のファイルが足りない様に思える
- 上記より、Colaboratoryでやり直しになるとめちゃめちゃ時間がかかるので、ローカルでやる
- 一部、Python-3.10想定のものがある。
- Colaboratoryは自分が見ている限りは3.11で、ダウングレードはいろいろと面倒
ということで、自由が効くローカルのUbuntu-22.04サーバ(RTX4090)でやることとする。
レポジトリクローン。サブモジュール含めてクローンする。
git clone --recursive https://github.com/FunAudioLLM/CosyVoice.git && cd CosyVoice
uvで仮想環境を作成。上にもある通り今回はPython-3.10で。
uv venv -p 3.10
uv pip install pip
source .venv/bin/activate
パッケージインストール。テキストの正規化に必要なpynini==2.1.5をインストール。
pip install pynini==2.1.5
その他の依存パッケージもインストール
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host=mirrors.aliyun.com
あと、soxが必要になるようなので入ってなければインストール。自分はすでにインストール済みだった。
apt-get install sox libsox-dev
そしてモデル等をダウンロードする。上に記載した通り、Modelscopeからダウンロードするのだが、これがめちゃめちゃ時間がかかる・・・気長に待つ。
mkdir -p pretrained_models
git lfs install
git clone https://www.modelscope.cn/iic/CosyVoice2-0.5B.git pretrained_models/CosyVoice2-0.5B
git clone https://www.modelscope.cn/iic/CosyVoice-300M.git pretrained_models/CosyVoice-300M
git clone https://www.modelscope.cn/iic/CosyVoice-300M-SFT.git pretrained_models/CosyVoice-300M-SFT
git clone https://www.modelscope.cn/iic/CosyVoice-300M-Instruct.git pretrained_models/CosyVoice-300M-Instruct
git clone https://www.modelscope.cn/iic/CosyVoice-ttsfrd.git pretrained_models/CosyVoice-ttsfrd
この最後のCosyVoice-ttsfrdというレポジトリだが、これはテキストの正規化精度をより高くするためのttsfrd
パッケージが含まれているらしい。ただし、このパッケージは必須というわけではなく、使用しない場合には WeTextProcessing という正規化パッケージが使用されるらしい(上の方でインストールしたPyniniはこのWeTextProcessingのために必要)。
多分これ
コードを見る限りは日本語もサポートしているように見えるが、より精度が高くなるなら…ということでttsfrd
パッケージをインストールすることとする。
CosyVoice-ttsfrdをクローンしたレポジトリに移動してZIPの展開とパッケージのインストールを行う。このパッケージがPython-3.10向けになっているようなのだよね。
cd pretrained_models/CosyVoice-ttsfrd
unzip resource.zip -d .
pip install ttsfrd_dependency-0.1-py3-none-any.whl
pip install ttsfrd-0.4.2-cp310-cp310-linux_x86_64.whl
cd ../..
これでセットアップは終了。では推論してみる。まずはゼロショットの例。
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
# より高い精度のためにはCosyVoice2-0.5Bを使用
cosyvoice = CosyVoice2(
'pretrained_models/CosyVoice2-0.5B',
load_jit=False,
load_trt=False,
fp16=False,
use_flow_cache=False
)
# リファレンス音声を読み込み
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
# 注: https://funaudiollm.github.io/cosyvoice2 の結果を再現する `text_frontend=False` を追加
for i, j in enumerate(cosyvoice.inference_zero_shot(
# 発話を生成したいテキストを指定
# 日本語訳: 「遠くから友人から誕生日プレゼントが届きました。その予期せぬ驚きと深い祝福に、私の心は甘い幸せで満たされ、笑顔が花のように咲きました。」
'收到好友从远方寄来的生日礼物,那份意外的惊喜与深深的祝福让我心中充满了甜蜜的快乐,笑容如花儿般绽放。',
# リファレンス音声内のテキストを指定
# 日本語訳: 「今後、私よりももっとうまくやっていくことを願っていますよ。」
'希望你以后能够做的比我还好呦。',
# リファレンス音声を指定
prompt_speech_16k,
stream=False
)):
torchaudio.save('zero_shot_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
リファレンスの音声はこちら。女性の声で中国語。
生成された音声はこちら
リファレンスの音声を使って発話されているのがわかる。
ゼロショット話者を保存して再利用する例
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
cosyvoice = CosyVoice2('pretrained_models/CosyVoice2-0.5B', load_jit=False, load_trt=False, fp16=False, use_flow_cache=False)
# リファレンス音声を読み込んで話者を追加
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
# 日本語訳: 「今後、私よりももっとうまくやっていくことを願っていますよ。」
assert cosyvoice.add_zero_shot_spk('希望你以后能够做的比我还好呦。', prompt_speech_16k, 'my_zero_shot_spk') is True
for i, j in enumerate(cosyvoice.inference_zero_shot(
# 日本語訳: 「遠くから友人から誕生日プレゼントが届きました。その予期せぬ驚きと深い祝福に、私の心は甘い幸せで満たされ、笑顔が花のように咲きました。」
'收到好友从远方寄来的生日礼物,那份意外的惊喜与深深的祝福让我心中充满了甜蜜的快乐,笑容如花儿般绽放。',
'',
'',
# ゼロショットで使用する話者を指定
zero_shot_spk_id='my_zero_shot_spk',
stream=False
)):
torchaudio.save('zero_shot_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
# 話者を保存
cosyvoice.save_spkinfo()
結果は最初のものと同じなので割愛。
特定のタグを含めることで、より発話を細かく制御することができる。
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
cosyvoice = CosyVoice2(
'pretrained_models/CosyVoice2-0.5B',
load_jit=False,
load_trt=False,
fp16=False,
use_flow_cache=False
)
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
# より細かい制御
# 制御可能な設定は cosyvoice/tokenizer/tokenizer.py#L248
# ---
# 'additional_special_tokens': [
# '<|im_start|>', '<|im_end|>', '<|endofprompt|>',
# '[breath]', '<strong>', '</strong>', '[noise]',
# '[laughter]', '[cough]', '[clucking]', '[accent]',
# '[quick_breath]',
# "<laughter>", "</laughter>",
# "[hissing]", "[sigh]", "[vocalized-noise]",
# "[lipsmack]", "[mn]"
# ]
# ---
for i, j in enumerate(cosyvoice.inference_cross_lingual(
# 発話を生成したいテキストにタグ的な要素を含める
# 日本語訳: 「彼がその荒唐無稽な話をしている最中に、彼は突然(笑)を止めてしまいました。なぜなら、彼自身もその話に笑ってしまったからです(笑)。」
'在他讲述那个荒诞故事的过程中,他突然[laughter]停下来,因为他自己也被逗笑了[laughter]。',
prompt_speech_16k,
stream=False
)):
torchaudio.save('fine_grained_control_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
笑い声が含まれているのがわかる。
指示を出すこともできる。
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
cosyvoice = CosyVoice2(
'pretrained_models/CosyVoice2-0.5B',
load_jit=False,
load_trt=False,
fp16=False,
use_flow_cache=False
)
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
for i, j in enumerate(cosyvoice.inference_instruct2(
# 日本語訳: 「遠くから友人から誕生日プレゼントが届きました。その予期せぬ驚きと深い祝福に、私の心は甘い喜びで満たされ、笑顔が花のように咲きました。」
'收到好友从远方寄来的生日礼物,那份意外的惊喜与深深的祝福让我心中充满了甜蜜的快乐,笑容如花儿般绽放。',
# 指示を書く
# 日本語訳: 「この言葉を四川語で言ってください」
'用四川话说这句话',
prompt_speech_16k,
stream=False
)):
torchaudio.save('instruct_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
四川語かどうかがわからない・・・w
ジェネレータで渡すこともできる。つまり、LLMのストリーミング出力を渡す場合に便利。
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
cosyvoice = CosyVoice2(
'pretrained_models/CosyVoice2-0.5B',
load_jit=False,
load_trt=False,
fp16=False,
use_flow_cache=False
)
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
#双方向ストリームを使用する場合、ジェネレーターを入力として使用できます。
# これは、テキストLLM モデルを入力として使用する場合に便利です。
# 注意: LLM は任意の文の長さを処理できないため、基本的な文分割ロジック
# は引き続き用意しておく必要があります。
def text_generator():
# 「遠くから友人から誕生日プレゼントが届きました。」
yield '收到好友从远方寄来的生日礼物,'
# 「その予期せぬ驚きと深い祝福に、」
yield '那份意外的惊喜与深深的祝福'
# 「私の心は甘い喜びで満たされ、」
yield '让我心中充满了甜蜜的快乐,'
# 「笑顔が花のように咲きました。」
yield '笑容如花儿般绽放。'
for i, j in enumerate(cosyvoice.inference_zero_shot(
text_generator(),
# 日本語訳: 「今後、私よりももっとうまくやっていくことを願っていますよ。」
'希望你以后能够做的比我还好呦。',
prompt_speech_16k,
stream=False)):
torchaudio.save('zero_shot_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
生成文章は最初と同じなので結果は割愛。
ここまでがCosyVoice「2」での利用方法で、以下は以前のCosyVoiceで使えていた機能をってことだと思う。読み込むモデル・モジュールが異なる。
学習済み音声を指定して生成
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
# 以前のCosyVoiceモデル SFT版を使用
cosyvoice = CosyVoice(
'pretrained_models/CosyVoice-300M-SFT',
load_jit=False,
load_trt=False,
fp16=False,
)
# 学習済み音声の一覧を表示
# ['中文女', '中文男', '日语男', '粤语女', '英文女', '英文男', '韩语女']
print(cosyvoice.list_available_spks())
# `stream=True`でチャンクストリーム出力
for i, j in enumerate(cosyvoice.inference_sft(
# 発話するテキスト
# 日本語訳: 「こんにちは、私は通義生成型音声大モデルです。何かお手伝いしましょうか?」
'你好,我是通义生成式语音大模型,请问有什么可以帮您的吗?',
# 使用する音声を指定
'中文女',
stream=False
)):
torchaudio.save('sft_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
ZeroShot。READMEには言語の指定があるけど、ZeroShotでどう指定していいのかわからない。次のクロスリンガルのサンプル向けのコメントじゃないかな?と思ったり。使えるのかもしれないけど。
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
# 以前のCosyVoiceモデルを使用
cosyvoice = CosyVoice('pretrained_models/CosyVoice-300M')
# リファレンス音声を読み込み
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
# ZeroShot
for i, j in enumerate(cosyvoice.inference_zero_shot(
# 初和させたいテキスト
# 日本語訳: 「遠くから友人から誕生日プレゼントが届きました。その予期せぬ驚きと深い祝福に、私の心は甘い喜びで満たされ、笑顔が花のように咲きました。」
'收到好友从远方寄来的生日礼物,那份意外的惊喜与深深的祝福让我心中充满了甜蜜的快乐,笑容如花儿般绽放。',
# リファレンス音声のテキスト
# 日本語訳: 「今後、私よりももっとうまくやっていくことを願っていますよ。」
'希望你以后能够做的比我还好呦。',
prompt_speech_16k,
stream=False
)):
torchaudio.save('zero_shot_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
クロスリンガルの例。
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
cosyvoice = CosyVoice('pretrained_models/CosyVoice-300M')
# リファレンス音声を読み込み
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
# 言語を指定
# - <|zh|> : 中国語
# - <|en|> : 英語
# - <|jp|> : 日本語
# - <|yue|> : 広東語
# - <|ko|> : 韓国語
for i, j in enumerate(cosyvoice.inference_cross_lingual(
# 言語を指定
'<|en|>And then later on, fully acquiring that company. So keeping management in line, interest in line with the asset that\'s coming into the family is a reason why sometimes we don\'t buy the whole thing.',
prompt_speech_16k,
stream=False
)):
torchaudio.save('cross_lingual_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
音声変換。元の音声の発話をリファレンス音声に変換する。
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
cosyvoice = CosyVoice('pretrained_models/CosyVoice-300M')
# リファレンス音声
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
# 元の音声
source_speech_16k = load_wav('./asset/cross_lingual_prompt.wav', 16000)
for i, j in enumerate(cosyvoice.inference_vc(
source_speech_16k,
prompt_speech_16k,
stream=False
)):
torchaudio.save('vc_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
元の音声。中国語の男性の声。
変換後の音声。
以前のモデルでも発話をプロンプトで指示できる
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
cosyvoice = CosyVoice('pretrained_models/CosyVoice-300M-Instruct')
# 以下のタグが使える
# - <laughter></laughter>
# - <strong></strong>
# - [laughter]
# - [breath]
for i, j in enumerate(cosyvoice.inference_instruct(
'在面对挑战时,他展现了非凡的<strong>勇气</strong>与<strong>智慧</strong>。',
'中文男',
'Theo \'Crimson\', is a fiery, passionate rebel leader. Fights with fervor for justice, but struggles with impulsiveness.',
stream=False
)):
torchaudio.save('instruct_{}.wav'.format(i), j['tts_speech'], cosyvoice.sample_rate)
WebUIも用意されている。--model_dir
でモデルを指定するみたいだけど、指定しなければ CosyVoice2になるみたい。
python webui.py --port 50000 --model_dir pretrained_models/CosyVoice-300M
CosyVoice2で試してみる。
python webui.py --port 50000
UIが中国語なのでよくわからないぞ・・・
DeepLで日本語翻訳した
雰囲気で使えるかなぁ。
記録し忘れたけど、VRAM消費は5GBぐらいだったと思う
あと、DockerでAPIも建てれるみたい。gRPCとREST APIがある様子。
で、肝心の日本語について。
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice, CosyVoice2
from cosyvoice.utils.file_utils import load_wav
import torchaudio
# ===== CozyVoice2 =====
cv2 = CosyVoice2('pretrained_models/CosyVoice2-0.5B', load_jit=False, load_trt=False, fp16=False, use_flow_cache=False)
# ゼロショット
prompt_speech_16k = load_wav('./asset/zero_shot_prompt.wav', 16000)
for i, j in enumerate(cv2.inference_zero_shot(
'遠くから友人から誕生日プレゼントが届きました。その予期せぬ驚きと深い祝福に、私の心は甘い幸せで満たされ、笑顔が花のように咲きました。',
'希望你以后能够做的比我还好呦。',
prompt_speech_16k,
stream=False
)):
torchaudio.save('cv2_zero_shot_ja.wav', j['tts_speech'], cv2.sample_rate)
# タグ
for i, j in enumerate(cv2.inference_cross_lingual(
'彼がその荒唐無稽な話をしている最中に、彼は突然 [laughter] 話を止めてしまいました。なぜなら、彼自身もその話に笑ってしまったからです [laughter] 。',
prompt_speech_16k,
stream=False
)):
torchaudio.save('cv2_fine_grained_control_ja.wav', j['tts_speech'], cv2.sample_rate)
# 指示
for i, j in enumerate(cv2.inference_instruct2(
'遠くから友人から誕生日プレゼントが届きました。その予期せぬ驚きと深い祝福に、私の心は甘い幸せで満たされ、笑顔が花のように咲きました。',
'これを囁くように言ってください。',
prompt_speech_16k,
stream=False
)):
torchaudio.save('cv2_instruct_ja.wav', j['tts_speech'], cv2.sample_rate)
# ===== CozyVoice =====
cv = CosyVoice('pretrained_models/CosyVoice-300M')
cv_sft = CosyVoice('pretrained_models/CosyVoice-300M-SFT', load_jit=False, load_trt=False, fp16=False)
cv_instruct = CosyVoice('pretrained_models/CosyVoice-300M-Instruct')
# 学習済み音声
for i, j in enumerate(cv_sft.inference_sft(
'こんにちは、音声生成モデルです。何かお手伝いしましょうか?',
'日语男',
stream=False
)):
torchaudio.save('cv_sft_ja.wav', j['tts_speech'], cv_sft.sample_rate)
# ZeroShot -> エラーで動かず
#for i, j in enumerate(cv.inference_zero_shot(
# '<|jp|>遠くから友人から誕生日プレゼントが届きました。その予期せぬ驚きと深い祝福に、私の心は甘い幸せで満たされ、笑顔が花のように咲きました。',
# '希望你以后能够做的比我还好呦。',
# prompt_speech_16k,
# stream=False
#)):
# torchaudio.save('cv_zero_shot_ja.wav', j['tts_speech'], cv.sample_rate)
# クロスリンガル -> エラーで動かず
#for i, j in enumerate(cv.inference_cross_lingual(
# '<|jp|>そして、その後、その会社を完全に買収します。したがって、経営陣を統制し、資産が家族のもとに流入することと利益が一致するようにすることは、私たちが会社全体を買収しない理由のひとつです。',
# prompt_speech_16k,
# stream=False
#)):
# torchaudio.save('cv_cross_lingual_ja.wav', j['tts_speech'], cv.sample_rate)
# 指示
for i, j in enumerate(cv_instruct.inference_instruct(
'彼は困難に直面した際、並外れた<strong>勇気</strong>と<strong>知恵</strong>を発揮しました。',
'日语男',
'テオ「クリムゾン」は、情熱的で激しい反乱軍のリーダーです。正義のために熱く戦いますが、衝動性に苦悩しています。',
stream=False
)):
torchaudio.save('cv_instruct_ja.wav', j['tts_speech'], cv_instruct.sample_rate)
うーん、イントネーションとかの感じはいいんだけどね。発話精度と安定性が厳しいかな・・・
まとめ
やれることは結構多いのだけど、日本語で使う上では精度と安定性の点でちょっと厳しいかな。CosyVoiceよりはCozyVoice2の方はいいのはそう(ただ日本語だとそれでも安定していない気がする)。
試してないけど、英語だと安定して動くのかしら・・・
同じ開発元が出しているこっちも気になる。そのうち試してみる。
SenseVoice は、高精度な多言語音声認識、音声感情認識、音声イベント検出に重点を置いています。
CosyVoice3の論文が出てた。そのうちコードやモデルも出てきそう。
alphaXivはこちら