Text-generation-webuiを用いたマルチGPU環境での複数AIモデル同時実行

Text-generation-webui×マルチGPU:ローダー別挙動解析と並列推論の最適解

約18分で読めます
文字サイズ:
Text-generation-webui×マルチGPU:ローダー別挙動解析と並列推論の最適解
目次

なぜ「マルチGPU×複数モデル」が現場で求められるのか

多くのAIプロジェクトにおいて、最近特に「オンプレミスでのマルチGPU環境構築」という課題が浮上しています。

クラウドAPIの手軽さは魅力的ですが、機密データの取り扱いや、トークン課金の青天井化、そして何よりレイテンシの制御という観点から、自社でGPUサーバーを導入するケースが急増しています。具体的には、最新のNVIDIA RTX 50シリーズ(第5世代Tensorコアを搭載したRTX 5090など)や、より大規模なメモリを搭載したRTX 6000 Ada、データセンター向けのL40Sなどを複数枚搭載したワークステーションの導入が進んでいます。なお、これまでコストパフォーマンスに優れていたRTX 4090は、RTX 50シリーズの登場に伴い市場から姿を消しつつあり、現在では大幅に帯域幅が向上したGDDR7メモリを備えるRTX 50シリーズへの移行が一般的な選択肢となっています。

しかし、ハードウェアを最新のものに揃えただけでプロジェクトが成功するわけではありません。実務の現場で直面するのが、「GPUを増やしたのに、思ったほど速くならない」「VRAMは余っているのにOOM(Out Of Memory)エラーが出る」という壁です。特に最新のアーキテクチャでは、NVLinkの対応状況やPCIeレーンの帯域制限がボトルネックになるケースも報告されています。

本記事では、生成AIの検証・運用ツールとしてデファクトスタンダードになりつつある「Text-generation-webui(通称Oobabooga)」を題材に、マルチGPU環境におけるモデルローダーの内部挙動と、リソース最適化の勘所を紐解きます。まずは動くプロトタイプを作り、そこから本質的な課題を見極めていきましょう。

単一GPU運用の物理的限界点

なぜマルチGPUが必要なのか。最大の理由は依然としてVRAM容量の壁です。

現在のオープンソースLLMのトレンドは、性能とサイズのバランスが良い70B(700億パラメータ)クラスのモデルや、MixtralのようなMoE(Mixture of Experts)モデル、さらにはそれ以上の大規模モデルへとシフトしています。これらを実用的な量子化精度(4bit以上)で動かすには、最低でも32GB〜48GB以上のVRAMが必要です。

最新のGPUアーキテクチャでは、NVFP4などの新しいデータ形式によるメモリ効率化技術が登場しています。さらに、RTX 5090では32GBのGDDR7メモリが搭載され、AI性能自体も従来比で飛躍的に向上しています。それでも、コンシューマー向けハイエンドカード単体では、大規模モデルのロードと長文の推論を同時にこなすのは困難です。また、単一の超高性能なデータセンター向けカードを導入するよりも、複数のカードを組み合わせるアプローチが、コストと性能のバランスを取るための現実解となるケースが増えています。経営的視点からも、この投資対効果は見逃せません。

加えて、Context Window(文脈長)の拡大もメモリを圧迫します。RAG(検索拡張生成)システムにおいて、数万トークンのドキュメントをプロンプトに含める場合、KV Cache(Key-Value Cache)が消費するメモリ量は指数関数的に増大します。単一GPUでは、モデル本体は何とか載っても、推論を始めた瞬間にメモリ不足に陥るという事態が珍しくありません。

推論と評価の並列化による開発サイクル短縮

開発現場におけるもう一つのニーズは、「比較検証の並列化」です。

AIエージェント開発や高速プロトタイピングのアプローチでは、プロンプトエンジニアリングやファインチューニングの効果を測定するために、ベースモデルとチューニング済みモデル、あるいは異なるパラメータ設定のモデルを同時に走らせて、同一の評価セットでテストする必要があります。

これを直列処理で行っていては、開発サイクルが著しく遅くなります。マルチGPU環境があれば、GPU 0-1でモデルAを、GPU 2-3でモデルBを同時に稼働させ、A/Bテストを高速に回すことが可能になります。これは単なる時短ではなく、開発者の思考を中断させないための重要なシステム投資と言えます。仮説を即座に形にして検証するスピード感こそが、ビジネスへの最短距離を描く鍵となります。

