【vLLM】「Not enough GPU memory」エラーの解決法:モデル分割、量子化、メモリ最適化の実践ガイド

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

vLLMは、大規模言語モデル(LLM)の推論を高速化するためのライブラリですが、特に大規模なモデルをロードする際に、以下のようなGPUメモリ不足エラーが頻繁に発生します。

RuntimeError: Not enough GPU memory to allocate the model. 
Requested: 45.00 GB, Available: 23.99 GB.
Try to reduce the model size or use tensor parallelism.

このエラーは、選択したLLMモデル(例:Llama-2-70B, Mixtral-8x7Bなど)をロードするために必要なGPUメモリ量が、システムの利用可能なGPUメモリ(上記例では約24GB)を超えていることを示しています。vLLMをpython -m vllm.entrypoints.api_serverllm = LLM(model="meta-llama/Llama-2-70b-chat-hf")のようなコマンド/コードで起動しようとした直後に発生し、サービス開始を阻みます。

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

このエラーの根本原因は、主に以下の3点に集約されます。

1. モデルパラメータの巨大さ

最新のLLMはパラメータ数が膨大です。例えば、FP16(半精度浮動小数点)形式の70Bパラメータモデルでは、パラメータだけでも約140GB(70B * 2バイト)のメモリが必要です。推論時のキーバリューキャッシュやアクティベーションなどのオーバーヘッドを加えると、さらに多くのメモリを消費します。

2. デフォルト設定の限界

vLLMはデフォルトで可能な限り高速な推論を実現するため、大きなキーバリューキャッシュを確保しようとします。また、デフォルトではモデル全体を単一GPUにロードしようとするため、マルチGPU環境でもこのエラーが発生することがあります。

3. 量子化されていないモデルの使用

Hugging Face Hubなどから取得したオリジナルのモデル重みは、多くの場合FP16またはBF16形式です。これらは高精度ですが、非常にメモリ効率が悪いという欠点があります。

解決方法:ステップバイステップ実践ガイド

GPUメモリ不足を解決するためのアプローチは、主に「モデルサイズの圧縮」「利用リソースの拡大」「メモリ使用量の最適化」の3つです。以下、具体的な手順を説明します。

ステップ1: モデルの量子化(最も効果的な手法)

モデル重みの精度を下げる(量子化する)ことで、メモリ使用量と計算コストを大幅に削減できます。vLLMはAWQ(Activation-aware Weight Quantization)やGPTQ形式の量子化モデルをネイティブサポートしています。

解決策A: 既に量子化されたモデルを使用する

Hugging Face Hubでは、コミュニティによって量子化されたモデルが多数公開されています。

# AWQ量子化モデルの使用例(例:Llama-2-7B)
from vllm import LLM
llm = LLM(model="TheBloke/Llama-2-7B-Chat-AWQ", quantization="awq")

# GPTQ量子化モデルの使用例
llm = LLM(model="TheBloke/Llama-2-7B-Chat-GPTQ", quantization="gptq")

解決策B: 自分でモデルを量子化する

オリジナルのモデルを量子化したい場合は、AutoAWQやAutoGPTQなどのツールを使用します。

# AutoAWQを使用した量子化の例(別スクリプトで実行)
# pip install autoawq
from awq import AutoAWQForCausalLM
model_path = "meta-llama/Llama-2-7b-hf"
quant_path = "./llama-2-7b-awq"
quant_config = { "zero_point": True, "q_group_size": 128, "w_bit": 4 }
model = AutoAWQForCausalLM.from_pretrained(model_path)
model.quantize(quant_config)
model.save_quantized(quant_path)

ステップ2: Tensor ParallelismによるマルチGPU活用

単一GPUのメモリでは足りなくても、複数GPUのメモリを束ねればモデルをロードできる場合があります。vLLMはTensor Parallelismをサポートしており、モデルを複数GPUに分割してロードします。

# Tensor Parallelismを利用したLLMの初期化(2GPU使用)
from vllm import LLM
llm = LLM(model="meta-llama/Llama-2-70b-chat-hf",
          tensor_parallel_size=2) # 利用するGPU数を指定

# APIサーバー起動時にも指定可能
# コマンド例:
# python -m vllm.entrypoints.api_server 
#   --model meta-llama/Llama-2-70b-chat-hf 
#   --tensor-parallel-size 2

tensor_parallel_sizeは利用可能なGPU数以下に設定する必要があります。NVIDIAのnvidia-smiコマンドでGPU数を確認できます。

