「🧨 JAXを使用したCloud TPU v5eでの高速で安定したXL推論の拡散を加速する」
「🧨 JAXを活用したCloud TPU v5eでの高速かつ安定なXL推論拡大の促進」
生成AIモデルであるStable Diffusion XL(SDXL)などは、幅広い応用において高品質でリアルなコンテンツの作成を可能にします。しかし、このようなモデルの力を利用するには、大きな課題や計算コストが伴います。SDXLは、そのUNetコンポーネントがモデルの以前のバージョンのものよりも約3倍大きい大きな画像生成モデルです。このようなモデルを実稼働環境に展開することは、増加したメモリ要件や推論時間の増加などの理由から難しいです。今日、私たちはHugging Face DiffusersがJAX on Cloud TPUsを使用してSDXLをサポートすることを発表できることを大いに喜んでいます。これにより、高性能でコスト効率の良い推論が可能になります。
Google Cloud TPUsは、大規模なAIモデルのトレーニングや推論を含む、最先端のLLMsや生成AIモデルなどのために最適化されたカスタムデザインのAIアクセラレータです。新しいCloud TPU v5eは、大規模AIトレーニングや推論に必要なコスト効率とパフォーマンスを提供するよう特別に設計されています。TPU v4の半分以下のコストで、より多くの組織がAIモデルのトレーニングと展開が可能になります。
🧨 Diffusers JAX連携は、XLAを介してTPU上でSDXLを実行する便利な方法を提供します。それに対応するデモも作成しました。このデモは、時間のかかる書式変換や通信時間、フロントエンド処理を含めて約4秒で4つの大きな1024×1024の画像を提供するために複数のTPU v5e-4インスタンス(各インスタンスに4つのTPUチップがあります)で実行されます。実際の生成時間は2.3秒です。以下で詳しく見ていきましょう!
このブログ記事では、
- なぜJAX + TPU + DiffusersはSDXLを実行するための強力なフレームワークなのかを説明します。
- DiffusersとJAXを使用してシンプルな画像生成パイプラインを書く方法を説明します。
- さまざまなTPU設定を比較したベンチマークを示します。
SDXLにはなぜJAX + TPU v5eを使うのか?
JAX on Cloud TPU v5eを使用して高性能かつコスト効率の良いSDXLを提供することは、特別に設計されたTPUハードウェアとパフォーマンスに最適化されたソフトウェアスタックの組み合わせによって可能になります。以下に2つの主要な要素を示します:JAXのジャストインタイム(jit)コンパイルとJAX pmapによるXLAコンパイラ駆動の並列性。
ジャストインタイム(jit)コンパイル
JAXの注目すべき特徴の1つは、ジャストインタイム(jit)コンパイルです。JITコンパイラは、最初の実行時にコードをトレースし、高度に最適化されたTPUバイナリを生成し、後続の呼び出しで再利用します。このプロセスの注意点は、入力、中間、出力の形状が静的である必要があります。つまり、予め知られている必要があるということです。形状が変更されるたびに、新しい費用のかかるコンパイルプロセスが再トリガされます。JITコンパイルは、静的形状を基に設計されたサービスに最適です。コンパイルは一度実行され、その後は超高速の推論時間を活用することができます。
画像生成はJITコンパイルに適しています。常に同じ数の画像を生成し、それらが同じサイズであれば、出力形状は一定で、事前にわかっています。テキスト入力も一定です。Stable DiffusionとSDXLは、ユーザーが入力するプロンプトを表す固定形状の埋め込みベクトル(パディングあり)を使用します。そのため、固定形状に依存するJAXコードを書くことができ、大幅に最適化することができます!
高バッチサイズに対する高性能スループット
JAXのpmapを使用することで、ワークロードを複数のデバイスにスケーリングすることができます。pmapは、単一プログラム複数データ(SPMD)プログラムを表現します。関数にpmapを適用すると、XLAで関数がコンパイルされ、さまざまなXLAデバイス上で並列に実行されます。テキストから画像生成ワークロードでは、同時にレンダリングされる画像の数を増やすことが容易であり、パフォーマンスを損なうことはありません。例えば、TPU上でSDXLを実行する場合、8つのチップを使用すると、1つのチップが単一の画像を作成するのと同じ時間で、8つの画像を生成することができます。
TPU v5eインスタンスは、1、4、8チップ形状など複数の形状で提供されており、最大256チップ(完全なTPU v5eポッド)まで拡張可能です。さらに、チップ間での超高速なICIリンクを備えています。これにより、JAXとTPUが提供する並列処理を効果的に活用しながら、最適なTPU形状を選択することが可能です。
JAXで画像生成のパイプラインを作成する方法
JAXを使用して推論を超高速で実行するために必要なコードをステップバイステップで説明します!最初に、必要な依存関係をインポートしましょう。
# SDXL JAXのベストプラクティスを表示するimport jaximport jax.numpy as jnpimport numpy as npfrom flax.jax_utils import replicatefrom diffusers import FlaxStableDiffusionXLPipelineimport time
次に、基本のSDXLモデルと推論に必要な他のコンポーネントをロードします。diffusersパイプラインは、すべてをダウンロードしキャッシュする処理を請け負っています。JAXの関数型アプローチに従うため、モデルのパラメータは別々に返され、推論時にパイプラインに渡す必要があります:
pipeline、params = FlaxStableDiffusionXLPipeline.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", split_head_dim=True)
モデルパラメータはデフォルトで32ビット精度でダウンロードされます。メモリを節約し計算を高速化するために、それらを効率的な16ビット表現であるbfloat16
に変換します。ただし、注意点があります。最良の結果を得るために、スケジューラの状態をfloat32
のままにしておかなければなりません。そうしないと、精度のエラーが蓄積され、低品質または黒い画像になる可能性があります。
scheduler_state = params.pop("scheduler")params = jax.tree_util.tree_map(lambda x: x.astype(jnp.bfloat16), params)params["scheduler"] = scheduler_state
これで、プロンプトとパイプラインの他の入力を設定する準備が整いました。
default_prompt = "高品質の写真:プールで遊ぶ赤ちゃんイルカにパーティーハットを被せる"default_neg_prompt = "イラスト、低品質"default_seed = 33default_guidance_scale = 5.0default_num_steps = 25
プロンプトはテンソルとしてパイプラインに供給する必要があり、呼び出しごとに同じ次元を持っていなければなりません。これにより、推論呼び出しをコンパイルすることができます。パイプラインのprepare_inputs
メソッドは、これらの手順をすべて実行してくれるため、プロンプトとネガティブプロンプトの両方をテンソルとして準備するためのヘルパー関数を作成します。後でgenerate
関数から使用します:
def tokenize_prompt(prompt, neg_prompt): prompt_ids = pipeline.prepare_inputs(prompt) neg_prompt_ids = pipeline.prepare_inputs(neg_prompt) return prompt_ids, neg_prompt_ids
並列化を活用するために、入力をデバイス全体で複製します。Cloud TPU v5e-4は4つのチップを持つため、入力を複製することで、それぞれのチップに異なる画像を並列に生成することができます。各チップに異なるランダムシードを提供する必要があるため、注意が必要です。
NUM_DEVICES = jax.device_count()# モデルパラメータは推論中に変わらないため、一度だけ複製すれば十分です。p_params = replicate(params)def replicate_all(prompt_ids, neg_prompt_ids, seed): p_prompt_ids = replicate(prompt_ids) p_neg_prompt_ids = replicate(neg_prompt_ids) rng = jax.random.PRNGKey(seed) rng = jax.random.split(rng, NUM_DEVICES) return p_prompt_ids, p_neg_prompt_ids, rng
これで、すべてを含んだgenerate関数を組み立てる準備が整いました:
def generate( prompt, negative_prompt, seed=default_seed, guidance_scale=default_guidance_scale, num_inference_steps=default_num_steps,): prompt_ids, neg_prompt_ids = tokenize_prompt(prompt, negative_prompt) prompt_ids, neg_prompt_ids, rng = replicate_all(prompt_ids, neg_prompt_ids, seed) images = pipeline( prompt_ids, p_params, rng, num_inference_steps=num_inference_steps, neg_prompt_ids=neg_prompt_ids, guidance_scale=guidance_scale, jit=True, ).images # 画像をPIL形式に変換する images = images.reshape((images.shape[0] * images.shape[1], ) + images.shape[-3:]) return pipeline.numpy_to_pil(np.array(images))
jit=True
は、パイプライン呼び出しをコンパイルすることを示します。これには、最初に generate
を呼び出すときに行われますが、非常に遅くなります。JAXは操作をトレースし、最適化し、低レベルのプリミティブに変換する必要があります。プロセスを完了し、実行を開始するために最初の生成を実行します。
start = time.time()print(f"コンパイル中...")generate(default_prompt, default_neg_prompt)print(f"コンパイルにかかった時間:{time.time() - start}")
最初に実行したときに約3分かかりました。ただし、コードがコンパイルされたら、推論は非常に高速になります。もう一度試してみましょう!
start = time.time()prompt = "llama in ancient Greece, oil on canvas"neg_prompt = "cartoon, illustration, animation"images = generate(prompt, neg_prompt)print(f"推論にかかった時間:{time.time() - start}")
4つの画像を生成するのに約2秒かかりました!
ベンチマーク
以下の数値は、デフォルトの Euler Discrete スケジューラを使用して、SDXL 1.0 baseを20ステップ実行した結果です。同じバッチサイズで Cloud TPU v5e と TPUv4 を比較しています。なお、並列処理のため、デモで使用している TPU v5e-4 デバイスは、バッチサイズが 1 の場合には4つの画像を生成します (バッチサイズが 2 の場合には8つの画像)。同様に、TPU v5e-8 はバッチサイズが 1 の場合には 8 つの画像を生成します。
Cloud TPU のテストは Python 3.10 および jax バージョン 0.4.16 を使用して実行されました。これは私たちの デモ Space で使用された仕様と同じです。
TPU v5e は、TPU v4 に比べて SDXL 上で最大で 2.4 倍のパフォーマンス/ドルを実現し、最新の TPU 世代のコスト効率の良さを示しています。
推論パフォーマンスを測るために、スループットという業界標準のメトリクスを使用します。まず、コンパイルおよび読み込み時の画像ごとのレイテンシを測定します。次に、バッチサイズをチップごとのレイテンシで除算してスループットを計算します。この結果、スループットは、使用されるチップの数に関係なく、モデルが製品環境での実行性能を計測したものです。そして、スループットをリスト価格で除算してドルあたりのパフォーマンスを計算します。
デモはどのように動作しますか?
前述の デモ は、このブログ記事に記載されているコードに基づいて構築されたものです。いくつかの Cloud TPU v5e デバイス (各デバイスには4つのチップがあります) 上で実行され、ユーザーリクエストをバックエンドサーバーにランダムにルーティングするシンプルな負荷分散サーバーがあります。デモでプロンプトを入力すると、リクエストはバックエンドサーバーの1つに割り当てられ、生成された4つの画像を受け取ります。
これは、いくつかの事前割り当てられた TPU インスタンスを基にしたシンプルなソリューションです。将来の投稿では、GKE を使用して負荷に適応する動的なソリューションの作成方法について説明します。
デモのコードはすべてオープンソースであり、Hugging Face Diffusers で提供されています。Diffusers + JAX + Cloud TPUs を使用して何を構築するかを楽しみにしています!
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