APIサーバーとしての可用性とスループット向上

Text-generation-webuiは、OpenAI互換のAPIサーバーとしても機能します。業務システムやチャットボットのバックエンドとして利用する場合、単一のリクエストを高速に処理する「低レイテンシ」だけでなく、複数のリクエストを同時にさばく「スループット」も求められます。

マルチGPUを活用し、適切に負荷分散(ロードバランシング)やバッチ処理を行うことで、限られたハードウェアリソースから最大のパフォーマンスを引き出すことができます。しかし、これには各ローダーがどのようにGPUを使っているかを正しく理解する必要があります。

ここからは、ブラックボックスになりがちな「マルチGPU処理の中身」と、その最適な活用アプローチを明らかにします。準備はよろしいでしょうか?

マルチGPU処理の技術的解剖:Tensor並列とPipeline並列

設定画面のパラメータをいじる前に、まずアーキテクチャレベルでの理解を深めておく必要があります。LLMを複数のGPUで動かす際、大きく分けて2つのアプローチが存在します。

  1. Pipeline Parallelism (PP) / Layer Splitting
  2. Tensor Parallelism (TP)

Text-generation-webuiで扱う多くのローダーは、実は前者のアプローチ、あるいはその変形を採用しています。

モデル分割の戦略:レイヤー分割とテンソル分割

Pipeline Parallelism(レイヤー分割)は、モデルの層(レイヤー)をGPUごとに分割して配置する方法です。例えば、全32層のモデルがある場合、第1〜16層をGPU0に、第17〜32層をGPU1に配置します。

  • 処理の流れ: 入力データ → GPU0で計算 → 結果をGPU1へ転送 → GPU1で計算 → 出力
  • メリット: 実装が比較的容易で、異なる性能のGPUを混在させやすい(VRAM容量に合わせて層の数を調整できる)。
  • デメリット: 「GPU1が計算している間、GPU0は遊んでいる(アイドル状態)」という時間が発生しやすく、計算リソースの利用効率が落ちる。また、GPU間のデータ転送が直列的に発生するため、レイテンシが増加する傾向がある。

一方、Tensor Parallelism(テンソル分割)は、1つの層(巨大な行列演算)自体を分割し、複数のGPUで同時に計算して結果を統合する方法です。

  • 処理の流れ: 全GPUが協力して1つの層を計算 → 通信して同期 → 次の層へ
  • メリット: 計算を並列化できるため、巨大なモデルでも単一GPUに近い速度(あるいはそれ以上)が出せる可能性がある。
  • デメリット: GPU間で頻繁かつ大量の通信(All-Reduce通信など)が発生するため、NVLinkのような超高速インターコネクトがないと、通信待ちがボトルネックになり逆に遅くなる。

Text-generation-webuiの主要なローダー(AutoGPTQやllama.cppなど)における「GPU Split」機能は、基本的にレイヤー単位での分割(Pipeline Parallelismに近い挙動)を行います。これが、「GPUを2枚にしたのに2倍速くならないどころか、少し遅くなった」という現象の正体です。

GPU間通信(NVLink vs PCIe)のボトルネック

ここで無視できないのが、GPUをつなぐ「道」の太さです。

データセンター向けのH100やA100サーバーでは、NVLinkやNVSwitchによってGPU間が数百GB/sで接続されています。しかし、一般的なオンプレミスのワークステーションや自作サーバーでは、PCIe(PCI Express)経由での通信が主となります。

  • PCIe 4.0 x16: 最大 64 GB/s
  • PCIe 4.0 x8: 最大 32 GB/s
  • PCIe 3.0 x1: (ライザーケーブル等) 約 1 GB/s

レイヤー分割方式であっても、GPU0からGPU1へ中間データ(Hidden States)を受け渡す必要があります。このデータ量はモデルの次元数やバッチサイズに依存しますが、PCIeの帯域が狭いと、ここで詰まります。

