【vLLM】LoRAアダプターの動的ロードで発生するエラー「ValueError: The adapter … is not found」の解決方法

問題の概要:vLLMでLoRAアダプターを動的にロードしようとするとエラーが発生する

vLLMは、大規模言語モデル(LLM)を高速に推論するためのライブラリです。モデルをLoRA(Low-Rank Adaptation)でファインチューニングした後、vLLMサーバーでベースモデルと複数のLoRAアダプターを動的に切り替えながら使用したい場面は多くあります。しかし、vLLMの`LLM`クラスや`AsyncLLMEngine`を使ってアダプターを動的にロード・アンロードしようとすると、以下のようなエラーに遭遇することが頻繁に報告されています。

# 典型的なエラーメッセージの例
ValueError: The adapter 'customer_support_lora' is not found. Please use the load_adapter method to load it first.
# または
RuntimeError: The adapter '...' is already loaded. Please unload it first before loading another adapter.
# サーバー起動時のエラー例
vllm.utils.router.AdapterNotSupportedError: Adapter is not supported for this model.

この問題は、vLLMサーバーをREST APIやOpenAI互換APIとして立ち上げ、リクエストごとに`adapter_name`パラメータを指定して異なるLoRAアダプターを適用したい場合に特に顕著です。単一のアダプターを静的にロードする場合は問題なく動作しますが、複数アダプターの動的切り替えを実装する過程で上記エラーが発生し、期待通りの動作が得られなくなります。

原因の解説:vLLMのアダプター管理メカニズムとAPIの理解不足

このエラーが発生する主な原因は、vLLMにおけるLoRAアダプターの管理方法を正しく理解していないこと、および使用するvLLMのバージョンやAPIによって実装方法が異なることにあります。

1. エンジン初期化とアダプターロードのタイミング

vLLMの`LLM`クラスや`AsyncLLMEngine`は、初期化時に`enabled_lora_adapters`という引数で使用可能なアダプターのリストを指定する必要があります。初期化後に全く新しいアダプターを「動的」にロードすることは、vLLMの現在のアーキテクチャでは完全にはサポートされていません。多くのユーザーが期待する「任意のアダプターをいつでもロード」という動作ではなく、「初期化時に指定したアダプター群の中から選択して使用する」というモデルです。

2. vLLMバージョンによる差異

vLLMは開発が活発で、LoRA機能に関してもバージョンによって大きく実装が変わっています。v0.2.x以前とv0.3.x以降では、アダプター関連のAPIが変更されているケースがあり、古いチュートリアルやコード例をそのまま使うとエラーが発生します。

3. アダプターファイルの形式とパス

Hugging FaceのPEFTライブラリで保存したLoRAアダプターの形式(`adapter_model.bin`と`adapter_config.json`)と、vLLMが期待する形式が一致していない場合もエラーの原因となります。また、アダプターファイルへのパスが正しく指定されていない可能性もあります。

解決方法:vLLMで複数LoRAアダプターを動的に切り替える手順

ここでは、vLLM v0.3.3を例に、OpenAI互換APIサーバーとして起動し、リクエストごとにLoRAアダプターを切り替える正しい手順を説明します。

ステップ1: 環境とバージョンの確認

まず、vLLMのバージョンを確認し、必要に応じてアップグレードします。LoRA機能はv0.2.0以降で本格的にサポートされています。

# vLLMのバージョン確認
pip show vllm
# 最新版へのアップグレード(推奨)
pip install -U vllm
# 特定のバージョンをインストールする場合
pip install vllm==0.3.3

ステップ2: LoRAアダプターの準備

使用するすべてのLoRAアダプターを事前に準備します。各アダプターは独立したディレクトリに保存されている必要があります。

# アダプターのディレクトリ構造の例
adapters/
├── customer_support/
│   ├── adapter_model.bin
│   └── adapter_config.json
├── creative_writing/
│   ├── adapter_model.bin
│   └── adapter_config.json
└── code_generation/
    ├── adapter_model.bin
    └── adapter_config.json

ステップ3: vLLMサーバーの起動(正しい方法)

最も重要なポイントは、サーバー起動時に--enable-loraオプションと--lora-modulesオプションを使用して、使用する可能性のあるすべてのアダプターを事前に宣言することです。

# OpenAI互換APIサーバーとして起動するコマンド例
python -m vllm.entrypoints.openai.api_server 
    --model meta-llama/Llama-2-7b-chat-hf 
    --enable-lora 
    --lora-modules customer_support=./adapters/customer_support,creative_writing=./adapters/creative_writing,code_generation=./adapters/code_generation 
    --served-model-name llama-2-7b-chat 
    --api-key token-abc123 
    --port 8000

--lora-modulesの引数形式はアダプター名=アダプターパスをカンマで区切ります。ここで指定したアダプター名(例: `customer_support`)が、後ほどAPIリクエストで参照する名前になります。

ステップ4: APIリクエストでのアダプター指定

サーバー起動後、OpenAI互換のChat Completions APIを呼び出す際に、`extra_body`パラメータ内で`adapter_name`を指定します。

# Pythonクライアントコードの例
import openai

