【vLLM】「Not enough GPU memory」エラーの解決法:メモリ不足を解消する5つの実践的アプローチ

問題の概要:vLLM起動時のGPUメモリ不足エラー

vLLM(vLLM Inference Engine)は、大規模言語モデル(LLM)の高速推論を実現する人気のライブラリです。しかし、特に大規模なモデル(例:Llama 2 70B, Mixtral 8x7B)をロードしようとすると、以下のようなエラーメッセージで起動に失敗することがあります。

RuntimeError: Not enough GPU memory to allocate 24576 MiB for the model weights.
Please try to reduce the model size or use more GPUs.

あるいは、より具体的なメッセージとして、

CUDA out of memory. Tried to allocate 20.00 GiB (GPU 0; 23.69 GiB total capacity; 1.34 GiB already allocated; 0 bytes free; 1.34 GiB reserved in total by PyTorch)

このエラーは、利用可能なGPUメモリ(VRAM)が、モデルのパラメータや推論に必要な作業領域(KVキャッシュなど)を保持するのに十分でないことを示しています。初心者から中級者のエンジニアが、限られたハードウェアリソースで大規模モデルを動かそうとする際に頻繁に遭遇する課題です。

原因の解説:なぜメモリが足りなくなるのか?

「Not enough GPU memory」エラーの根本原因は、主に以下の3点に集約されます。

1. モデルパラメータのメモリ占有

LLMのパラメータは通常、float16(16ビット浮動小数点数)またはbfloat16形式で保持されます。例えば、70Bパラメータのモデルでは、単純計算で70 * 10^9 * 2バイト = 約140GBのメモリが必要です。ただし、vLLMは高度なメモリ管理(PagedAttention)を行いますが、それでもモデル自体をロードするための最低限のメモリは必須です。

2. KVキャッシュのメモリ要求

LLMの推論では、過去のトークンのKeyとValueのテンソルをキャッシュ(KVキャッシュ)する必要があります。このキャッシュのサイズは、シーケンス長(入力+出力トークン数)とモデルの層数、注意力ヘッド数に比例して増大します。長文生成や大きなコンテキストウィンドウを指定すると、KVキャッシュがメモリ不足の主要因となることがあります。

3. その他のオーバーヘッド

PyTorchやCUDAコンテキスト自体が使用するメモリ、バッチ処理時の一時バッファ、システムプロセスが使用するVRAMなども、利用可能な総メモリを減少させます。特にマルチGPU環境では、通信バッファもメモリを消費します。

解決方法:ステップバイステップでメモリ不足を解消する

ここからは、実際にエラーを解決するための5つの実践的なアプローチを、優先度の高い順に説明します。

解決策1:モデルの量子化(Quantization)を利用する

最も効果的な方法は、モデルの重みを低ビット精度(例:8ビットや4ビット)に変換(量子化)してメモリ使用量を削減することです。vLLMはAWQ(Activation-aware Weight Quantization)やGPTQ(GPT Quantization)形式の量子化モデルをネイティブサポートしています。

手順:

  1. Hugging Face Hubなどから、事前に量子化されたモデル(例:`TheBloke/Llama-2-7B-Chat-AWQ`)を探す。
  2. vLLMの`–quantization`オプションを指定して起動する。
# AWQ量子化モデルの起動例
python -m vllm.entrypoints.openai.api_server 
    --model TheBloke/Llama-2-7B-Chat-AWQ 
    --quantization awq 
    --tensor-parallel-size 1

量子化により、メモリ使用量はほぼ比例して削減されます(例:FP16→4bitで約75%削減)。

解決策2:Tensor ParallelismによるマルチGPU活用

単一のGPUのメモリが足りない場合、複数のGPUにモデルを分割して配置する「テンソル並列処理」が有効です。vLLMでは`–tensor-parallel-size`オプションで簡単に指定できます。

# 2つのGPUを使用してモデルをロードする例
python -m vllm.entrypoints.openai.api_server 
    --model meta-llama/Llama-2-13b-chat-hf 
    --tensor-parallel-size 2

この場合、モデルのパラメータと計算が2つのGPU間で均等に分散され、1GPUあたりのメモリ負荷が大幅に軽減されます。

解決策3:推論パラメータの最適化