特に注意すべきは、マザーボードの仕様です。「x16スロットが3本ある」と書いてあっても、全スロット同時に使うと帯域が分割され、実質x8やx4で動作するケースが多々あります。nvidia-smi topo -m コマンドなどで、現在のトポロジーとリンク速度を確認することは、トラブルシューティングの第一歩です。

VRAM割り当てのメカニズム

もう一つ、多くのエンジニアが誤解しているのがVRAMの使用量です。

「モデルサイズ ÷ GPU数」できれいに割れるわけではありません。最初のGPU(GPU0)には、モデルの重みだけでなく、入力データの埋め込み(Embedding)や、最終的な出力層の計算、さらにはWebUI自体のオーバーヘッドが乗ってきます。

また、推論中にはKV Cacheと呼ばれる、過去のトークンの計算結果を保持する領域が動的に確保されます。マルチGPU構成の場合、このキャッシュも各GPUに分散して配置されるため、計算を担当するレイヤー数に応じてキャッシュ消費量も変わります。

したがって、gpu-memory設定で限界ギリギリまで詰め込むと、コンテキストが長くなった瞬間にOOMが発生します。一般的な傾向として、各GPUに10〜20%程度の余力を残す設計が、安定運用の鉄則とされています。

Text-generation-webuiにおける主要バックエンドのマルチGPU挙動比較

Text-generation-webuiにおける主要バックエンドのマルチGPU挙動比較 - Section Image

Text-generation-webuiの強みは、多様なバックエンド(ローダー)を切り替えられる点にあります。しかし、マルチGPU環境ではローダーごとの挙動差が顕著に現れます。ここでは、代表的な3つのローダーについて、その特性と最適解を解説します。

ExLlamaV2:最速推論のためのメモリ管理と分割ロジック

現在、NVIDIA GPU環境において最も高速な推論エンジンと言えるのがExLlamaV2です。これはExLlamaの後継で、極限までチューニングされたカーネルを使用しています。

  • マルチGPU挙動:
    ExLlamaV2は非常に効率的なパイプライン処理を行います。特筆すべきは、gpu-split(VRAM割り当て設定)に対する忠実さと、スクラッチバッファの扱いです。ExLlamaV2は推論開始時に必要な一時メモリ(temp state)を事前に確保します。

  • 最適化のポイント:
    WebUIの「Model」タブでExLlamaV2を選択する際、gpu-split欄にカンマ区切りでVRAM容量(例: 20,24)を指定できます。ここで重要なのは、GPU0の割り当てを少し減らすことです。先述の通り、GPU0はシステムオーバーヘッドやWebUIの表示処理などを担うことが多いため、GPU1以降よりも1〜2GB程度少なく申告することで、OOMを回避しつつパフォーマンスを最大化できます。

  • 注意点:
    ExLlamaV2はEXL2形式という独自の量子化フォーマットを使用します。GGUFやGPTQとは互換性がないため、専用のモデルを用意する必要がありますが、その手間をかける価値は十分にあります。

llama.cpp (GGUF):CPUオフロードとのハイブリッド戦略

llama.cppは、元々Apple Silicon向けに開発されましたが、現在はCUDAもサポートしており、CPUとGPUのハイブリッド推論が可能です。モデル形式はGGUFを使用します。

  • マルチGPU挙動:
    llama.cppの最大の特徴は、「VRAMに乗り切らない分をメインメモリ(CPU)に溢れさせることができる」点です。n-gpu-layersパラメータですべてのレイヤーをGPUに載せれば高速ですが、VRAMが足りない場合でも、一部をCPU処理に回すことで(速度は落ちますが)動作は可能です。

  • Row Split vs Layer Split:
    最近のllama.cppのアップデートでは、マルチGPU時の分割戦略として、従来のレイヤー分割に加え、一部でRow Split(行分割=Tensor Parallelism的な挙動)のサポートも進んでいます。起動オプションで--split-modeを指定できる場合がありますが、WebUI上ではデフォルトでレイヤー分割が優先されることが多いです。レイヤー分割の場合、GPU間のデータ移動が発生するため、PCIe帯域の影響を強く受けます。

Transformers / AutoGPTQ:互換性と設定の柔軟性

