「Orpheus-TTS」を試す
ここで知った。
公式のデモ
公式の記事
人間らしい音声合成技術に向けて
Orpheus Speech のご紹介
現時点で、オープンソースのTTSモデルは、クローズドソースのモデルに対して競争力のあるものはありません。また、TTSモデルは、人間の感情的知性に匹敵する共感を表現することもできていません。
私たちは、人間レベルの音声生成のために、最先端の音声LLMファミリーであるOrpheusを紹介します。また、Llamaアーキテクチャに基づき、事前学習と微調整を行ったモデルを4つのサイズでリリースします:
- Medium - 3B parameters
- Small - 1B parameters
- Tiny - 400M parameters
- Nano - 150M parameters
私たちは、非常に小さなモデルサイズであっても、非常に高品質で審美的な音声生成を実証しています。
私たちの微調整されたモデルは、音声の選択で訓練され、プロダクションで使用することができます。また、ゼロショットボイスのクローニングや独自の微調整に使用できる、微調整スクリプトのサンプルと共にベースモデルを提供しています。
また、非常にシンプルなPythonパッケージでリアルタイムストリーミングを行うコードも提供しています。ストリーミング推論は、A100の40GBで30億パラメータモデルでも再生より高速です。
(Google Colabノートブックを参照)
デモをお試しください
事前学習済みモデルと事後学習済みモデルの両方について、簡単に推論できるように設定しました。 以下のリンクから、モデルの実行結果をご覧ください。
技術概要
モデルのアーキテクチャ
referred from https://canopylabs.ai/model-releases我々の事前学習済みモデルはLlama-3bをバックボーンとして使用しています。10万時間以上の英語音声データと数十億のテキスト・トークンでトレーニングしました。テキスト・トークンでトレーニングすることで、言語に対する理解が深まり、TTSタスクでのパフォーマンスが向上します。以下では、このモデルの興味深い能力をいくつか紹介する。
私たちは、エンドツーエンドのスピーチモデルをトレーニングするために、全く同じアーキテクチャとトレーニング方法を使用しており、おそらく今後数週間のうちにオープンソースのエンドツーエンドスピーチモデルをリリースする予定です。
↑最後の部分がちょっと熱いな
本番環境での使用
私たちのモデルは、LLMアーキテクチャにより、高精度、表現力豊かで、カスタマイズが可能です。 エコシステムにおけるLlamaモデルの大規模なサポートと、私たちが持つ膨大な量の音声とテキストデータにより、モデルが拡張されました。
リアルタイム利用
リアルタイムの使用は、会話のユースケースを可能にする。 我々のモデルは、約200 msという非常に低いレイテンシーでリアルタイム出力ストリーミングをサポートしている。 さらに低レイテンシを実現するために、我々のモデルのKVキャッシュにテキストを入力ストリーミングすることで、レイテンシを〜25〜50ミリ秒まで短縮することができる。
モデル設計
我々は、実時間音声LLMの慣例に反する2つの設計パラダイムを選択した。
Snacは異なる頻度でトークンをサンプリングし、それを次のように平坦化する。
referred from https://canopylabs.ai/model-releasesフレームあたり7つのトークンを取得し、7つのLMヘッドを使用するのではなく、1つの平坦化されたシーケンスとしてデコードする。 これにより、モデルが生成するステップ数が増加する。 このモデルは、A100 または H100 GPU 上で素直な vLLM 実装を使用して、リアルタイム再生よりも快適な速さでトークンを生成できます。
トークン生成には非ストリーミング(CNNベース)を使用。 SNACをデコーダーとして使用する他の音声LLMは、デトークナイザーに供給されるフレーム間のポッピングに悩まされている。 私たちは、デトケナイザーの実装に簡単なスライディングウィンドウの修正を加え、ポッピングのないストリーミングを可能にしました。
以下のデモ音声が用意されている。
- ElevenLabs・PlayHTとの流暢さの比較例
- ElevenLabs・PlayHTとのゼロショット音声クローンの比較例
- (特定タグによる)ガイド付き感情とイントネーションの例
なお、2025/3/20時点で、今のところリリースされているのは3Bモデルのみ。
FTモデル
事前学習モデル
GitHubレポジトリ
公式記事には記載がないものをピックアップして補足
Colab 上での簡単なセットアップ
- ファインチューニング済みモデル用 Colab(ストリーミングではなく、下記のリアルタイムストリーミングを参照) – 日常的な TTS アプリケーション向けにファインチューニングされたモデル。
- Pretrained モデル用 Colab – このノートブックは条件付き生成用に設定されていますが、さまざまなタスクに拡張可能です。
とりあえずはファインチューニングモデル用のノートブックをまず試せば良さそう。
その他
- リアルタイムストリーミング推論の例
- プロンプトの使い方
- ファインチューニング手順の概要
また以下のような例もあった。
LM Studioで動かす例
GradioのWebUIの例
ファインチューニングモデル用ノートブックをColaboratoryで動かしてみる。
ノートブックに設定されているランタイムはA100だが、自分が試した感じだと、
- デモをそのまま動かすだけならVRAM 8GBぐらい
- メモリも結構使う
ようなので、T4 ハイメモリならとりあえずミニマムで動かせる。ただし、発話させるプロンプトを変えたり増やしたりすると、必要なVRAMもメモリも増えるので、適宜L4なりA100なりを選択。
パッケージインストール。
- snac: オーディオデータを低ビットレートに圧縮するマルチスケールニューラルオーディオコーデックを使うためのライブラリ
- ipywebrtc: ノートブック環境でWebRTC/MediaStream APIを使うためのライブラリ
!pip install snac ipywebrtc
モデル等のセットアップ
#@title Installation & Setup
%%capture
!pip install snac ipywebrtc
from snac import SNAC
import torch
import torch
from transformers import AutoModelForCausalLM, Trainer, TrainingArguments, AutoTokenizer
import numpy as np
import soundfile as sf
import IPython.display as ipd
import librosa
from ipywebrtc import AudioRecorder, Audio
from IPython.display import display
import ipywidgets as widgets
from huggingface_hub import snapshot_download
# SNACモデルはCPUにロード、より高速な推論の場合はGPUに変更も可
snac_model = SNAC.from_pretrained("hubertsiuzdak/snac_24khz")
snac_model = snac_model.to("cpu")
model_name = "canopylabs/orpheus-3b-0.1-ft"
tokeniser_name = "meta-llama/Llama-3.2-3B-Instruct"
# モデルの一部ファイルのみをダウンロード
model_path = snapshot_download(
repo_id=model_name,
allow_patterns=[
"config.json",
"*.safetensors",
"model.safetensors.index.json",
],
ignore_patterns=[
"optimizer.pt",
"pytorch_model.bin",
"training_args.bin",
"scheduler.pt",
"tokenizer.json",
"tokenizer_config.json",
"special_tokens_map.json",
"vocab.json",
"merges.txt",
"tokenizer.*"
]
)
# モデルとトークナイザーをロード
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.bfloat16)
model.cuda()
tokenizer = AutoTokenizer.from_pretrained(model_name)
nvidia-smiの結果は約6.5GB
Thu Mar 20 08:13:53 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |
| N/A 49C P0 26W / 70W | 6444MiB / 15360MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
プロンプトの設定。感情のタグ設定は公式記事やGitHubのREADMEを参照。
# バッチでプロンプトを渡すことができる。<chuckle> などの感情をタグで指定できる。
prompts = [
"Hey there my name is Tara, <chuckle> and I'm a speech generation model that can sound like a person.",
"I've also been taught to understand and produce paralinguistic things like sighing, or chuckling, or yawning!",
"I live in San Francisco, and have, uhm let's see, 3 billion 7 hundred ... well, lets just say a lot of parameters!",
]
# 音声の選択
# "tara", "leah", "jess", "leo", "dan", "mia", "zac", "zoe" から選択
chosen_voice = "tara"
プロンプトをテンプレートに変換
prompts = [f"{chosen_voice}: " + p for p in prompts]
all_input_ids = []
for prompt in prompts:
input_ids = tokenizer(prompt, return_tensors="pt").input_ids
all_input_ids.append(input_ids)
start_token = torch.tensor([[ 128259]], dtype=torch.int64) # Start of human
end_tokens = torch.tensor([[128009, 128260]], dtype=torch.int64) # End of text, End of human
all_modified_input_ids = []
for input_ids in all_input_ids:
modified_input_ids = torch.cat([start_token, input_ids, end_tokens], dim=1) # SOH SOT Text EOT EOH
all_modified_input_ids.append(modified_input_ids)
all_padded_tensors = []
all_attention_masks = []
max_length = max([modified_input_ids.shape[1] for modified_input_ids in all_modified_input_ids])
for modified_input_ids in all_modified_input_ids:
padding = max_length - modified_input_ids.shape[1]
padded_tensor = torch.cat([torch.full((1, padding), 128263, dtype=torch.int64), modified_input_ids], dim=1)
attention_mask = torch.cat([torch.zeros((1, padding), dtype=torch.int64), torch.ones((1, modified_input_ids.shape[1]), dtype=torch.int64)], dim=1)
all_padded_tensors.append(padded_tensor)
all_attention_masks.append(attention_mask)
all_padded_tensors = torch.cat(all_padded_tensors, dim=0)
all_attention_masks = torch.cat(all_attention_masks, dim=0)
input_ids = all_padded_tensors.to("cuda")
attention_mask = all_attention_masks.to("cuda")
出力を生成
# Model.generateは生成が遅い。リアルタイムでのストリーミング推論についてはGitHubのvLLM実装を確認のこと
# 推論パラメータを調整すると、より感情豊かになるが、生成は不安定になる
with torch.no_grad():
generated_ids = model.generate(
input_ids=input_ids,
attention_mask=attention_mask,
max_new_tokens=1200,
do_sample=True,
temperature=0.6,
top_p=0.95,
repetition_penalty=1.1,
num_return_sequences=1,
eos_token_id=128258,
)
生成時点でのnvidia-smiの結果。約7.6GB。プロンプトを増やすとよりVRAMが必要になる。
Thu Mar 20 08:17:19 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |
| N/A 76C P0 71W / 70W | 7568MiB / 15360MiB | 99% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
出力を音声としてパース。さっぱり理解が追いつかないが、公式記事にあった「7つのLMヘッドを使用するのではなく、1つの平坦化されたシーケンスとしてデコードする。」ってのはこのあたりなんだろう。
token_to_find = 128257
token_to_remove = 128258
token_indices = (generated_ids == token_to_find).nonzero(as_tuple=True)
if len(token_indices[1]) > 0:
last_occurrence_idx = token_indices[1][-1].item()
cropped_tensor = generated_ids[:, last_occurrence_idx+1:]
else:
cropped_tensor = generated_ids
mask = cropped_tensor != token_to_remove
processed_rows = []
for row in cropped_tensor:
masked_row = row[row != token_to_remove]
processed_rows.append(masked_row)
code_lists = []
for row in processed_rows:
row_length = row.size(0)
new_length = (row_length // 7) * 7
trimmed_row = row[:new_length]
trimmed_row = [t - 128266 for t in trimmed_row]
code_lists.append(trimmed_row)
def redistribute_codes(code_list):
layer_1 = []
layer_2 = []
layer_3 = []
for i in range((len(code_list)+1)//7):
layer_1.append(code_list[7*i])
layer_2.append(code_list[7*i+1]-4096)
layer_3.append(code_list[7*i+2]-(2*4096))
layer_3.append(code_list[7*i+3]-(3*4096))
layer_2.append(code_list[7*i+4]-(4*4096))
layer_3.append(code_list[7*i+5]-(5*4096))
layer_3.append(code_list[7*i+6]-(6*4096))
codes = [torch.tensor(layer_1).unsqueeze(0),
torch.tensor(layer_2).unsqueeze(0),
torch.tensor(layer_3).unsqueeze(0)]
audio_hat = snac_model.decode(codes)
return audio_hat
my_samples = []
for code_list in code_lists:
samples = redistribute_codes(code_list)
my_samples.append(samples)
なお、一番最初のモデルセットアップ時にSNACモデルはCPU上にロードしていたので、ここの処理はCPUで行われ、メモリ使用量が大きく上昇する。ハイメモリが必要なのはこのため。
上記でオーディオデータが生成されているので、これをColaboratory上で再生。
from IPython.display import display, Audio
if len(prompts) != len(my_samples):
raise Exception("Number of prompts and samples do not match")
else:
for i in range(len(my_samples)):
print(prompts[i])
samples = my_samples[i]
display(Audio(samples.detach().squeeze().to("cpu").numpy(), rate=24000))
生成されたものを1つにマージしたのが以下。
ちなみに生成するたびに結構結果は変わる。
なお、日本語には非対応。
Llamaベースの日本語モデルを使って、データも用意して、事前学習からやればできるのかなぁ・・・
とりあえずIssueは上がってる
他の言語で微調整を行う場合、2つの問題が発生する可能性があります。
- ベースモデルと事前学習データは主に英語のみなので、同じ結果を得るには、おそらく非常に大規模なデータセットで学習を行う必要があるでしょう。その後、特定の音声で微調整を行い、より高品質な結果を得ます。
- アルファベット - これらが通常の Llama テキストトークナイザーでトークン化できるのであれば、はい、可能です。ただし、これらはかなり訓練不足の状態になります。そのため、やはり大量のデータで訓練する必要があります。おそらく2,000~3,000時間(推測ですが)です。私の頭の中にある限りでは、プログラムで音訳してローマ字に変換できれば、おそらく訓練の負担は軽減されるでしょう。
私たちはチームと話し合いを重ねており、需要があれば、今後数週間のうちに、より多様な言語セットで事前学習したモデルを作成することに間違いなく前向きです。これは、微調整がはるかに簡単になるはずです。
こういうのもあった