client = openai.OpenAI(
    api_key="token-abc123",
    base_url="http://localhost:8000/v1"
)

# customer_supportアダプターを使用するリクエスト
response_support = client.chat.completions.create(
    model="llama-2-7b-chat", # --served-model-nameで指定した名前
    messages=[{"role": "user", "content": "商品の返品について教えてください。"}],
    extra_body={
        "adapter_name": "customer_support"  # 起動時に指定したアダプター名
    }
)

# creative_writingアダプターを使用する別のリクエスト
response_writing = client.chat.completions.create(
    model="llama-2-7b-chat",
    messages=[{"role": "user", "content": "海辺の情景を詩にしてください。"}],
    extra_body={
        "adapter_name": "creative_writing"
    }
)

cURLを使用する場合は以下のようになります。

curl http://localhost:8000/v1/chat/completions 
  -H "Content-Type: application/json" 
  -H "Authorization: Bearer token-abc123" 
  -d '{
    "model": "llama-2-7b-chat",
    "messages": [{"role": "user", "content": "Pythonでクイックソートを実装してください。"}],
    "extra_body": {
      "adapter_name": "code_generation"
    }
  }'

ステップ5: AsyncLLMEngineを直接使用する場合(上級者向け)

APIサーバーではなく、プログラム内で直接`AsyncLLMEngine`を使用する場合は、以下のように初期化します。

from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.async_llm_engine import AsyncLLMEngine

engine_args = AsyncEngineArgs(
    model="meta-llama/Llama-2-7b-chat-hf",
    enable_lora=True,
    lora_modules=[
        ("customer_support", "./adapters/customer_support"),
        ("creative_writing", "./adapters/creative_writing")
    ],
    # 他の引数...
)
engine = AsyncLLMEngine.from_engine_args(engine_args)

# 推論時はRequestのLoRAパラメータにアダプター名を指定
from vllm import SamplingParams
from vllm import RequestOutput
from vllm import LoRARequest

sampling_params = SamplingParams(temperature=0.7, max_tokens=100)
lora_request = LoRARequest("customer_support", 1)  # アダプター名, ローレベル

# エンジンを使用した非同期推論の実行
# (実際の使用ではadd_requestやgenerateなどのメソッドを適切に組み合わせます)

コード例・コマンド例:よくあるエラーとその対処法

エラー1: 「Adapter is not supported for this model」

原因: ベースモデルがLoRAをサポートしていない、または`–enable-lora`オプションを忘れている。

解決策: モデルがLoRAをサポートしているか確認し、必ず`–enable-lora`オプションを付ける。

# 誤った起動方法(--enable-loraがない)
python -m vllm.entrypoints.openai.api_server --model meta-llama/Llama-2-7b-chat-hf

# 正しい起動方法
python -m vllm.entrypoints.openai.api_server --model meta-llama/Llama-2-7b-chat-hf --enable-lora ...

エラー2: 「The adapter ‘xxx’ is not found」

原因: サーバー起動時に`–lora-modules`で指定していないアダプター名をAPIリクエストで指定している。

解決策: 使用するすべてのアダプターを起動時に宣言する。

# 不足しているアダプターを追加する
--lora-modules customer_support=./adapters/cs,writing=./adapters/write  # writingはあるがcreative_writingはない

# APIリクエストでcreative_writingを指定するとエラーになる
{"adapter_name": "creative_writing"}  # エラー!

エラー3: アダプターファイルの読み込みエラー

原因: アダプターパスが間違っている、またはファイル形式が不正。

解決策: パスを確認し、PEFT形式のアダプターであることを確認する。

# アダプターディレクトリの内容確認
ls -la ./adapters/customer_support/
# 期待される出力:
# -rw-r--r-- adapter_config.json
# -rw-r--r-- adapter_model.bin

まとめ・補足情報

vLLMでLoRAアダプターを「動的」に使用するとは、サーバー起動時にすべての潜在的なアダプターを事前ロードしておき、リクエストごとにそれらを切り替えることを意味します。真の意味での実行時動的ロード(新しいアダプターファイルをサーバー再起動なしで追加)は、現在のvLLMでは完全にはサポートされていません。

パフォーマンスに関する注意点: 多数のLoRAアダプターを有効にすると、GPUメモリ使用量が増加します。vLLMはメモリ効率の良い方法でアダプターを管理しますが、10個以上のアダプターを同時に有効にする場合は、メモリ容量に注意が必要です。また、アダプターの切り替えには多少のオーバーヘッドが生じるため、超高スループットが要求される環境では、アダプターごとに専用のエンジンインスタンスを立ち上げることを検討してください。

今後の展望: vLLM開発チームはLoRA機能の強化を継続しており、将来的にはより柔軟なアダプターの動的管理が可能になることが期待されます。最新の情報はvLLMのGitHubリポジトリや公式ドキュメントを定期的に確認することをお勧めします。

本記事で紹介した方法を適用することで、vLLMを使用したマルチテナントなLLMサービスや、単一モデルで複数タスクを切り替える効率的な推論システムの構築が可能になります。エラーが発生した場合は、まずvLLMのバージョンと起動オプション、アダプターパスを再確認することが最も確実な解決への第一歩です。

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