推論時の設定を見直すことで、KVキャッシュなどの動的メモリ使用量を削減できます。

  • `–max-model-len`(最大モデル長)の制限: デフォルトはモデルのコンテキスト長ですが、アプリケーションで必要ない場合は小さな値を設定します。
  • `–gpu-memory-utilization`の調整: GPUメモリ使用率の目標値を下げます(デフォルトは0.9)。安全マージンを確保できますが、性能が低下する可能性があります。
# 最大モデル長とメモリ使用率を調整する例
python -m vllm.entrypoints.openai.api_server 
    --model mistralai/Mistral-7B-Instruct-v0.2 
    --max-model-len 2048  # コンテキスト長を2048トークンに制限
    --gpu-memory-utilization 0.85 # 使用率目標を85%に設定

解決策4:バッチサイズとKVキャッシュの設定確認

同時に処理するリクエスト数(バッチサイズ)が多いと、KVキャッシュのメモリ使用量が急増します。vLLMは動的バッチ処理を行いますが、極端に多くのリクエストが同時に来ないようにアプリケーション側で制御するか、`–max-num-batched-tokens`や`–max-num-seqs`で上限を設定します。

# バッチ処理に関するパラメータを設定する例
python -m vllm.entrypoints.openai.api_server 
    --model facebook/opt-6.7b 
    --max-num-seqs 4  # 同時処理シーケンス数を4に制限
    --max-num-batched-tokens 2048 # バッチ内の最大トークン数

解決策5:環境のメモリ状態を確認・クリーンアップする

他のプロセスがGPUメモリを占有していないか確認します。以下のコマンドで状態を確認し、不要なプロセスを終了させます。

# GPUメモリ使用状況の確認(nvidia-smi)
nvidia-smi

# PyTorchからGPUメモリを解放する(スクリプト内で)
import torch
torch.cuda.empty_cache()

また、Dockerコンテナを使用している場合、ホストの全メモリがコンテナにマウントされているか確認します(`–gpus all` オプションなど)。

コード例・コマンド例:総合的な設定

以下は、量子化とテンソル並列を組み合わせ、推論パラメータも最適化した実践的な起動コマンドの例です。限られたリソース(例:2x24GB GPU)で13Bモデルを動かすことを想定しています。

# 総合的なvLLMサーバー起動コマンド例
python -m vllm.entrypoints.openai.api_server 
    --model TheBloke/Llama-2-13B-Chat-AWQ 
    --quantization awq 
    --tensor-parallel-size 2  # 2GPUを使用
    --max-model-len 4096 
    --gpu-memory-utilization 0.88 
    --max-num-seqs 8 
    --served-model-name llama-2-13b-chat 
    --port 8000

起動後、以下のようなcurlコマンドで動作確認ができます。

curl http://localhost:8000/v1/completions 
    -H "Content-Type: application/json" 
    -d '{
        "model": "llama-2-13b-chat",
        "prompt": "日本の首都は",
        "max_tokens": 50,
        "temperature": 0.7
    }'

まとめ・補足情報

vLLMの「Not enough GPU memory」エラーは、大規模LLMを扱う上で避けて通れない課題ですが、本記事で紹介した以下の多角的なアプローチを組み合わせることで、多くの場合解決が可能です。

  1. 量子化:メモリ削減の最も強力な手段。AWQ/GPTQモデルの利用を第一に検討する。
  2. テンソル並列:複数GPUが利用可能な環境では必須の技術。
  3. 推論パラメータの調整:アプリケーション要件に合わせて`max-model-len`などを適切に設定する。
  4. バッチ処理の制限:同時リクエスト数が爆発的に増えないように設計する。
  5. 環境のクリーンアップ:他のプロセスによるメモリ占有を排除する。

補足情報: 最新のvLLMでは、重みのみをCPUメモリにオフロードする機能(`–device cpu`)の実験的サポートもありますが、推論速度が大幅に低下する点に注意が必要です。また、メモリ不足の根本的な解決には、クラウドサービスでより大容量メモリのGPUインスタンス(例:NVIDIA A100 80GB, H100)を利用する選択肢も常に検討に入れておきましょう。ハードウェアの制約とアプリケーションの要求のバランスを取りながら、最適な設定を見つけ出すことが重要です。

この記事は役に立ちましたか?