ステップ3: 推論パラメータの最適化

vLLMの推論設定を調整することで、メモリ使用量を削減できます。特にキーバリューキャッシュのメモリ割り当ては大きな影響を与えます。

from vllm import LLM, SamplingParams

# メモリ割り当てを制限するLLMの初期化
llm = LLM(model="meta-llama/Llama-2-13b-chat-hf",
          gpu_memory_utilization=0.85,  # GPUメモリ使用率の上限を85%に設定
          max_num_seqs=10,               # 同時処理するシーケンス数を制限
          max_model_len=4096)           # サポートする最大コンテキスト長を制限

# サンプリングパラメータも最小限に
sampling_params = SamplingParams(temperature=0.8, top_p=0.95, max_tokens=512)

gpu_memory_utilizationは0.0から1.0の間で設定します。低くしすぎるとパフォーマンスが低下するため、0.8〜0.9がバランスの良い値です。

ステップ4: より小さいモデルへの切り替え

どうしてもメモリ制約が厳しい場合は、パラメータ数の少ないモデルを選択するのが現実的です。7Bや13Bパラメータのモデルは、単一のコンシューマー向けGPU(RTX 4090 24GB等)でも量子化せずに動作可能な場合があります。

# より小さいモデルを選択する例
llm = LLM(model="meta-llama/Llama-2-7b-chat-hf")  # 70Bではなく7Bモデル
# または
llm = LLM(model="mistralai/Mistral-7B-Instruct-v0.2")

コード例・コマンド例:総合的な解決策の実装

実際のプロジェクトでは、上記の解決策を組み合わせて使用します。以下は、限られたGPUリソース(例:2x RTX 4090 24GB)で70Bモデルを動かすための実践的な例です。

# 組み合わせ例:量子化 + Tensor Parallelism + メモリ最適化
from vllm import LLM, SamplingParams

# 1. AWQ量子化された70Bモデルを選択
# 2. 2つのGPUでTensor Parallelismを適用
# 3. メモリ使用率を制限
llm = LLM(model="TheBloke/Llama-2-70B-Chat-AWQ",
          quantization="awq",
          tensor_parallel_size=2,
          gpu_memory_utilization=0.9,
          max_model_len=8192)  # 長いコンテキストが必要なければ2048などに下げる

# 推論の実行
sampling_params = SamplingParams(temperature=0.7, top_p=0.9, max_tokens=256)
prompts = ["日本の首都はどこですか?", "機械学習について説明してください。"]
outputs = llm.generate(prompts, sampling_params)

for output in outputs:
    print(f"Prompt: {output.prompt}")
    print(f"Generated text: {output.outputs[0].text}n")

APIサーバーとして起動する場合のコマンド例:

# 量子化モデル + マルチGPU + メモリ制限を全て適用した起動コマンド
python -m vllm.entrypoints.api_server 
  --model TheBloke/Llama-2-70B-Chat-AWQ 
  --quantization awq 
  --tensor-parallel-size 2 
  --gpu-memory-utilization 0.9 
  --max-model-len 4096 
  --port 8000

まとめ・補足情報

vLLMの「Not enough GPU memory」エラーは、大規模LLMを扱う上で避けて通れない課題ですが、適切な対処法を講じることで解決可能です。最も効果が高いのはモデルの量子化(AWQ/GPTQ)であり、4ビット量子化ではメモリ使用量を約75%削減できます。マルチGPU環境が利用可能であれば、Tensor Parallelismを活用することで、より大きなモデルをロードできるようになります。

その他の重要なポイントとして:

  • GPUドライバとCUDAのバージョン確認: nvidia-smiでGPUメモリを正確に確認し、CUDAとvLLMの互換性を確保してください。
  • vLLMのバージョンアップ: 新しいバージョンではメモリ最適化が進んでいるため、pip install -U vllmでアップデートを検討してください。
  • スワップメモリの影響: システムのRAMが不足していると、GPUメモリとは別にスワップが発生し、パフォーマンスが劇的に低下する可能性があります。htopfree -hでシステムメモリの空き容量を監視しましょう。
  • プロセス競合の確認: 他のプロセスがGPUメモリを占有していないか、nvidia-smiで確認し、不要なプロセスを終了させてください。

最終的には、ご自身のハードウェア制約(GPUメモリ量、GPU数)と、求める推論性能(速度、精度)のバランスを取りながら、最適な設定を見つけていくことが重要です。小さなモデルから始めて、段階的に設定を調整していくことをお勧めします。

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