Hugging FaceのTransformersライブラリやAutoGPTQは、最も標準的で互換性が高いローダーです。

  • マルチGPU挙動:
    これらは内部的にHugging FaceのAccelerateライブラリを使用しており、device_map="auto"という設定で自動的にモデルを分割配置します。

  • 課題:
    Accelerateによる自動分割は「とにかくメモリに載せる」ことを優先するため、必ずしも計算効率が良い配置になりません。場合によっては、GPU0 → GPU1 → GPU0 といった無駄なデータの往復(ピンポン)が発生し、推論速度が劇的に低下することがあります。

  • 対策:
    WebUIの設定でno_inject_fused_attentionなどのオプションを調整するか、あるいはパフォーマンスを追求するならExLlamaV2への移行を検討すべきです。Transformersローダーは、最新のモデルアーキテクチャやマイナーなモデルを試す際の「互換性重視」の選択肢と捉えるのが良いでしょう。

実践:複数モデル同時ロードとAPIエンドポイントの分離

実践:複数モデル同時ロードとAPIエンドポイントの分離 - Section Image 3

さて、ここからはより実践的なシナリオです。「7Bのチャットモデル」と「7Bの要約モデル」を1台のサーバー(例えばGPU4枚構成)で同時に動かしたい場合、どうすればよいでしょうか?

Text-generation-webuiのGUI上では、一度にロードできるモデルは1つだけです。タブを切り替えても、前のモデルはアンロードされます。ここでシステム思考が必要になります。

1つのWebUIインスタンス vs 複数インスタンス起動

解決策はシンプルです。WebUIのプロセス自体を複数立ち上げるのです。

Dockerや仮想環境(conda/venv)を活用し、ポート番号と参照するGPUを変えて起動します。

構成例(GPU 4枚搭載サーバーの場合):

  1. インスタンスA(チャット用):

    • 使用GPU: GPU 0, 1
    • ポート: 7860 (GUI), 5000 (API)
    • 起動コマンド(イメージ):
      CUDA_VISIBLE_DEVICES=0,1 python server.py --model chat-model-70b --api --listen-port 7860 --api-port 5000
      
  2. インスタンスB(要約・分析用):

    • 使用GPU: GPU 2, 3
    • ポート: 7861 (GUI), 5001 (API)
    • 起動コマンド(イメージ):
      CUDA_VISIBLE_DEVICES=2,3 python server.py --model summary-model-70b --api --listen-port 7861 --api-port 5001
      

リソース競合を避けるためのGPU割り当て戦略(CUDA_VISIBLE_DEVICES)

ここで最も重要なのが、環境変数 CUDA_VISIBLE_DEVICES です。この変数を設定することで、各プロセスに対して「使用可能なGPUはこれだけだ」とOSレベルで認識させることができます。

これを設定せずにWebUIの設定画面だけでGPU指定を行おうとすると、ライブラリによっては全GPUを認識してしまい、VRAMの初期化時に競合エラーを起こしたり、意図しないGPUにメモリを確保してしまうことがあります。

プロセスレベルで完全に分離することで、片方のモデルがエラーで落ちても、もう片方は影響を受けずに稼働し続けることができます。これは本番運用を見据えた可用性の観点からも推奨されるアーキテクチャです。

API起動時の引数設定とポート管理

複数起動する場合、以下のポートが重複しないように注意が必要です。

  • --listen-port: WebUIの操作画面用ポート(デフォルト7860)
  • --api-port: OpenAI互換API等のブロッキングAPI用ポート(デフォルト5000)
  • --api-blocking-port: ストリーミング用ポート(デフォルト5005付近、WebUIのバージョンにより異なる)

これらを startup_scriptdocker-compose.yml で管理することで、サーバー再起動時にも自動的に複数のモデルが立ち上がる環境を構築できます。

パフォーマンス最適化とトラブルシューティング

最後に、マルチGPU環境構築時によくあるトラブルとその解決策、さらにパフォーマンスを絞り出すための最適化テクニックを紹介します。

「Out of Memory」の真犯人:KV Cacheとフラグメンテーション

