背景とLLM推論最適化の必要性
Llama、Mixtral、GPTなどの大規模言語モデル(LLM)は広く普及しました。これらは仮想アシスタント、コンテンツ作成、データ分析など幅広い分野で応用され、多くの可能性を切り開いています。しかし、特にパフォーマンスの面で、LLMを製品環境(production)に導入することは容易ではありません。
LLM推論を実装する際、最大の課題はレイテンシ(latency)とスループット(throughput)の2つです。ユーザーに良い体験を提供するためには、モデルが迅速に応答する必要があります。また、システムは同時に多くのリクエストを処理できなければならず、インフラコストの最適化にも貢献します。さらに、LLMは非常に多くのVRAMを消費することが多く、一般的なGPUで複数のモデルを実行したり、大規模なバッチ処理を行うことを困難にしています。
以前、私もプロダクション環境でのLLM推論の最適化に苦労しました。特に大規模モデルや大量のリクエストを扱う場合です。レイテンシを削減し、同時処理能力を高め、GPUリソースを効率的に活用する方法を見つけることは難しい課題でした。静的バッチ処理やカーネル最適化といった従来の解決策では、問題の一部しか解決できませんでした。
様々な調査と実験の結果、vLLMは最終的に優れた効果を証明しました。vLLMは、PagedAttentionアルゴリズムによってLLM推論の速度を向上させるために作成された、非常に効率的なライブラリです。従来の連続的なメモリ割り当てを行うアテンションメカニズムとは異なり、PagedAttentionはK/Vキャッシュをページ単位で管理します。
この方法は、オペレーティングシステムが仮想メモリを管理する方法に似ています。これにより、vLLMは非効率なメモリ割り当てを大幅に削減し、より高いスループットと驚くべきVRAM節約を実現します。プロダクション環境に導入したところ、非常に安定した結果が得られました。これにより、インフラコストが削減され、ユーザーエクスペリエンスが大幅に向上しました。
では、vLLMには他のソリューションと比較してどのような利点があるのでしょうか?
- スループットの向上:PagedAttentionにより、vLLMはプロンプトと出力の長さが異なる場合でも、同時に多くのリクエストを処理できます。
- レイテンシの低減:連続バッチ処理技術により、バッチが満たされるのを待つことなく、すぐにリクエストを処理できます。
- VRAMの節約:K/Vキャッシュの効率的な管理により、GPUメモリを最大限に活用できます。
- 使いやすさ:Hugging Faceの多くの人気モデルをサポートし、使いやすいAPIを提供しています。
この記事では、Linux上でvLLMを導入した経験を共有します。あなたのプロジェクトでもLLM推論のパフォーマンスを最適化できることを願っています。
Linux環境でのvLLMのインストール
vLLMを始めるには、NVIDIA GPUとCUDAドライバーがインストールされたLinuxシステムが必要です。この記事では、すでにCUDAツールキットがインストールされていることを前提としています。まだの場合は、NVIDIAの公式サイトの指示に従ってインストールしてください。
1. Python環境の準備
ライブラリの競合を避けるために、Python仮想環境を使用することをお勧めします。
# 仮想環境の作成
python3 -m venv vllm_env
# 環境のアクティベート
source vllm_env/bin/activate
2. vLLMのインストール
vLLMはpipを使って簡単にインストールできます。主なオプションは2つあります:PyPI(安定版)から、またはソース(最新版ですが、完全に安定していない場合があります)からです。
PyPIからのインストール(推奨)
pip install vllm
最新機能を使用したい場合やカスタマイズの必要がある場合は、ソースからインストールすることもできます。しかし、ほとんどのケースではPyPI版で十分です。
ソースからのインストール(オプション)
git clone https://github.com/vllm-project/vllm.git
cd vllm
pip install -e .
インストールが完了したら、簡単なコマンドを実行してvLLMが準備できているかを確認できます。
import vllm
print(vllm.__version__)
エラーが発生せずvLLMのバージョンが表示されれば、インストールは成功です!
詳細な設定とvLLMサーバーの実行
インストール後、vLLMをAPIサーバーとして起動する必要があります。これにより、他のアプリケーションから簡単に推論を呼び出すことができます。vLLMはこのための便利なインターフェースを提供しています。
1. 基本的なAPIサーバーの起動
いくつかの基本的なパラメータでvLLMサーバーを起動できます。例えば、自分のGPUでLlama 2 7Bモデルを実行する場合:
python -m vllm.entrypoints.api_server \
--model meta-llama/Llama-2-7b-hf \
--port 8000 --host 0.0.0.0
パラメータの説明:
--model meta-llama/Llama-2-7b-hf: Hugging Face Hubからのモデル名を指定します。vLLMは、このモデルがまだない場合は自動的にダウンロードします。--port 8000と--host 0.0.0.0: APIサーバーがリッスンするアドレスとポートを設定します。
私はこの項目を注意深く監視し、モデルがどれくらいのVRAMを占有し、使用中のGPUに適しているかを確認しています。
2. 重要な設定パラメータ
さらに最適化するために、vLLMはあなたが理解しておくべき多くのパラメータを提供しています。
--tensor-parallel-size (または -tp)
このパラメータは、複数のGPUを持っている場合に非常に役に立ちます。モデルをそれらの間で分割することで、速度を向上させたり、1つのGPUに収まらない大規模モデルを実行したりできます。例えば、2つのGPUを持っている場合:
python -m vllm.entrypoints.api_server \
--model meta-llama/Llama-2-13b-hf \
--tensor-parallel-size 2 \
--port 8000 --host 0.0.0.0
この場合、Llama 2 13Bモデルは2つのGPUに均等に分割され、各GPUが計算の一部を処理します。これにより、特に超大規模モデルでの推論速度が大幅に向上します。
--gpu-memory-utilization
このパラメータは、vLLMが使用を許可されるVRAMの量を制御するために非常に重要です。デフォルトは0.9(90%)です。他のタスクのためにVRAMを確保する必要がある場合や、同じGPUで複数のアプリケーションを実行する場合は、この値を減らすことができます。例えば、VRAMの80%のみを使用する場合:
python -m vllm.entrypoints.api_server \
--model mistralai/Mistral-7B-Instruct-v0.2 \
--gpu-memory-utilization 0.8 \
--port 8000 --host 0.0.0.0
私は通常、テスト中の実際のVRAM監視結果に基づいてこのパラメータを調整します。低すぎると、vLLMのバッチ処理能力に影響を与え、スループットが低下する可能性があるため、設定しすぎないでください。
--dtype
これはモデルのデータ型(例: float16、bfloat16、float32)です。float16またはbfloat16を使用すると、VRAMを節約し、計算速度を向上させることができます。通常、精度に大きな影響はありません。
python -m vllm.entrypoints.api_server \
--model mistralai/Mistral-7B-Instruct-v0.2 \
--dtype bfloat16 \
--port 8000 --host 0.0.0.0
--max-model-len
このパラメータは、モデルが処理できるシーケンス(プロンプト+出力)の最大長を定義します。もしリクエストが特定の長さを超えないことがわかっている場合、このパラメータを設定することでvLLMのメモリ最適化を向上させることができます。ただし、短く設定しすぎると、長い応答が切り詰められてしまうため注意してください。
量子化(Quantization)の設定
VRAMを最大限に節約するためには、AWQやGPTQなどの量子化モデルを使用できます。vLLMはこれらのモデルを直接サポートしています。
# AWQモデルの例
python -m vllm.entrypoints.api_server \
--model TheBloke/Mistral-7B-Instruct-v0.2-AWQ \
--quantization awq \
--port 8000 --host 0.0.0.0
量子化モデルを使用すると、同じGPU上で、あるいはVRAMが少ないGPU上でも、はるかに大きなモデルを実行できます。私個人としては、RTX 3090カードでLlama 2 70B AWQを正常に実行したことがあります。これは印象的な結果です。
3. クライアントからのAPI呼び出し
vLLMサーバーが起動したら、推論リクエストを送信できます。このAPIはOpenAI APIと互換性があり、大きな利便性を提供します。
cURLの使用
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-2-7b-hf",
"prompt": "プログラミングを学ぶことの利点について短い文章を書いてください。",
"max_tokens": 100,
"temperature": 0.7
}'
Pythonクライアントの使用
Pythonでは、openaiライブラリまたはhttpxライブラリを使用して簡単に操作できます。
import openai
client = openai.OpenAI(
api_key="EMPTY", # vLLMはAPIキーを要求しません
base_url="http://localhost:8000/v1"
)
response = client.completions.create(
model="mistralai/Mistral-7B-Instruct-v0.2",
prompt="探偵猫についての短い物語を書いてください。",
max_tokens=150,
temperature=0.8,
)
print(response.choices[0].text)
モデルがチャットモデルの場合、/v1/chat/completionsエンドポイントを使用することもできます。
import openai
client = openai.OpenAI(
api_key="EMPTY",
base_url="http://localhost:8000/v1"
)
response = client.chat.completions.create(
model="mistralai/Mistral-7B-Instruct-v0.2",
messages=[
{"role": "system", "content": "あなたは役立つAIアシスタントです。"},
{"role": "user", "content": "探偵猫についての短い物語を書いてください。"}
],
max_tokens=150,
temperature=0.8,
)
print(response.choices[0].message.content)
標準のOpenAI APIを使用することで、OpenAIからvLLMへのアプリケーション移行が、多くのコード変更をせずに容易になります。
パフォーマンスの検証とモニタリング
vLLMサーバーが安定して稼働している場合、パフォーマンスの検証と監視は不可欠なステップです。これにより、システムが期待通りに動作していることを確認できます。
1. スループットとレイテンシの検証
vLLMには統合されたベンチマークスクリプトが用意されています。このスクリプトを使用すると、スループットとレイテンシを簡単に測定できます。これは、最適化の前後のパフォーマンスを比較する効果的な方法です。
python -m vllm.benchmarks.benchmark_throughput \
--model meta-llama/Llama-2-7b-hf \
--dataset sharegpt \
--num-prompts 1000
パラメータ--dataset sharegptは、実際のプロンプトデータセットを使用して負荷をシミュレートします。--num-promptsを変更して、異なるリクエスト数でテストできます。結果には、Prompt throughput (tokens/s)、Generation throughput (tokens/s)、およびLatencyなどの指標が表示されます。
さらに、複数のリクエストを同時に送信し、平均応答時間を測定するために独自のPythonスクリプトを作成することもできます。または、ApacheBench(ab)やLocustなどのストレステストツールを使用してください。
2. VRAMとシステムリソースの監視
vLLMサーバーが稼働してリクエストを処理している間、GPUのVRAM使用量を定期的に監視する必要があります。nvidia-smiコマンドがこれに適しています。
nvidia-smi
合計VRAM、使用済みVRAM、およびVRAMを占有しているプロセスに関する情報が表示されます。vLLMがどれくらいのVRAMを使用しているか、設定した--gpu-memory-utilizationの制限を超えていないかに注意してください。VRAMが過負荷になっている場合は、--gpu-memory-utilizationを減らすか、より小さなモデルに切り替えるか、量子化モデルを使用する必要があるかもしれません。
システムのCPUとRAMも忘れずに監視してください。vLLMはGPUを最適化しますが、一部の前処理または後処理タスクにはCPUが必要です。モデルをロードするためにも、一定量のRAMが必要です。
3. 監視結果に基づいた最適化
- スループットが低い場合:
--gpu-memory-utilizationを再確認します。低すぎると、vLLMが多くのリクエストをバッチ処理できない可能性があります。- 十分なVRAMがある場合は、
--max-num-seqs(同時シーケンスの最大数)を増やすことを検討します。 --dtype float16またはbfloat16を使用していることを確認します。- 複数のGPUがある場合は、
--tensor-parallel-sizeを増やすことを試します。
- VRAMが満杯の場合:
--gpu-memory-utilizationを減らします。- 量子化モデル(AWQ、GPTQ)を使用します。
- より小さなモデルに切り替えます。
- マルチGPUを実行している場合は、
--tensor-parallel-sizeが正しく設定されていることを確認します。
- レイテンシが高い場合:
- サーバーの負荷を確認します。リクエストが多すぎると、サーバーが過負荷になっている可能性があります。
- プロンプトと出力の長さを最適化します。
これは継続的なループであると認識しています:設定 → 検証 → 監視 → 微調整。すべてのケースに「完璧な」設定というものはありません。それはモデル、GPUの種類、および予測される負荷に大きく依存します。
結論
効率的なLLM推論の実装は、現代のAIアプリケーションの成功を決定する要素です。vLLMは、PagedAttentionと連続バッチ処理のおかげで、高いスループットとVRAMの節約を実現するための効果的なソリューションであることを証明しました。
実際の経験からのこれらの共有を通じて、Linux上でのvLLMのインストール、設定、および最適化方法についてより深く理解していただけたことを願っています。システムの最適な設定を見つけるために、さまざまなパラメータを試すことを躊躇しないでください。成功を祈ります!
