「OuteTTS」を試す
OuteTTS バージョン 0.3
OuteTTSバージョン0.3では、さまざまな使用事例に合わせて調整された複数のモデルバリエーションが導入されています。このリリースでは、句読点のサポートを追加し、生成された音声の流暢性と明瞭性を向上させることで、音声合成の自然性と一貫性が大幅に強化されています。サポートされている句読点は次のとおりです:
'.', '!', '?', ',', '"', '„', '¡', '¿', '…', '...', '。', '!', '?', ',', '؟'
。これらは特殊トークンに変換されます。例えば、.
は<|period|>
に変換されます。さらに、モデルは改良され拡張されたデータセットでトレーニングされ、より幅広い言語をカバーできるようになりました。このバージョンでは、新たに**ドイツ語(de)とフランス語(fr)**の2言語がサポートされ、英語(en)、日本語(jp)、韓国語(ko)、中国語(zh)、フランス語(fr)、**ドイツ語(de)**の6言語に対応しています。OuteTTSは、テキスト音声変換(TTS)と音声合成機能により、既存のあらゆる大規模言語モデル(LLM)を拡張するソリューションとして設計されています。オリジナルのアーキテクチャを維持することで、幅広いライブラリやツールとの高い互換性を確保し、柔軟性を損なうことなく音声機能の統合を容易にしています。
実験的な音声コントロール機能も含まれていますが、開発はまだ初期段階です。データが限られているため、これらの機能では一貫性のない結果が生成されることがあり、モデルによっては無視されることもあります。
このモデルのトレーニングを可能にしたGPUグラントを提供してくれたHugging Face🤗に感謝いたします!
利用可能なバリエーション
OuteTTS-0.3-500M
- ベース:Qwen2.5-0.5B(Apache-2.0
- TTSモデルライセンス:CC-BY-SA-4.0
- トレーニング:10,000時間の音声オーディオ(〜約40億トークン)
- サポートされている言語:en、jp、ko(小規模データセット)、zh、fr、de
OuteTTS-0.3-1B
- ベース:OLMo-1B(Apache-2.0
- TTSモデルライセンス:CC-BY-NC-SA-4.0(Emiliaデータセットを組み込み、品質を向上)
- トレーニング:20,000時間分の音声オーディオ(〜約80億トークン)
- サポート対象言語:en、jp、ko、zh、fr、de
GGUF版もある
ライセンスは、1BはCC-BY-NC-SA-4.0、500MはCC-BY-SA-4.0なので、注意。
Colaboratory T4で。
パッケージインストール
!pip install -U outetts
!pip freeze | grep -i outetts
outetts==0.3.2
使い方は以下にある
モデルの設定とロード。500Mモデルを使用。
import outetts
# モデルの設定
model_config = outetts.HFModelConfig_v2(
model_path="OuteAI/OuteTTS-0.3-500M",
tokenizer_path="OuteAI/OuteTTS-0.3-500M"
)
# インタフェースを初期化
interface = outetts.InterfaceHF(model_version="0.3", cfg=model_config)
利用可能な音声の一覧
# デフォルトで利用可能な発話音声を表示
interface.print_default_speakers()
Available default speakers v2: [
'de_male_1',
'de_male_2',
'en_female_1',
'en_male_1',
'en_male_2',
'en_male_3',
'fr_female_1',
'fr_female_2',
'fr_male_1',
'fr_male_2',
'jp_female_1',
'jp_female_2',
'jp_male_1',
'ko_female_1',
'zh_female_1',
'zh_female_2',
'zh_male_1',
'zh_male_2'
]
日本語は、女性音声x1、男性音声x1が用意されている様子。
一旦は英語で試してみる。
from IPython.display import Audio
speaker_name = "en_male_1"
# 発話音声をロード:英語・男性1
speaker = interface.load_default_speaker(name=speaker_name)
# 発話を生成
gen_cfg = outetts.GenerationConfig(
text="Speech synthesis is the artificial production of human speech.",
temperature=0.1,
repetition_penalty=1.1,
max_length=4096,
speaker=speaker,
)
output = interface.generate(config=gen_cfg)
# 生成された音声をファイルに保存
output.save(f"{speaker_name}.wav")
# 再生
display(Audio(f"{speaker_name}.wav", autoplay=True))
実際に生成されたものはこちら
日本語もやってみる。
speaker_name = "jp_female_1"
# 発話音声をロード:日本語・女性1
speaker = interface.load_default_speaker(name=speaker_name)
gen_cfg = outetts.GenerationConfig(
text="音声合成とは、人間の音声を人工的に作り出すことです。",
temperature=0.1,
repetition_penalty=1.1,
max_length=4096,
speaker=speaker
)
output = interface.generate(config=gen_cfg)
output.save(f"{speaker_name}.wav")
display(Audio(f"{speaker_name}.wav", autoplay=True))
実際に生成されたもの
ついでに日本語・女性2と日本語・男性1も。
VRAMはこんな感じ
500M
1B
GGUFやExLlamaV2にも対応しているようなので、そのあたりを使えばよりVRAM消費を抑えれるかもしれない。
ということでGGUFを試してみる。今回はQ4_K_M
で。
!wget --content-disposition https://huggingface.co/OuteAI/OuteTTS-0.3-500M-GGUF/resolve/main/OuteTTS-0.3-500M-Q4_K_M.gguf?download=true
llama-cpp-python
が必要になる。ビルドはめちゃめちゃ時間がかかるので、事前ビルド済みのパッケージを使った。
!CMAKE_ARGS="-DGGML_CUDA=on" FORCE_CMAKE=1 pip install llama-cpp-python
GGUF向けにモデルの設定とロード。n_gpu_layers
を指定できるってことはGPUオフロードもできるってことだよね?
import outetts
# GGUFモデルの設定
model_config = outetts.GGUFModelConfig_v2(
model_path="OuteTTS-0.3-500M-Q4_K_M.gguf",
tokenizer_path="OuteAI/OuteTTS-0.3-500M",
n_gpu_layers=0,
)
# GGUFインタフェースを初期化
interface = outetts.InterfaceGGUF(model_version="0.3", cfg=model_config)
以下のようなログが延々と出るが、とりあえず気にしなくて良さそう。
llm_load_vocab: control token: 153329 '<|1657|>' is not marked as EOG
llm_load_vocab: control token: 152927 '<|1255|>' is not marked as EOG
llm_load_vocab: control token: 155037 '<|3365|>' is not marked as EOG
llm_load_vocab: control token: 154228 '<|2556|>' is not marked as EOG
llm_load_vocab: control token: 155425 '<|3753|>' is not marked as EOG
あとは同じ使い方。
from IPython.display import Audio
speaker_name = "en_male_1"
speaker = interface.load_default_speaker(name=speaker_name)
gen_cfg = outetts.GenerationConfig(
text="Speech synthesis is the artificial production of human speech.",
temperature=0.1,
repetition_penalty=1.1,
max_length=4096,
speaker=speaker
)
output = interface.generate(config=gen_cfg)
output.save(f"{speaker_name}.wav")
display(Audio(f"{speaker_name}.wav", autoplay=True))
推論時のログを見ていると25レイヤーあることがわかる。
llm_load_tensors: offloading 0 repeating layers to GPU
llm_load_tensors: offloaded 0/25 layers to GPU
GPUオフロードしてみる。
import outetts
model_config = outetts.GGUFModelConfig_v2(
model_path="OuteTTS-0.3-500M-Q4_K_M.gguf",
tokenizer_path="OuteAI/OuteTTS-0.3-500M",
n_gpu_layers=25, # オフロードするレイヤー数
)
interface = outetts.InterfaceGGUF(model_version="0.3", cfg=model_config)
from IPython.display import Audio
speaker_name = "en_male_1"
speaker = interface.load_default_speaker(name=speaker_name)
gen_cfg = outetts.GenerationConfig(
text="Speech synthesis is the artificial production of human speech.",
temperature=0.1,
repetition_penalty=1.1,
max_length=4096,
speaker=speaker
)
output = interface.generate(config=gen_cfg)
output.save(f"{speaker_name}.wav")
display(Audio(f"{speaker_name}.wav", autoplay=True))
オフロードされているのがわかる
llm_load_tensors: offloading 24 repeating layers to GPU
llm_load_tensors: offloading output layer to GPU
llm_load_tensors: offloaded 25/25 layers to GPU
VRAMも使用されているが、GGUFで量子化だとやはり消費が少なめ。
音声クローンもできる。まずREADMEに推奨事項が記載されている
スピーカープロファイルの推奨事項
スピーカープロファイルを作成する際に最良の結果を得るため、以下の推奨事項を考慮してください:
- 音声クリップの長さ:
- 約10秒間の音声クリップを使用してください。
- この長さは、モデルが話者の特徴を学習するのに十分なデータを提供しつつ、入力を管理しやすい範囲に抑えます。
- 音声の品質:
- 音声が明瞭でノイズがないことを確認してください。
- 背景ノイズや歪みがあると、モデルが正確な声の特徴を抽出する能力が低下します。
- 話者の特徴:
- モデルは、訓練時に見た声に近いものを最も得意とします。訓練データに比べて大きく異なる声(例:特異なアクセントや珍しい声の特徴)を使用すると、正確に再現されない場合があります。
- そのような場合、対象となる話者の声に特化してモデルを微調整することで、より良い再現性を得られる可能性があります。
- パラメータの調整:
- 生成機能のtemperatureなどのパラメータを調整することで、合成音声の表現力や一貫性を洗練させることができます。
なるほど、音声クリップは10秒程度でいいのだけど、元の学習データセットに近しいものを使うのが良さそうである。OuteTTSの学習データを見る限り、日本語の場合はMozilla Common Voiceが良さそう。
今回は手元のITAコーパスに準じたとある音声を使ってみる(結果は公開しない)
手順は以下。
モデルとインタフェースについてはこれまでと同じ。
import outetts
# モデルの設定
model_config = outetts.HFModelConfig_v2(
model_path="OuteAI/OuteTTS-0.3-500M",
tokenizer_path="OuteAI/OuteTTS-0.3-500M"
)
# インタフェースを初期化
interface = outetts.InterfaceHF(model_version="0.3", cfg=model_config)
今回のWAVはITAコーパスの一部(RECITATION324_127
)を発話したもので、だいたい10秒程度。これを使ってクローン音声から話者プロファイルを作成する。
speaker = interface.create_speaker(
audio_path="RECITATION324_127.wav",
transcript="満洲は雨季以外には雨が少ないと言われているが、わたしが満洲にあるあいだは、大戦中のせいか、ずいぶん雨が多かった。"
)
# 話者プロファイルを保存
interface.save_speaker(speaker, "sample.json")
作成した話者プロファイルを使ってTTSさせてみる。
from IPython.display import Audio
speaker = interface.load_speaker("sample.json")
gen_cfg = outetts.GenerationConfig(
text="今日はいいお天気ですね。競馬観戦にもってこいですね。",
temperature=0.1,
repetition_penalty=1.1,
max_length=4096,
speaker=speaker,
)
output = interface.generate(config=gen_cfg)
output.save("sample.wav")
display(Audio("sample.wav", autoplay=True))
自分が試した限りだと、元の音声とはかなり音声が異なる感じになった。適当な音声データで学習できる、というわけではないように感じる。このあたりは元の学習データにあるコーパスにしたがった音声データで学習した場合にどうなるのか、が気になるところ。
また、ドキュメントにはマイクから直接入力して文字起こしはWhisperを使うやり方も記載されている。デモも用意されているので、音声クローンはそれを使って確認するのがいいかもしれない。
まとめ
イントネーションは結構自然に感じた。GGUFで使えるってのもいいね。ただライセンス的にはちょっと使いにくいかもしれない。
学習に関しては以下のような最低限のドキュメントしかない。自分はこのあたりの知見がないので、これで十分かどうかはちょっとわからないな・・・
1.0が出ているがすべてCC-BY-NC-SA-4.0、かつ、Llamaのライセンスも継承してるっぽい
- 1BはLlama3.2ベースで独自のデータセットで学習しているため、CC-BY-NC-SA-4.0、かつ、Llamaのライセンスを継承
- 0.6BはQwen3ベースでオープンなデータセットで学習しているため、Apache-2.0かつCC-BY
の様子。
参考