「🤗 Transformersを使用してBarkを最適化する」
Optimizing Bark using 🤗 Transformers
🤗 Transformersは、ドメインやタスクにわたる最新の最先端(SoTA)モデルを提供しています。これらのモデルから最高のパフォーマンスを引き出すには、推論速度とメモリ使用量を最適化する必要があります。
🤗 Hugging Faceエコシステムは、このような最適化ツールを提供し、ライブラリ内のすべてのモデルに一括して適用することができる簡単かつ使いやすいものです。これにより、わずか数行のコードでメモリの使用量を減らし、推論を改善することができます。
このハンズオンチュートリアルでは、🤗 Transformersでサポートされているテキスト音声合成(TTS)モデルであるBarkを、3つのシンプルな最適化に基づいて最適化する方法を実演します。これらの最適化は、🤗エコシステムのTransformers、Optimum、Accelerateライブラリにのみ依存しています。
- 室温超伝導体であることが確認されたLK-99ではありません
- 「AI Time Journalが「AIにおけるSEOのトレンド2023」eBookを紹介します:SEOの将来に関する専門家の洞察」
- AIを使ってYouTubeショートを作成する
このチュートリアルは、最適化されていないモデルとそのさまざまな最適化のベンチマークを行う方法をデモンストレーションするものでもあります。
説明の少ないがコードが含まれている、より簡略化されたバージョンのチュートリアルは、関連するGoogle Colabを参照してください。
このブログ投稿は次のように構成されています:
目次
- Barkアーキテクチャのリマインダー
- 異なる最適化手法の概要とその利点
- ベンチマーク結果のプレゼンテーション
Barkは、suno-ai/barkで提案された、トランスフォーマーベースのテキスト音声合成モデルです。音声、音楽、背景音、シンプルな効果音など、さまざまなオーディオ出力を生成することができます。さらに、笑い声、ため息、泣き声など、非言語コミュニケーションの音も生成することができます。
Barkは、v4.31.0以降の🤗 Transformersで利用可能です!
Barkで遊んでその能力を発見することができます。
Barkは4つの主要なモデルで構成されています:
BarkSemanticModel
(または「テキスト」モデルとも呼ばれる):トークン化されたテキストを入力とし、テキストの意味を捉えるセマンティックなテキストトークンを予測する因果自己回帰トランスフォーマーモデルです。BarkCoarseModel
(または「粗い音響」モデルとも呼ばれる):BarkSemanticModel
モデルの結果を入力とし、EnCodecに必要な最初の2つのオーディオコードブックを予測する因果自己回帰トランスフォーマーモデルです。BarkFineModel
(「細かい音響」モデル):非因果オートエンコーダートランスフォーマーで、前のコードブック埋め込みの合計に基づいて最後のコードブックを反復的に予測します。EncodecModel
からすべてのコードブックチャネルを予測した後、Barkはそれを使用して出力オーディオ配列をデコードします。
執筆時点では、2つのBarkチェックポイントが利用可能です。小さいバージョンと大きいバージョンがあります。
モデルとプロセッサの読み込み
事前学習済みのBarkの小さいバージョンと大きいバージョンのチェックポイントは、Hugging Face Hubの事前学習済みウェイトから読み込むことができます。使用したいチェックポイントサイズに応じて、repo-idを変更することができます。
高速さを保つために、デフォルトでは小さいチェックポイントを使用しますが、"suno/bark-small"
の代わりに"suno/bark"
を使用することで、大きいチェックポイントを試すこともできます。
from transformers import BarkModel
model = BarkModel.from_pretrained("suno/bark-small")
最適化手法の最大限の効果を得るために、モデルをアクセラレータデバイスに配置します:
import torch
device = "cuda:0" if torch.cuda.is_available() else "cpu"
model = model.to(device)
トークナイズとオプションのスピーカー埋め込みを処理するプロセッサを読み込みます。
from transformers import AutoProcessor
processor = AutoProcessor.from_pretrained("suno/bark-small")
このセクションでは、🤗 Optimumおよび🤗 Accelerateライブラリの既製の機能を使用して、コードの最小限の変更でBarkモデルを最適化する方法を探ります。
いくつかのセットアップ
入力を準備し、Barkの生成メソッドのレイテンシとGPUメモリフットプリントを測定する関数を定義しましょう。
text_prompt = "音声生成を試してみましょう、Barkはテキストから音声に変換するモデルです"
inputs = processor(text_prompt).to(device)
レイテンシとGPUメモリの使用量を測定するには、特定のCUDAメソッドを使用する必要があります。モデルの推論時におけるレイテンシとGPUメモリの使用量を測定するためのユーティリティ関数を定義します。これらのメトリクスの正確な結果を得るために、指定された回数の実行nb_loops
の平均を取ります:
import torch
from transformers import set_seed
def measure_latency_and_memory_use(model, inputs, nb_loops = 5):
# generateパスの開始と終了を測定するためのイベントを定義する
start_event = torch.cuda.Event(enable_timing=True)
end_event = torch.cuda.Event(enable_timing=True)
# CUDAメモリの統計情報をリセットし、キャッシュを空にする
torch.cuda.reset_peak_memory_stats(device)
torch.cuda.empty_cache()
torch.cuda.synchronize()
# 開始時間を取得する
start_event.record()
# 実際に生成する
for _ in range(nb_loops):
# 再現性のためにシードを設定する
set_seed(0)
output = model.generate(**inputs, do_sample = True, fine_temperature = 0.4, coarse_temperature = 0.8)
# 終了時間を取得する
end_event.record()
torch.cuda.synchronize()
# メモリ使用量と経過時間を測定する
max_memory = torch.cuda.max_memory_allocated(device)
elapsed_time = start_event.elapsed_time(end_event) * 1.0e-3
print('実行時間:', elapsed_time/nb_loops, '秒')
print('最大メモリ使用量', max_memory*1e-9, ' GB')
return output
ベースケース
最適化を組み込む前に、ベースラインモデルのパフォーマンスを測定し、生成された例を聞いてみましょう。モデルを5回の反復でベンチマークし、メトリクスの平均を報告します:
with torch.inference_mode():
speech_output = measure_latency_and_memory_use(model, inputs, nb_loops = 5)
出力:
実行時間: 9.3841625 秒
最大メモリ使用量 1.914612224 GB
さて、出力を聞いてみましょう:
from IPython.display import Audio
# 出力を聞く
sampling_rate = model.generation_config.sample_rate
Audio(speech_output[0].cpu().numpy(), rate=sampling_rate)
出力は次のようになります(オーディオをダウンロード):
お使いのブラウザはオーディオ要素をサポートしていません。
重要な注意:
ここでは、反復回数が実際には非常に低いです。正確な測定と比較結果を得るためには、少なくとも100まで増やす必要があります。
nb_loops
を増やすことの重要な理由の1つは、固定された入力でも生成される音声の長さが異なることです。
これの結果として、measure_latency_and_memory_use
で測定されるレイテンシは、実際の最適化技術のパフォーマンスを正確に反映していないかもしれません!ブログ記事の最後のベンチマークでは、100回の反復で平均化された結果が報告されており、モデルのパフォーマンスを正確に示しています。
1. 🤗 Better Transformer
Better Transformerは、カーネルフュージョンを行う🤗 Optimumの機能です。これにより、特定のモデル操作がGPU上でより最適化され、モデルが最終的に高速化されます。
具体的には、🤗 Transformersでサポートされているほとんどのモデルは、出力を生成する際に入力の特定の部分に重点を置くことができるアテンションに依存しています。これにより、モデルは効果的に長距離の依存関係を処理し、データ中の複雑な文脈関係を捉えることができます。
素朴なアテンション技術は、Daoらによって2022年に提案されたFlash Attentionという技術によって大幅に最適化できます。
Flash Attentionは、アテンション計算のためのより速く、効率的なアルゴリズムで、タイリングや再計算などの従来の方法を組み合わせてメモリ使用量を最小化し、速度を向上させます。従来のアルゴリズムとは異なり、Flash Attentionはシーケンスの長さに対して二次ではなく線形のメモリ使用量を持つため、メモリ効率が重要なアプリケーションに特に有用です。
🤗 Better Transformerは、実際にはFlash Attentionをサポートしています!モデルを🤗 Better Transformerにエクスポートし、Flash Attentionを有効にするには、1行のコードが必要です:
model = model.to_bettertransformer()
with torch.inference_mode():
speech_output = measure_latency_and_memory_use(model, inputs, nb_loops = 5)
出力:
実行時間: 5.43284375 秒
最大メモリ使用量: 1.9151841280000002 GB
出力は次のようになります(音声をダウンロード):
お使いのブラウザはオーディオ要素をサポートしていません。
何をもたらしますか?
パフォーマンスの低下はありませんので、この機能を使用せずにまったく同じ結果を得ることができますが、スピードが20%から30%向上します! もっと知りたいですか? このブログ記事をご覧ください。
2. ハーフ精度
ほとんどのAIモデルは通常、シングルプレシジョン浮動小数点と呼ばれるストレージ形式、つまりfp32
を使用しています。これは実際にはどういう意味ですか? 各数値は32ビットで格納されます。
したがって、ハーフプレシジョン浮動小数点、つまりfp16
を使用して数値を16ビットでエンコードし、以前よりも半分のストレージを使用することができます! さらに、推論のスピードも向上します!
もちろん、fp32
を使用する場合と同じくらい正確ではないため、わずかなパフォーマンスの低下もあります。
ハーフプレシジョンで🤗 Transformersモデルをロードするには、BarkModel.from_pretrained(...)
の行にtorch_dtype=torch.float16
を追加するだけです!
つまり:
model = BarkModel.from_pretrained("suno/bark-small", torch_dtype=torch.float16).to(device)
with torch.inference_mode():
speech_output = measure_latency_and_memory_use(model, inputs, nb_loops = 5)
出力:
実行時間: 7.00045390625 秒
最大メモリ使用量: 2.7436124160000004 GB
出力は次のようになります(音声をダウンロード):
お使いのブラウザはオーディオ要素をサポートしていません。
何をもたらしますか?
わずかなパフォーマンスの低下で、メモリ使用量が50%減少し、速度が5%向上します。
3. CPUオフロード
この冊子の最初のセクションで説明したように、Barkには4つのサブモデルが含まれており、オーディオ生成中に順次呼び出されます。つまり、1つのサブモデルが使用されている間、他のサブモデルはアイドル状態です。
なぜこれが問題なのでしょうか? AIでは、GPUメモリは貴重です。なぜなら、そこが最も高速な処理が行われる場所であり、しばしばボトルネックになるからです。
簡単な解決策は、非アクティブなときにサブモデルをGPUからアンロードすることです。この操作をCPUオフロードと呼びます。
良いニュース: BarkのCPUオフロードは🤗 Transformersに統合されており、わずか1行のコードで使用することができます。
ただし、🤗 Accelerateがインストールされていることを確認する必要があります!
model = BarkModel.from_pretrained("suno/bark-small")
# CPUオフロードを有効にする
model.enable_cpu_offload()
with torch.inference_mode():
speech_output = measure_latency_and_memory_use(model, inputs, nb_loops = 5)
出力:
実行時間: 8.97633828125 秒
最大メモリ使用量: 1.3231160320000002 GB
出力は次のようになります(音声をダウンロード):
お使いのブラウザはオーディオ要素をサポートしていません。
何をもたらしますか?
わずかな速度の低下(10%)ですが、メモリ使用量が大幅に削減されます(60% 🤯)。
この機能を有効にすると、bark-large
のメモリ使用量は5GBから2GBになります。それはbark-small
と同じメモリ使用量です!
もっと欲しいですか? fp16
を有効にすると、1GBにまで減少します。次のセクションで実際に確認してみましょう!
4. 組み合わせる
すべてをまとめましょう。良いニュースは、最適化技術を組み合わせることができるため、CPUオフロードとハーフ精度、さらに🤗 Better Transformerを使用することができるということです。
# fp16で読み込み
model = BarkModel.from_pretrained("suno/bark-small", torch_dtype=torch.float16).to(device)
# BetterTransformerに変換
model = BetterTransformer.transform(model, keep_original_model=False)
# CPUオフロードを有効化
model.enable_cpu_offload()
with torch.inference_mode():
speech_output = measure_latency_and_memory_use(model, inputs, nb_loops = 5)
出力:
実行時間: 7.4496484375000005 秒
最大メモリ使用量: 0.46871091200000004 GB
出力は次のようになります(オーディオをダウンロード):
お使いのブラウザはオーディオ要素をサポートしていません。
何が得られるのでしょうか?
最終的には、23%の高速化と80%のメモリ削減が得られます!
バッチ処理の使用
さらに欲しいですか?
3つの最適化テクニックを組み合わせることで、バッチ処理の場合にさらに良い結果が得られます。バッチ処理とは、複数のサンプルの操作を組み合わせることで、個々のサンプルを生成するためにかかる時間を短縮することです。
以下は、その使い方の簡単な例です:
text_prompt = [
"Barkというテキストtoスピーチモデルを使用して音声を生成してみましょう",
"バッチ処理は素晴らしいですね!",
"Hugging Faceが大好きです、とてもクールです。"]
inputs = processor(text_prompt).to(device)
with torch.inference_mode():
# サンプルは一度に生成されます
speech_output = model.generate(**inputs, do_sample = True, fine_temperature = 0.4, coarse_temperature = 0.8)
出力は次のようになります(最初、2番目、最後のオーディオをダウンロード):
お使いのブラウザはオーディオ要素をサポートしていません。お使いのブラウザはオーディオ要素をサポートしていません。お使いのブラウザはオーディオ要素をサポートしていません。
上記のように、行った小さな実験は思考の練習であり、パフォーマンスのより良い測定のために拡張する必要があります。また、適切なパフォーマンスの測定の前に、GPUをいくつかの空の反復でウォームアップする必要があります。
以下は、Barkの大規模バージョンを使用した100サンプルのベンチマークの結果です。
ベンチマークは、最大256の新しいトークンを持つNVIDIA TITAN RTX 24GBで実行されました。
結果の読み方
レイテンシ
バッチサイズに関係なく、生成メソッドの単一呼び出しの時間を測定します。
つまり、elapsedTimenbLoops\frac{elapsedTime}{nbLoops}nbLoopselapsedTimeとなります。
低いレイテンシが望ましいです。
最大メモリ使用量
生成メソッドの単一呼び出し時の最大メモリ使用量を測定します。
低いフットプリントが望ましいです。
スループット
サンプルの生成数を秒単位で測定します。この場合、バッチサイズも考慮されます。
つまり、nbLoops∗batchSizeelapsedTime\frac{nbLoops*batchSize}{elapsedTime}elapsedTimenbLoops∗batchSizeとなります。
高いスループットが望ましいです。
バッチ処理なし
batch_size=1
の場合の結果は以下の通りです。
コメント
予想通り、CPUオフロードはメモリ使用量を大幅に減らし、レイテンシをわずかに増加させます。
ただし、bettertransformerとfp16
を組み合わせると、レイテンシとメモリの両方の削減が得られます!
バッチサイズを8に設定
batch_size=8
の場合のベンチマーク結果は次のとおりです。
なお、bettertransformer
は非最適化モデルと同じ操作を行い、同じメモリ使用量でより高速なため、ベンチマークはこの最適化がデフォルトで有効化された状態で実行されました。
コメント
ここでは、3つの最適化機能を組み合わせたポテンシャルを見ることができます!
fp16
のレイテンシへの影響は、batch_size = 1
ではあまり目立ちませんが、ここでは半分近くのレイテンシ削減と、スループットのほぼ倍増が可能で非常に興味深いものです。
このブログ投稿では、🤗エコシステムにバンドルされたいくつかのシンプルな最適化トリックを紹介しました。これらのテクニックのいずれかを使用するか、またはすべてを組み合わせることで、Barkの推論速度とメモリのフットプリントを大幅に改善することができます。
-
🤗 Better TransformerとCPUオフロードを使用して、パフォーマンスの低下なしにBarkの大容量バージョンを使用することができます。フットプリントはわずか2GBで、5GBの代わりに15%高速です。
-
高いスループットをお好みですか? 🤗 Better Transformerと半精度を使用してバッチを8つずつ処理します。
-
fp16、🤗 Better Transformer、CPUオフロードを使用することで、ベストオブ両方の世界を得ることができます。
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles