この記事の対象読者
- 「vLLM」という名前を見かけて「LLMと何が違うの?」と思った方
- OllamaでローカルLLMを使い始めたが、もっと高速に大量のリクエストを捌きたい方
- AIアプリケーションの本番デプロイを検討しているエンジニアの方
この記事で得られること
- vLLMが「何者」なのか完全に理解できる: LLMとvLLMの関係を、比喩を交えてスッキリ整理
- PagedAttentionの仕組みが腹落ちする: vLLMの核心技術を、OSの仮想メモリに例えてわかりやすく解説
- Ollamaとの使い分けが判断できる: 各ツールの立ち位置を性能データ付きで比較し、自分に最適な選択肢がわかる
この記事で扱わないこと
- vLLMの内部カーネル実装(CUDAレベル)の詳細
- TensorRT-LLMやSGLangとの詳細ベンチマーク比較
- モデルの学習・ファインチューニング方法
1. vLLMとの出会い
「vLLM? LLMのtypoか何か? vがついてるだけで何が変わるの?」
最初に名前を見たとき、正直そう思った。LLM(Large Language Model)は知っている。ChatGPTやLlamaのことだろう。でも「vLLM」って...新しいモデルの名前?
調べてみると全然違った。vLLMは「モデルそのもの」ではなく、モデルを「高速に動かすためのエンジン」だった。
例えるなら、LLMが「F1マシンのエンジン」だとすると、vLLMは「そのエンジンを最高の効率で回すためのECU(エンジン制御ユニット)」。エンジン自体は同じでも、制御の仕方で出力が全然変わる。そういう話だ。
この記事では、vLLMがなぜ生まれ、何を解決し、自分のプロジェクトにどう活かせるのかを徹底的に掘り下げていく。
ここまでで、vLLMが「モデル」ではなく「エンジン」だということがイメージできたと思う。次は、この記事で使う用語を整理しておこう。
2. 前提知識の確認
本題に入る前に、この記事で登場する用語を確認する。
2.1 推論(Inference)とは
学習済みのAIモデルが、入力に対して予測や回答を生成する処理のこと。ChatGPTにプロンプトを送って回答を得るとき、裏側で行われているのがこの「推論」だ。料理に例えれば、レシピを覚えた(学習した)シェフが、お客さんの注文に応じて料理を作る(推論する)ようなもの。
2.2 KVキャッシュ(Key-Value Cache)とは
LLMがテキストを生成する際、過去のトークン(単語の断片)の計算結果を保存しておくメモリ領域のこと。これがあるおかげで、毎回すべてを再計算しなくて済む。「会話の文脈を覚えておくためのメモ帳」のようなものだ。
このKVキャッシュがGPUのVRAMを大量に消費するのが、LLM推論の最大のボトルネックになっている。
2.3 スループット(Throughput)とは
単位時間あたりに処理できるリクエスト数やトークン数のこと。レストランで言えば「1時間に何皿の料理を提供できるか」に相当する。vLLMはこのスループットを劇的に向上させることを目的としている。
2.4 連続バッチ処理(Continuous Batching)とは
複数のリクエストを同時にまとめて処理する技術。従来のバッチ処理は「全員の注文が揃ってから一斉に調理開始」だったが、連続バッチ処理は「席が空いたら次のお客さんをどんどん案内する」方式。GPUの遊び時間を最小化できる。
これらの用語が押さえられたら、vLLMが生まれた背景を見ていこう。
3. vLLMが生まれた背景
3.1 UC Berkeleyから生まれた研究プロジェクト
vLLMは2023年、カリフォルニア大学バークレー校(UC Berkeley)のSky Computing Labで生まれた。正式名称は「Virtual Large Language Model」。ここで言う"Virtual"は「仮想メモリ」の"Virtual"から来ている(この意味は後述する)。
開発を主導したWoosuk Kwon氏は2025年にvLLMをテーマとした博士論文を発表。現在はPyTorch Foundationのホストプロジェクトとなり、GitHubスターは5万以上、コントリビューターは500人以上に達している。
出典: vLLM GitHub Repository / UC Berkeley EECS - vLLM Dissertation
3.2 LLM推論が抱えていた「メモリの壁」
vLLMが解決しようとした課題は明確だ。GPUメモリの60〜80%が無駄になっていた。
なぜか? 従来のLLM推論システムには3つのメモリ浪費パターンがあった。
| 浪費パターン | 説明 | 例え |
|---|---|---|
| 内部フラグメンテーション | リクエストの最大長分のメモリを事前確保するが、実際にはその20〜30%しか使わない | 10人分の席を予約したのに3人しか来ない |
| 外部フラグメンテーション | 確保・解放を繰り返すうちにメモリが「虫食い状態」になり、連続した空きが取れない | スイスチーズのように穴だらけ |
| 重複キャッシュ | 同じプロンプトを含むリクエストでも、KVキャッシュを個別に保持 | 同じ教科書をクラス全員が1冊ずつ買う |
従来のシステムでは、KVキャッシュを「大きな連続メモリブロック」として確保していた。リクエストの最大長(例: 2048トークン)分のメモリを丸ごと確保するため、短いリクエストでも巨大なメモリが占有される。
出典: Efficient Memory Management for Large Language Model Serving with PagedAttention (arXiv:2309.06180)
3.3 「OSの仮想メモリ」というヒント
Kwon氏のチームはこの問題を見て、コンピュータサイエンスの古典的な解決策を思い出した。それが仮想メモリとページングだ。
OSがメモリ管理で抱えていた問題と、LLM推論のKVキャッシュ管理の問題は驚くほど似ていた。
| OSのメモリ管理 | LLMのKVキャッシュ管理 |
|---|---|
| プロセスごとに連続メモリを確保 → フラグメンテーション | リクエストごとに連続KVキャッシュを確保 → フラグメンテーション |
| 解決策: ページングで小さなブロックに分割 | 解決策: PagedAttentionで小さなブロックに分割 |
| 仮想アドレス → 物理アドレスのマッピング | 論理ブロック → 物理ブロックのマッピング |
この発想から生まれたのがPagedAttention、そしてそれを実装したvLLMだ。
背景がわかったところで、PagedAttentionの基本的な仕組みを見ていこう。
4. 基本概念と仕組み
4.1 PagedAttention:vLLMの核心技術
PagedAttentionの考え方はシンプルだ。
「KVキャッシュを小さな固定サイズのブロックに分割し、メモリ上のどこにでも配置できるようにする」
従来のシステムとvLLMの違いを図で示す。
【従来のシステム】
リクエストA: [████████████████░░░░░░░░░░░░░░░░] ← 実際は30%しか使ってない
リクエストB: [████████░░░░░░░░░░░░░░░░░░░░░░░░] ← もっと無駄
リクエストC: [確保不可能 - 連続空きメモリ不足] ← 全体では空きがあるのに入れない
【vLLM(PagedAttention)】
リクエストA: [■][■][■] ← 必要な分だけブロックを確保、飛び飛びでOK
リクエストB: [■][■] ← 必要な分だけ
リクエストC: [■][■][■] ← 空きブロックがあれば入れる
(各ブロックはメモリ上の好きな場所に配置可能)
具体的な動作フローはこうだ。
- リクエストが来ると、最初のトークン群(プロンプト)のKVキャッシュをブロック単位で確保する(1ブロック = 16トークン分)
- トークン生成が進むにつれて、新しいブロックをオンデマンドで追加確保する
- ブロックの論理的な順序と物理的な配置はブロックテーブルで管理する
- リクエストが完了したらブロックを解放し、別のリクエストに再利用する
この仕組みにより、メモリの無駄はブロック内の未使用部分(最大15トークン分)だけになる。従来の60〜80%の浪費から、4%未満にまで削減された。
4.2 連続バッチ処理(Continuous Batching)
PagedAttentionと並んでvLLMの性能を支えるのが連続バッチ処理だ。
【従来のバッチ処理(Static Batching)】
時刻T1: [リクエストA(生成中)] [リクエストB(生成中)] [リクエストC(生成中)]
時刻T2: [リクエストA(完了)] [リクエストB(生成中)] [リクエストC(生成中)]
時刻T3: [---- 待機 ----] [リクエストB(完了)] [リクエストC(生成中)]
↑ Aが完了してもバッチ全体が終わるまで次を入れられない
【連続バッチ処理(Continuous Batching)】
時刻T1: [リクエストA(生成中)] [リクエストB(生成中)] [リクエストC(生成中)]
時刻T2: [リクエストA(完了)] [リクエストB(生成中)] [リクエストC(生成中)]
→ Aの枠にリクエストDを即座に投入!
時刻T3: [リクエストD(生成中)] [リクエストB(完了)] [リクエストC(生成中)]
→ Bの枠にリクエストEを即座に投入!
レストランに例えると、従来のバッチ処理は「テーブルの全員が食べ終わるまで次のグループを案内しない」運営。連続バッチ処理は「席が空いた瞬間に次のお客さんを案内する」効率的な運営だ。
4.3 vLLMの全体アーキテクチャ
vLLMは大きく2つのレイヤーで構成されている。
| レイヤー | 役割 | 技術 |
|---|---|---|
| サービングレイヤー | HTTPリクエストの受付、OpenAI互換API提供 | FastAPI、非同期処理 |
| 推論エンジン | 実際のモデル実行、メモリ管理 | PagedAttention、連続バッチ処理、CUDAカーネル |
そしてvLLMが対応する主要機能は以下の通りだ。
- テンソル並列・パイプライン並列・データ並列: 複数GPUでの分散推論
- 投機的デコーディング(Speculative Decoding): 小さなモデルで先読みして高速化
- プレフィックスキャッシュ: 同じシステムプロンプトを共有するリクエスト間でKVキャッシュを再利用
- 量子化サポート: INT8/INT4/FP8/GPTQ/AWQ等で省メモリ化
- 100以上のモデルアーキテクチャ対応: Llama、Mistral、Qwen、Gemma、マルチモーダルモデル等
基本概念が理解できたところで、実際にvLLMを動かしてみよう。
5. 実践:実際に使ってみよう
5.1 環境構築
# Python 3.10以上が必要(3.12+推奨)
python --version
# uvでのインストール(公式推奨・高速)
pip install uv
uv pip install vllm
# または通常のpipでインストール
pip install vllm
注意: vLLMはGPUが必須。NVIDIA GPU + CUDAドライバが正しくセットアップされていることが前提。
5.2 環境別の設定ファイル
以下の3種類の設定を用意した。用途に応じて選択してほしい。
開発環境用(config.development.yaml)
# config.development.yaml - 開発環境(ローカルテスト用)
server:
host: "127.0.0.1"
port: 8000
model:
name: "meta-llama/Llama-3.2-3B-Instruct" # 小さめのモデル
max_model_len: 4096
gpu_memory_utilization: 0.85 # GPUメモリの85%を使用
dtype: "auto"
engine:
# 開発時はデバッグしやすい設定
enforce_eager: true # CUDAグラフを無効化(デバッグ用)
max_num_seqs: 8 # 同時処理数を控えめに
logging:
level: "DEBUG"
ステージング環境用(config.staging.yaml)
# config.staging.yaml - ステージング環境(チーム内テスト用)
server:
host: "0.0.0.0"
port: 8000
model:
name: "meta-llama/Llama-3.1-8B-Instruct" # 中規模モデル
max_model_len: 8192
gpu_memory_utilization: 0.90
dtype: "auto"
quantization: "awq" # 量子化で省メモリ
engine:
enforce_eager: false
max_num_seqs: 64 # ある程度の同時処理に対応
enable_prefix_caching: true # プレフィックスキャッシュ有効
logging:
level: "INFO"
本番環境用(config.production.yaml)
# config.production.yaml - 本番環境(高スループット構成)
server:
host: "0.0.0.0"
port: 8000
model:
name: "meta-llama/Llama-3.1-70B-Instruct" # 大規模モデル
max_model_len: 16384
gpu_memory_utilization: 0.95
dtype: "auto"
tensor_parallel_size: 4 # 4GPU並列
engine:
enforce_eager: false
max_num_seqs: 256 # 大量同時処理
enable_prefix_caching: true
# 投機的デコーディング(オプション)
# speculative_model: "meta-llama/Llama-3.2-1B-Instruct"
# num_speculative_tokens: 5
logging:
level: "WARNING"
5.3 基本的な使い方
方法1: OpenAI互換APIサーバーとして起動(最も一般的)
# サーバー起動(開発環境)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.2-3B-Instruct \
--host 127.0.0.1 \
--port 8000
"""
vLLM OpenAI互換APIクライアント
実行方法: python vllm_client.py
前提: vLLMサーバーがlocalhost:8000で起動済み
"""
from openai import OpenAI
def main():
"""vLLMサーバーにリクエストを送信"""
# OpenAIクライアントをvLLMに接続
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="dummy" # vLLMはデフォルトで認証不要
)
# チャット形式でリクエスト
response = client.chat.completions.create(
model="meta-llama/Llama-3.2-3B-Instruct",
messages=[
{"role": "system", "content": "あなたは親切なアシスタントです。"},
{"role": "user", "content": "Pythonでフィボナッチ数列を計算する関数を書いてください"}
],
max_tokens=512,
temperature=0.7
)
print(response.choices[0].message.content)
if __name__ == "__main__":
main()
方法2: Pythonライブラリとしてオフライン利用
"""
vLLMオフライン推論
実行方法: python vllm_offline.py
"""
from vllm import LLM, SamplingParams
def main():
"""オフラインバッチ推論"""
# モデルのロード
llm = LLM(
model="meta-llama/Llama-3.2-3B-Instruct",
gpu_memory_utilization=0.85
)
# サンプリングパラメータ
params = SamplingParams(
temperature=0.7,
max_tokens=256,
top_p=0.9
)
# 複数プロンプトを一括処理(バッチ推論)
prompts = [
"Pythonの特徴を3つ挙げてください",
"機械学習とディープラーニングの違いは?",
"Dockerの利点を教えてください",
]
outputs = llm.generate(prompts, params)
for output in outputs:
prompt = output.prompt
generated = output.outputs[0].text
print(f"プロンプト: {prompt[:30]}...")
print(f"回答: {generated[:100]}...")
print("-" * 50)
if __name__ == "__main__":
main()
方法3: Docker起動(推奨)
# Dockerでの起動(GPUパススルー)
docker run --gpus all \
-p 8000:8000 \
vllm/vllm-openai:latest \
--model meta-llama/Llama-3.2-3B-Instruct \
--host 0.0.0.0 \
--port 8000
5.4 実行結果
上記のクライアントコードを実行すると、以下のような出力が得られる。
$ python vllm_client.py
はい、フィボナッチ数列を計算する関数をいくつかの方法で実装します。
**方法1: 再帰(シンプルだが非効率)**
```python
def fibonacci_recursive(n):
if n <= 1:
return n
return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)
方法2: メモ化再帰(効率的)
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci_memo(n):
if n <= 1:
return n
return fibonacci_memo(n-1) + fibonacci_memo(n-2)
...
### 5.5 よくあるエラーと対処法
| エラー | 原因 | 対処法 |
|-------|------|--------|
| `torch.cuda.OutOfMemoryError` | モデルが[VRAM](https://qiita.com/GeneLab_999/items/fd49cfa1bfd30d4a1b8d)に収まらない | `--gpu-memory-utilization 0.8` を下げる or 量子化(`--quantization awq`)を使う |
| `ValueError: The model's max seq len (X) is larger than the maximum number of tokens` | `max_model_len`がモデル対応範囲外 | `--max-model-len` を小さい値に設定 |
| `RuntimeError: CUDA error: no kernel image is available` | [CUDA](https://qiita.com/GeneLab_999/items/dfedb349f4971a1c7786)バージョンとGPUアーキテクチャの不整合 | vLLMが対応するCUDAバージョンを確認、`pip install vllm --upgrade` を実行 |
| `Connection refused` on `localhost:8000` | サーバーが起動完了していない | 起動ログで `Uvicorn running on ...` が表示されるまで待つ(大きなモデルは数分かかる) |
| `Model not found` | Hugging Faceのアクセストークン未設定(ゲートモデルの場合) | `huggingface-cli login` でトークンを設定 |
### 5.6 環境診断スクリプト
問題が発生した場合は、以下のスクリプトで環境を診断できる。
```python
#!/usr/bin/env python3
"""
vLLM環境診断スクリプト
実行方法: python check_vllm_env.py
"""
import sys
import subprocess
def check_section(title: str):
"""セクションヘッダーを表示"""
print(f"\n{'='*55}")
print(f" {title}")
print(f"{'='*55}")
def check_python():
"""Pythonバージョン確認"""
version = sys.version_info
print(f" Python: {version.major}.{version.minor}.{version.micro}")
if version < (3, 10):
return ["CRITICAL: Python 3.10以上が必要です"]
if version >= (3, 12):
print(" [OK] Python 3.12+(推奨バージョン)")
return []
def check_cuda():
"""CUDA環境確認"""
issues = []
try:
import torch
print(f" PyTorch: {torch.__version__}")
if torch.cuda.is_available():
print(f" CUDA利用可能: Yes")
print(f" CUDAバージョン: {torch.version.cuda}")
print(f" GPU数: {torch.cuda.device_count()}")
for i in range(torch.cuda.device_count()):
name = torch.cuda.get_device_name(i)
mem = torch.cuda.get_device_properties(i).total_mem
mem_gb = mem / (1024**3)
print(f" GPU {i}: {name} ({mem_gb:.1f} GB)")
else:
issues.append(
"CRITICAL: CUDAが利用できません。"
"NVIDIAドライバとCUDAツールキットを確認してください。"
)
except ImportError:
issues.append("CRITICAL: PyTorchがインストールされていません")
return issues
def check_vllm():
"""vLLMインストール確認"""
issues = []
try:
import vllm
print(f" vLLM: {vllm.__version__}")
except ImportError:
issues.append(
"WARNING: vLLMがインストールされていません。"
"`pip install vllm` を実行してください。"
)
return issues
def check_vram_estimate():
"""VRAMとモデルサイズの目安を表示"""
try:
import torch
if not torch.cuda.is_available():
return []
total_vram = sum(
torch.cuda.get_device_properties(i).total_mem
for i in range(torch.cuda.device_count())
)
total_gb = total_vram / (1024**3)
print(f"\n 合計VRAM: {total_gb:.1f} GB")
print(f" 推奨モデルサイズ目安:")
if total_gb >= 80:
print(f" 70Bモデル(FP16): OK")
if total_gb >= 48:
print(f" 70Bモデル(AWQ 4bit): OK")
if total_gb >= 24:
print(f" 8Bモデル(FP16): OK")
print(f" 70Bモデル(AWQ 4bit): 複数GPU推奨")
if total_gb >= 12:
print(f" 3Bモデル(FP16): OK")
print(f" 8Bモデル(AWQ 4bit): OK")
if total_gb < 12:
print(f" 3Bモデル(AWQ 4bit): OK")
print(f" WARNING: VRAMが少ないため量子化モデルを推奨")
except Exception:
pass
return []
def main():
"""メイン診断処理"""
print("=" * 55)
print(" vLLM環境診断ツール v1.0")
print("=" * 55)
all_issues = []
check_section("1. Pythonバージョン")
all_issues.extend(check_python())
check_section("2. CUDA / GPU環境")
all_issues.extend(check_cuda())
check_section("3. vLLMインストール状態")
all_issues.extend(check_vllm())
check_section("4. VRAMとモデルサイズ目安")
all_issues.extend(check_vram_estimate())
# 結果サマリー
check_section("診断結果")
if all_issues:
for issue in all_issues:
prefix = "[!]" if "CRITICAL" in issue else "[?]"
print(f" {prefix} {issue}")
else:
print(" [OK] vLLMの実行環境は正常です")
return 1 if any("CRITICAL" in i for i in all_issues) else 0
if __name__ == "__main__":
sys.exit(main())
5.7 Docker設定(本番推奨)
# docker-compose.yml - vLLM本番デプロイ構成
version: '3.8'
services:
vllm:
image: vllm/vllm-openai:latest
ports:
- "127.0.0.1:8000:8000"
volumes:
- model_cache:/root/.cache/huggingface
environment:
- HUGGING_FACE_HUB_TOKEN=${HF_TOKEN}
command: >
--model meta-llama/Llama-3.1-8B-Instruct
--host 0.0.0.0
--port 8000
--gpu-memory-utilization 0.90
--max-model-len 8192
--enable-prefix-caching
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
volumes:
model_cache:
実装方法がわかったので、次は具体的なユースケースを見ていく。
6. ユースケース別ガイド
6.1 ユースケース1: Ollamaからの移行(個人→チーム利用)
想定読者: Ollamaで個人利用していたが、チームで共有するためにスループットが必要になった方
なぜvLLMなのか: Ollamaはシングルユーザー向けに最適化されており、同時リクエスト処理が苦手。vLLMはPagedAttentionと連続バッチ処理により、同時10ユーザーでもスループットが5倍以上になる。
| 指標 | Ollama (8B, GPU) | vLLM (8B, GPU) |
|---|---|---|
| 単一ユーザー速度 | 約65 tokens/sec | 約140 tokens/sec |
| 10同時ユーザー合計 | 約150 tokens/sec | 約800 tokens/sec |
| セットアップ難易度 | 極めて簡単 | やや複雑 |
出典: Clore.ai - LLM Serving: Ollama vs vLLM vs TGI / Build with Matija - LLM Inference Engine Showdown
移行手順(APIの書き換えだけでOK):
"""
OllamaからvLLMへの移行例
どちらもOpenAI互換APIなので、base_urlを変えるだけ
"""
from openai import OpenAI
def create_client(backend: str = "vllm") -> OpenAI:
"""バックエンドに応じたクライアント生成"""
configs = {
"ollama": {
"base_url": "http://localhost:11434/v1",
"api_key": "ollama",
"model": "llama3.2",
},
"vllm": {
"base_url": "http://localhost:8000/v1",
"api_key": "dummy",
"model": "meta-llama/Llama-3.2-3B-Instruct",
},
}
config = configs[backend]
client = OpenAI(
base_url=config["base_url"],
api_key=config["api_key"]
)
return client, config["model"]
# 使い方(APIコードは共通)
client, model = create_client("vllm") # "ollama" に変えるだけで切替可能
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)
6.2 ユースケース2: RAGアプリケーションのバックエンド
想定読者: 社内ドキュメント検索 + LLM回答生成のRAGシステムを構築している方
なぜvLLMなのか: RAGでは複数ユーザーの検索クエリが同時に飛んでくる。さらに、システムプロンプトが共通なのでプレフィックスキャッシュが効果的。
推奨構成: vLLM + プレフィックスキャッシュ有効
# プレフィックスキャッシュを有効にして起動
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--enable-prefix-caching \
--max-num-seqs 64 \
--port 8000
"""
RAGバックエンドでのvLLM活用例
実行方法: python rag_with_vllm.py
前提: vLLMサーバーがlocalhost:8000で起動済み
"""
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
# 全リクエスト共通のシステムプロンプト(プレフィックスキャッシュが効く)
SYSTEM_PROMPT = """あなたは社内ドキュメント検索アシスタントです。
以下のルールに従って回答してください:
1. 提供されたコンテキストのみに基づいて回答する
2. コンテキストに情報がない場合は「該当する情報が見つかりません」と回答する
3. 回答には必ず参照元ドキュメント名を記載する"""
def answer_with_context(question: str, context: str) -> str:
"""検索結果コンテキストを使って回答生成"""
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"コンテキスト:\n{context}\n\n質問: {question}"}
],
max_tokens=512,
temperature=0.3 # RAGでは低めの温度が推奨
)
return response.choices[0].message.content
# 使用例
context = "【社内規定.pdf】有給休暇は年間20日付与され、翌年への繰越は最大10日まで可能です。"
answer = answer_with_context("有給休暇の繰越上限は?", context)
print(answer)
6.3 ユースケース3: 大規模モデルのマルチGPU推論
想定読者: 70Bクラスの大規模モデルを複数GPUで動かしたい方
なぜvLLMなのか: テンソル並列(Tensor Parallelism)を使えば、1枚のGPUに収まらないモデルも複数GPUに分散してロードできる。Ollamaのレイヤーオフローディングと異なり、全GPUが並列に計算するため高速。
推奨構成: 70Bモデル + 4xGPU テンソル並列
# 70Bモデルを4GPU並列で起動
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.95 \
--max-model-len 16384 \
--port 8000
"""
マルチGPU環境でのvLLMバッチ推論
実行方法: python multi_gpu_batch.py
"""
from vllm import LLM, SamplingParams
def main():
"""マルチGPUバッチ推論"""
# 4GPU並列でモデルロード
llm = LLM(
model="meta-llama/Llama-3.1-70B-Instruct",
tensor_parallel_size=4,
gpu_memory_utilization=0.95,
)
params = SamplingParams(temperature=0.7, max_tokens=1024)
# 大量のプロンプトを一括処理
prompts = [
f"トピック{i}について詳しく解説してください"
for i in range(100) # 100件を一括処理
]
outputs = llm.generate(prompts, params)
print(f"処理完了: {len(outputs)}件")
for i, output in enumerate(outputs[:3]): # 先頭3件だけ表示
print(f"\n--- プロンプト {i+1} ---")
print(output.outputs[0].text[:200])
if __name__ == "__main__":
main()
ユースケースを把握できたところで、この先の学習パスを確認しよう。
7. 学習ロードマップ
この記事を読んだ後、次のステップとして以下をおすすめする。
初級者向け(まずはここから)
- vLLM公式ドキュメント - Quickstartで実際にサーバーを立ててみる
- Ollamaと同じモデルを動かして速度差を体感する
- OpenAI互換APIでの呼び出しに慣れる
中級者向け(実践に進む)
- 量子化モデル(AWQ/GPTQ)を試してVRAM効率を最適化する
- プレフィックスキャッシュを使ったRAGシステムを構築する
- PagedAttention論文(arXiv:2309.06180)を読んで内部メカニズムを理解する
上級者向け(さらに深く)
- テンソル並列を使ったマルチGPUデプロイメントを構築する
- Aleksa GordicのvLLM解析記事でソースコードレベルの理解を深める
- llm-dプロジェクトでKubernetes環境でのスケーリングを検討する
8. まとめ
この記事では、vLLMについて以下を解説した。
- vLLMはLLMではなく推論エンジン: モデルを高速に動かすための基盤ソフトウェア
- PagedAttentionが核心技術: OSの仮想メモリの発想をKVキャッシュ管理に応用し、メモリ浪費を4%未満に削減
- Ollamaとの使い分け: 個人利用はOllama、チーム/本番利用はvLLMという棲み分け
各ツールの立ち位置まとめ
| ツール | 主な用途 | 同時処理 | セットアップ |
|---|---|---|---|
| Ollama | 個人利用・プロトタイプ | 苦手 | 5分 |
| vLLM | 本番デプロイ・高スループット | 得意(PagedAttention) | 30分〜 |
| TGI | HFエコシステム連携 | 得意 | 30分〜 |
| TensorRT-LLM | 最高性能・NVIDIA特化 | 最高 | 数日 |
私の所感
vLLMを調べていて一番感動したのは、「OSの仮想メモリ」というコンピュータサイエンスの古典的な概念が、最先端のAI推論に直接適用できたという点だ。コンピュータサイエンスの基礎は、何年経っても役に立つ。
個人でローカルLLMを楽しむならOllamaで十分。しかし「チームで使いたい」「APIとして公開したい」「同時に複数人で使いたい」となった瞬間に、vLLMの出番がやってくる。OllamaからvLLMへの移行は、OpenAI互換APIのおかげでbase_urlを書き換えるだけ。思っているより気軽に試せるので、ぜひ一度触ってみてほしい。
参考文献
- vLLM公式サイト
- vLLM GitHub Repository
- Efficient Memory Management for Large Language Model Serving with PagedAttention (arXiv:2309.06180)
- UC Berkeley EECS - vLLM: An Efficient Inference Engine for Large Language Models (PhD Thesis, 2025)
- Red Hat - What is vLLM?
- vLLM Docs - Paged Attention
- Clore.ai - LLM Serving: Ollama vs vLLM vs TGI
- Build with Matija - LLM Inference Engine Showdown: vLLM vs Ollama vs TGI (2025)
- Nebius - Serving LLMs with vLLM: A practical inference guide
- Aleksa Gordic - Inside vLLM: Anatomy of a High-Throughput LLM Inference System
この記事が役に立ったら、いいね・ストックしていただけると励みになります。
vLLMやローカルLLM推論について質問があれば、コメントで気軽にどうぞ。
他にもローカルLLM関連の記事を書いています:
- Ollamaってなんだ?
- llama.cppってなんだ?
- GGUFってなんだ?
- LM Studioってなんだ?
- CUDAってなんだ?
- GPUってなんだ?
- VRAMってなんだ?
- PyTorchってなんだ?