fal.ai、DSparkとSGLangを用いてIdeogram V4のプロンプト拡張で1000 tok/s・スループット16倍を達成
fal.ai は DSpark と SGLang を活用し、Ideogram V4 のプロンプト拡張機能において Qwen モデルの処理速度を約1000 トークン/秒に向上させ、スループットを16倍改善する技術的突破を成し遂げた。
キーポイント
高スループットの実現と技術基盤
fal.ai は DSpark と SGLang を組み合わせることで、Qwen3.6 モデルを用いたプロンプト拡張処理のスループットを16倍に引き上げ、約1000 トークン/秒の速度を達成した。
モデル選定における世界知識の重要性
パラメータ数の少ない小規模モデル(4B 以下)では生成品質が著しく低下するため、Qwen3.5 397B MoE をベースラインとし、35B MoE モデルが最も高い世界知識と出力品質を両立したことが判明した。
推論ボトルネックの解明
LLM 推論はメモリ帯域幅(memory bandwidth)に強く依存しており、モデルサイズが小さくても低同時実行環境下では速度差が限定的であるという実測結果を示した。
35B MoE モデルの採用と PEFT による微調整
メモリ帯域ボトルネックにより小規模モデルでは速度向上が限定的なため、画像品質を維持しつつ推論速度の最適解である 35B MoE (FP8) を選択し、不安定性を防ぐために MoE 重みを凍結した PEFT で微調整を行いました。
低同時実行時の速度低下と MTP ヘッドによる改善
既存の推論エンジンでは同時実行数が増えるとスループットが急激に低下しましたが、Qwen 3.6 の MTP ヘッドを活用した推測デコーディングにより、平均受容長約 2.5 でスループットを 500 tok/s に向上させました。
画像生成の品質とフォーマットの維持
小規模モデルでは芸術的スタイルや JSON フォーマットの欠陥が見られたため、大規模モデルからスタイルと形式を継承し、Coherency を保つために過度な能力拡張を防ぐ微調整戦略を採用しました。
DFlash モデルの再訓練と暖始動戦略
既存のチェックポイントに重みをロードして暖始動することで、250K サンプルの不足を補い、ブロックサイズ8で約3.9トークンの受容長と700 tok/sのスループットを実現した。
影響分析・編集コメントを表示
影響分析
この記事は、生成 AI アプリケーションにおいて「プロンプト拡張」という前処理工程がボトルネックとなり得ることを明確にし、大規模言語モデルの実用化におけるインフラ最適化の重要性を再認識させる内容です。特に DSpark や SGLang といった最新の推論フレームワークを活用することで、高品質な大規模モデルでも低遅延・高スループットを実現できる道筋を示しており、リアルタイム性が求められる AI アプリケーション開発者にとって重要な指針となります。
編集コメント
画像生成 AI の品質向上にはプロンプトの質が不可欠ですが、その前処理工程の遅延がユーザー体験を損なうケースが多々あります。本記事は、最新の推論技術でこの課題を解決した具体的な事例として、開発者にとって非常に参考になる実装ノウハウを含んでいます。
image @fal では、SGLang 上で DSpark を活用し、ユーザーごとの高いインタラクション性が求められるユースケースにおいて、Qwen3.6 のスループットを 16 倍に向上させることに成功しました。しかし、なぜそれが必要だったのでしょうか?また、どのようにして実現したのでしょうか?
イントロダクション
Ideogram V4 などのテキストから画像への変換モデルは、見事で非常に詳細な画像を生成することができます。ただし、その品質はユーザーが与えるプロンプト(指示文)次第であり、多くのユーザーは詳細なプロンプトを作成しません。もし与えられたプロンプトが十分に詳細でない場合、ユーザーにとって印象的な結果にならない可能性があります。そこで登場するのが「プロンプト・エクスパンダー」と呼ばれる機能です。これはユーザーのプロンプトを強化するために存在します。このプロンプト拡張機能は、LLM(大規模言語モデル)の世界知識を活用し、ユーザーのプロンプトからシーン描写を詳細に行うことで画像モデルに適用します。これにはスタイルに関するヒントや、すべての要素がどこに配置されるかという情報も含まれます。また、この機能により、モデルはすべての要素の配置について非常に正確になるため、ユーザーはシーンのあらゆる部分を慎重に編集できるようになります。なぜなら、モデルはこれらの指示を容易に理解し実行できるからです。
image「飛行機」というプロンプトから生成された画像。左から右へ進むにつれて、プロンプトはより詳細になります。しかし、このプロンプト拡張段階は遅く、特にディストillation(蒸留)モデルが画像を生成する時間と比較すると顕著です。ボトルネックはすぐに画像生成そのものではなく、プロンプトの拡張になってしまいます。私たちは約 600 トークンを生成してプロンプトを拡張し、シーンを詳細に記述しています。API プロバイダーを使用している場合、これには 15 秒から 1 分程度かかることもありますが、画像モデルの実行には数秒しかかかりません。
モデルの選択
まず、小さなモデルを選ぶことは理想的ではありません。プロンプト拡張段階は、モデルの世界知識に敏感だからです。私たちは Qwen 3.5 4B モデルで実験しましたが、その出力は著しく劣っていました。一方、大きなモデルは非常に美しい画像を作成できますが、実行コストが高く、高いスループット(処理能力)を達成するのは困難です。画像モデルとは異なり、大きなモデルを実行するには、VRAM(ビデオメモリ)を大量に必要とする 4 つ、あるいは 8 つの GPU が必要になる可能性があります。
image異なるサイズのモデルによって拡張されたプロンプトから生成された画像。左から右へモデルサイズが小さくなっています。ベースラインとして、大規模モデルの Qwen 3.5 397B MoE(Mixture of Experts)を特定しました。これに対し、0.8B、4B、9B、27B の密集型(Dense)バリアントと、35B の MoE バリアントと比較しました。35B モデルはアクティブパラメータがわずか 3B しかありませんが、それらすべてのモデルを上回る性能を示しました。これは、より広範な世界知識に起因すると考えています。
留意すべき点として、サイズと推論速度の間には線形関係はありません。LLM(大規模言語モデル)の推論は、メモリー帯域幅のボトルネックによって強く支配されています。B200 上で 0.8B モデルをサービス提供しても、9B バージョンに対して 10 倍のスループットが保証されるわけではありません。私たちが発見したのは、すべての推論エンジンにおいて、低同時実行設定ではこれらのモデルがほぼ同様に低速であるという事実です。これは、ユーザーあたりのインタラクション性を最大化するためにサービスを提供しようとしている領域です。この知見から、画像性能を大幅に犠牲することなく推論速度の最適解となる 35B MoE モデル(FP8)を採用する決定に至りました。
より小さなモデルによって生成された画像にはいくつかの問題がありました。大きなモデルが実現できた芸術的なスタイルを継承できていないのです。また、few-shot 例があるにもかかわらず、拡張されたプロンプトの JSON 出力においてフォーマットエラーが発生していました。そこで次に、397B モデルの出力スタイルと形式を 35B バージョンに蒸留(distillation)しました。オンライン蒸留、オフライン蒸留、PEFT(Parameter-Efficient Fine-Tuning: パラメータ効率的微調整)を試しましたが、いずれも同様のモデル結果をもたらすことが分かりました。私たちは PEFT を採用しました。なぜなら、微調整がはるかに容易で高速だったからです。MoE(Mixture of Experts: 専門家の混合)の重みを微調整中に凍結しました。これらにトレーニングを施すと不安定性が増大することが分かったためです。モデルが時として自身の能力を超えた野心的なことを試み、画像の一貫性が損なわれることがあるからです。
より高速に提供するには?
35B モデルが完成した今、課題はどのように効率的に提供するかです。微調整済みであるため他社プロバイダーに頼ることはできません。また、プロンプト拡張には高い双方向性(interactivity)が必要となるため、低同時実行度で最大速度を実現することを目指しました。SGLang と vLLM の両方をテストしましたが、SGLang の FlashInfer バックエンドおよび FlashInfer + TRTLLM MoE カーネルを使用した場合でも、同時実行度=1 で 328 tok/s を達成できるのが精一杯でした。これにより、プロンプト拡張を 2 秒未満で完了させることができました。しかし、同時実行度を 8 に増やすとスループットは 200 tok/s 未満に低下し、16 や 32 の場合では約 100 tok/s に近づき、高い双方向性が求められるユースケースには十分速くありません。
Qwen 3.6 ファミリーには MTP ヘッドが付属しているため、最初の自然な対応策としてこの MTP ヘッドを有効化しました。残念ながら PEFT フレームワークでは MTP ヘッドのファインチューニングが行われず、精度がわずかに低下しましたが、受容長はベースモデルとほぼ同等でした。MTP ヘッドを活用した推測デコーディング(speculative decoding)により、平均受容長が約 2.5 となることで、スループットを 500 tok/s に引き上げることができました。
しかしこれは不十分だったため、DFlash のような他の解決策を探しました。DFlash は拡散ベースの推測デコーディングモデルで、各トークンを並列に予測します。MTP よりわずかに高い受容長を実現でき、低バッチサイズでのサービス時にはドラフトオーバーヘッドが低いという利点があります。しかし、z-lab のチェックポイントを使用した場合、平均受容長約 2.4 でスループットは 468 tok/s に留まりました。このチェックポイントはベースモデル向けに訓練されたものであり、MTP ヘッドと同様に理想的な受容長には届かないと予想していました。次に自然なステップとして、本タスクおよびファインチューニング済みモデルに特化した独自の推測デコーディングモデルを訓練することになりました。
TorchSpec という推論と学習を分離した予測的デコーディングモデルのトレーニング用フレームワークを用いて、DFlash モデルを再トレーニングしました。対象としたのは、当社のディストillation モデルによって再生成された 250K のプロンプト拡張サンプルです。これにより、出力分布との整合性をより良く達成できました。しかし、再トレーニングしたモデルは期待ほどには機能しませんでした:250K のサンプルでは多様性が十分ではなく、非常に高い受容率を得るには至らなかった上、学習に長時間を要しました。そこで私たちは、重みを事前にロードして学習のウォームスタートを行い、その上で 250K のサンプルを用いて追加トレーニングを行う方針に変更しました。このアプローチは非常に成功し、ブロックサイズ 8 で約 3.9 の受容長と 700 tok/s のスループットを達成できました。
imageDSpark アーキテクチャ(出典:DeepSeek チームによる DSpark 論文)。プロンプトエクスパンダーの高速化に取り組んでいる間、DeepSeek チームから DSpark が発表されました。これは拡散ベースのブロック予測器 (DFlash) とマルコフヘッドを組み合わせたもので、後続位置の条件付き受容率を向上させるものです。まるで DFlash ドラフターに小さな MTP 風の自己回帰的リファインメント部分を付加して受容率を高めるような仕組みです。DSpark のトレーニングツールも公開されましたが、すべての隠れ状態をディスク上に保存する必要があり、私たちはそれを避けたいと考えていました。約 38TB の隠れ状態を保存するのは容易ではなく、特にネットワーク接続されたディスクの場合、すぐにボトルネックになる恐れがありました。そこで私たちは TorchSpec で DSpark を実装しました。
DSpark は DFlash をバックボーンとして採用しているため、重みをロードしてマルコフヘッドをスキップすることで、独自の DFlash チェックポイントからトレーニングを開始しました。これにより、モデルは DFlash バックボーンの精度を迅速に回復し、その上にさらに多くの受容性を積み重ねることが可能になりました。また、長いトレーニングループを経ることなく DSpark モデルをトレーニングすることもできました。
次の課題は、DSpark モデルを効率的にサーブすることでした。まず、DFlash が非常に新しいためまだ対応していなかった vLLM と SGLang の両方をパッチ適用して DSpark に対応させました。最終的に、低バッチでのサーブでは SGLang の方が高速であることが判明したため、こちらを採用することにしました。また、1000 tok/s を達成できるかどうかを確認したかったため、Tensor Parallelism(テンソル並列処理)を試しましたが、これは逆に速度低下を引き起こしました。その理由は、マルコフヘッドがランク間でシャード化されていたため集合演算を実行していたからです。これを解決するために重みを複製する方式に変更したところ、TP=4 の環境でサーブ速度を 1000 tok/s を超えることができました。ただし、本番環境では TP=4 は GPU を過剰に非効率に使用しておりコストパフォーマンスが悪いため、TP=1 で 830 tok/s、平均受容長 4.6 を維持することを選択しました。
imageDSpark を使用した場合と使用しない場合の Qwen3.6 の最終推論速度。ユーザーごとの意図された最小限の対話性を維持しつつ、16 倍のスループットを達成し、プロンプト拡張を 2 秒未満で完了させることができました。
最後に、最終的なサービング設定と当初の設定を比較する実験を行いました。まず、最大限の対話性を実現するための単一ユーザー向けサービングでは、プロンプト拡張時間を 1 秒未満に抑えつつ、スループットが 2.6 倍向上しました。一方、より現実的なサービング環境では、ユーザーあたり 300 tok/s を目標とし、その制約条件下で同じハードウェアを使用しながら、単一の B200 グラフィックボード上で総スループットを 16 倍に増強し、かつプロンプト拡張時間を 2 秒未満に維持することができました。
原文を表示
imageAt @fal , we've achieved 16 times higher throughput on Qwen3.6 for a use-case that required high interactivity per user using DSpark on SGLang. But why did we need it? And how did we achieve it?
Intro
Text-to-image models like Ideogram V4 can generate stunning, highly detailed images. However they're only as good as the prompt you give them, and most users don't write detailed prompts. If a given prompt is not detailed enough, it might not look as impressive to the user. That's why a feature called the prompt expander exists to enhance the user's prompt. The prompt expansion applies the LLM's world knowledge to the image model by describing the scene from the user's prompt in detail, with hints about the style and where everything is placed. This feature also makes the model very precise about where everything is placed, which lets users carefully edit any part of the scene, since the model can easily follow those instructions.
imageGenerated image of "an airplane", the prompt gets more detailed left to right.However, this prompt expansion stage is slow, especially compared to the time it takes for distilled models to generate images. The bottleneck might quickly become the prompt expansion rather than the image generation itself. We generate around 600 tokens to expand the prompt and describe the scene in detail. If you are using some API provider, it might take anywhere from 15 seconds up to a minute, whereas your image model only takes a couple of seconds to run.
Model Choice
First of all, choosing a small model is not ideal; the prompt expansion stage is sensitive to the model's world knowledge. We have experimented with Qwen 3.5 4B model, and its output was significantly worse. On the other hand, a big model can create very good looking images; however, running it is more expensive, and getting high throughput is challenging. Unlike image models, a big model might require 4 or even 8 GPUs with significant VRAM to run.
imageGenerated images from a prompt expanded by models of different sizes, model size shrinks left to right.We identified a big model, Qwen 3.5 397B MoE, as our baseline. We compared it to 0.8B, 4B, 9B, and 27B dense variants and a 35B MoE variant. Despite the 35B model having only 3B active parameters, it outperformed them all, which we believe comes from its larger world knowledge.
One thing to note is that size is not linear with serving speed. LLM inference is heavily dominated by the memory bandwidth bottleneck. Serving a 0.8B model on a B200 doesn't guarantee 10x throughput over the 9B version. What we found is that these models are almost equally slow at low concurrency settings across all inference engines, which is where we aim to serve in order to give the most interactivity per user. This led us to the decision to use the 35B MoE model (FP8), which we found to be the sweet spot for inference speed without sacrificing significant image performance.
Images generated by the smaller model had some issues; it did not carry the same artistic style the bigger model was able to achieve. It was also making formatting errors with the JSON output of the expanded prompt, despite the few-shot examples. So next, we distilled our 397B model's output style and format onto our 35B version. We tried online distillation, offline distillation, and PEFT. Distillation took a lot longer and required more data; however, we found that all of them resulted in a similar model. We stuck to PEFT since it was much easier and faster to fine-tune. We froze the MoE weights during fine-tuning, as we found that training them creates more instability: the model sometimes tries to do more ambitious things beyond its capability, breaking the coherency in the image.
How to Serve Faster?
Since we now have our 35B model, the question is: how do we serve it efficiently? We can't rely on other providers since we have fine-tuned it. Also, prompt expansion requires high interactivity, so we aimed to get maximum speed at low concurrency. We tested both SGLang and vLLM, and we were only able to achieve 328 tok/s at concurrency=1 with SGLang's FlashInfer backend and FlashInfer + TRTLLM MoE kernels. This allowed us to get sub-2-second prompt expansion. However, if we increased concurrency to 8, our throughput dropped to <200 tok/s, and at 16 and 32 we were getting near ~100 tok/s, which is not fast enough for a highly interactive use case.
The Qwen 3.6 family comes with an MTP head, so the first natural thing to do was to enable this MTP head. Unfortunately, the PEFT framework did not fine-tune MTP heads, so our accuracy was slightly degraded; however, acceptance length was pretty close to the base model. We were able to increase our throughput to 500 tok/s by enabling speculative decoding using the MTP head with around ~2.5 acceptance length.
This wasn't enough, so we looked for other solutions such as DFlash, a diffusion-based speculative decoding model that predicts each token in parallel. It can get slightly better acceptance than MTP, and drafting overhead is lower for low batch size serving. However, we were only able to reach 468 tok/s with ~2.4 acceptance length using z-lab's checkpoints. This checkpoint was trained for the base model, so we expected lower than ideal acceptance, similar to the MTP head. The next natural step was to train our own speculative decoding model, specifically for this task and for the fine-tuned model.
We retrained a DFlash model using TorchSpec, a framework for training speculative decoding models with inference/training disaggregation, on our 250K prompt expansion samples, regenerated by our distilled model. This helped us match the output distribution better. However, the retrained model fell short: 250K samples were not diverse enough to give very high acceptance, and the training took a long time. So instead, we decided to warm-start the training by pre-loading the weights and just kept training on top of them using our 250K samples. This worked out great: we were able to get ~3.9 acceptance length @ block size 8 and 700 tok/s throughput.
imageDSpark architecture, source: DSpark paper by the DeepSeek team.While we were working on speeding up the prompt expander, DSpark was released by the DeepSeek team. It combines a diffusion-based block predictor (DFlash) with Markovian heads that raise the conditional acceptance rates of later positions. It is almost like a small MTP-like autoregressive refining part attached to the DFlash drafter to improve the acceptance rate. The trainer for DSpark was released, but it required all hidden states to be stored on disk, something we did not want to deal with. Storing ~38TB of hidden states wasn't easy, especially if the disk is networked, where it might quickly become the bottleneck. So we implemented DSpark in TorchSpec.
Since DSpark uses DFlash as the backbone, we warm-started our training from our own DFlash checkpoint by loading its weights and skipping the Markov heads. This allowed the model to quickly recover the accuracy of the DFlash backbone and compound more acceptance on top of it. It also let us train a DSpark model without going through a long training loop.
The next challenge was serving the DSpark model efficiently. First, we patched both vLLM and SGLang to support DSpark, since neither did yet given how recent it was. We eventually found that SGLang was faster for low batch serving, so we settled on it. We also wanted to know whether we could reach 1000 tok/s, so we tried Tensor Parallelism, but it caused a slowdown: the Markovian heads were doing collective operations because they were sharded across the ranks. We fixed it by replicating the weights instead, which finally pushed our serving speed past 1000 tok/s at TP=4. That said, we decided to keep TP=1 at 830 tok/s with 4.6 acceptance length in production, since TP=4 was heavily underutilizing the GPUs and wasn't cost-effective.
imageFinal inference speed of Qwen3.6 with and without DSpark. We were able to get 16x more throughput at the intended minimum per user interactivity, expand prompts under 2 seconds.Finally, we ran experiments comparing our final serving config to where we started. First, at single-user serving for maximum interactivity, we got 2.6x throughput with under 1 second prompt expansion. But for a realistic serving setting, we targeted 300 tok/s per user, and under that constraint, on the same hardware, we were able to increase our total throughput by 16 times on a single B200 while keeping prompt expansion under 2 seconds.
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み