「モデルサイズは40GB、VRAMは48GBあるのにOOMで落ちる」。この原因の多くは KV Cacheメモリフラグメンテーション です。

  • KV Cacheの肥大化:
    コンテキスト長(n_ctx)を大きく設定しすぎると、KV CacheがVRAMを大量に消費します。特にfloat16精度の場合、コンテキスト長4096とか8192程度なら問題ありませんが、32kや128kといった長大なコンテキストを扱う場合、キャッシュだけで数十GBを消費します。

    • 対策: ローダーの設定で cache_8bit(8ビットキャッシュ)や cache_4bit を有効にする。これにより、精度への影響を最小限に抑えつつ、キャッシュメモリ使用量を1/2〜1/4に削減できます。
  • メモリフラグメンテーション:
    PyTorchなどのフレームワークは、メモリの確保・解放を繰り返すうちに、連続した空き領域が不足することがあります。これを防ぐには、環境変数 PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 などを設定し、メモリ割り当ての挙動を調整する方法がありますが、まずはモデルロード時に余計なアプリ(ブラウザなど)を閉じてVRAMをクリーンに保つことが基本です。

推論速度低下の原因特定と解消法

推論速度(tokens/sec)が出ない場合、ボトルネックがどこにあるかを切り分けます。

  1. GPU使用率を見る: watch -n 1 nvidia-smi で推論中のGPU使用率を監視します。

    • GPU使用率が低い(0〜30%)まま推論が進む → CPUボトルネック(前処理が遅い)か、通信ボトルネック(PCIe転送待ち)の可能性大。
    • 特定のGPUだけ100%張り付き、他が0% → モデル分割のバランスが悪いgpu-splitを見直す。
  2. PCIeリンク速度の確認:
    BIOS設定でPCIeがGen3やx4に制限されていないか確認してください。また、ライザーケーブルを使用している場合、品質の悪いケーブルはノイズにより速度低下や転送エラーを引き起こします。

Linux環境におけるGPUドライバとCUDAバージョンの落とし穴

Text-generation-webuiは頻繁にアップデートされますが、ベースとなるPyTorchやCUDAのバージョンとの不整合がトラブルの元凶になりがちです。

特にマルチGPU環境では、NVIDIAドライバのバージョンが古いと、GPU間のP2P(Peer-to-Peer)通信が正しく機能しないことがあります。nvidia-smi で表示されるドライババージョンと、インストールされているCUDA Toolkitのバージョンが互換性を持っているか、定期的にチェックしましょう。

また、NCCL_P2P_DISABLE=1 という環境変数が「おまじない」として紹介されることがありますが、これはP2P通信を無効化してCPU経由で通信させる設定です。デバッグ時には有用ですが、パフォーマンスは低下するため、恒久的な対策としては推奨しません。

総括:オンプレミスLLM基盤構築への示唆

総括:オンプレミスLLM基盤構築への示唆 - Section Image

ここまで、Text-generation-webuiを用いたマルチGPU環境の構築について、技術的な深掘りをしてきました。

WebUIは、その名の通り「Webインターフェース」として認識されがちですが、その本質は「最新の推論技術を即座に試せる実験場」です。ExLlamaV2やGGUFといった最先端のローダー技術は、商用プロダクトよりも早くWebUIに取り込まれます。

コスト対効果の最大化ポイント

オンプレミスでLLM基盤を構築する際、やみくもに最高級のGPU(H100など)を買う必要はありません。本記事で解説したように、ローダーの特性を理解し、中古のRTX 3090や4090を複数枚組み合わせ、適切なソフトウェア設定(量子化、キャッシュ最適化、並列化設定)を行うことで、数分の一のコストで実用的な推論速度を実現できます。経営者視点とエンジニア視点の双方から見ても、このアプローチは非常に合理的です。

将来的なスケーラビリティの確保

Text-generation-webuiで得た知見(モデルの挙動、メモリ消費感覚、プロンプトへの反応)は、将来的にvLLMやTensorRT-LLMといった、よりプロダクション向けの推論サーバーへ移行する際にも必ず役立ちます。まずはWebUIで小さく始め、PoCを回し、確信が得られた段階でシステムをスケールさせていく。

これが、リスクを抑えつつ最速で価値を出す、AIエージェント開発や業務システム設計における成功パターンです。まずは動くものを作り、技術の本質を見極めていきましょう。

Text-generation-webui×マルチGPU:ローダー別挙動解析と並列推論の最適解 - Conclusion Image

コメント

コメントは1週間で消えます
コメントを読み込み中...