跳到內容

LoRA 介面卡

本文件將向您展示如何在基礎模型之上,在 vLLM 中使用 LoRA 介面卡

LoRA 介面卡可以與任何實現了 SupportsLoRA 的 vLLM 模型一起使用。

介面卡可以高效地按請求進行服務,開銷極小。首先,我們下載介面卡並使用以下命令將其本地儲存:

from huggingface_hub import snapshot_download

sql_lora_path = snapshot_download(repo_id="yard1/llama-2-7b-sql-lora-test")

然後,我們例項化基礎模型並傳遞 enable_lora=True 標誌

from vllm import LLM, SamplingParams
from vllm.lora.request import LoRARequest

llm = LLM(model="meta-llama/Llama-2-7b-hf", enable_lora=True)

現在我們可以提交提示,並透過 lora_request 引數呼叫 llm.generateLoRARequest 的第一個引數是人類可識別的名稱,第二個引數是介面卡的全域性唯一 ID,第三個引數是 LoRA 介面卡的路徑。

程式碼
sampling_params = SamplingParams(
    temperature=0,
    max_tokens=256,
    stop=["[/assistant]"],
)

prompts = [
    "[user] Write a SQL query to answer the question based on the table schema.\n\n context: CREATE TABLE table_name_74 (icao VARCHAR, airport VARCHAR)\n\n question: Name the ICAO for lilongwe international airport [/user] [assistant]",
    "[user] Write a SQL query to answer the question based on the table schema.\n\n context: CREATE TABLE table_name_11 (nationality VARCHAR, elector VARCHAR)\n\n question: When Anchero Pantaleone was the elector what is under nationality? [/user] [assistant]",
]

outputs = llm.generate(
    prompts,
    sampling_params,
    lora_request=LoRARequest("sql_adapter", 1, sql_lora_path),
)

請檢視 examples/offline_inference/multilora_inference.py 以獲取使用非同步引擎的 LoRA 介面卡以及如何使用更高階配置選項的示例。

服務 LoRA 介面卡

LoRA 適配的模型也可以與相容 OpenAI 的 vLLM 伺服器一起提供服務。為此,我們使用 --lora-modules {name}={path} {name}={path} 在啟動伺服器時指定每個 LoRA 模組。

vllm serve meta-llama/Llama-2-7b-hf \
    --enable-lora \
    --lora-modules sql-lora=$HOME/.cache/huggingface/hub/models--yard1--llama-2-7b-sql-lora-test/snapshots/0dfa347e8877a4d4ed19ee56c140fa518470028c/

注意

提交 ID 0dfa347e8877a4d4ed19ee56c140fa518470028c 可能會隨時間變化。請檢查您環境中的最新提交 ID,以確保您使用的是正確的 ID。

伺服器入口點接受所有其他 LoRA 配置引數(max_lorasmax_lora_rankmax_cpu_loras 等),這些引數將應用於所有後續請求。在查詢 /models 端點時,我們應該看到我們的 LoRA 及其基礎模型(如果未安裝 jq,您可以按照 此指南 進行安裝。)

命令
curl localhost:8000/v1/models | jq .
{
    "object": "list",
    "data": [
        {
            "id": "meta-llama/Llama-2-7b-hf",
            "object": "model",
            ...
        },
        {
            "id": "sql-lora",
            "object": "model",
            ...
        }
    ]
}

請求可以透過 model 請求引數將 LoRA 介面卡指定為任何其他模型。請求將根據伺服器範圍的 LoRA 配置進行處理(即與基礎模型請求並行,並且如果提供了其他 LoRA 介面卡請求並且 max_loras 設定得足夠高,則可能與其他 LoRA 介面卡請求並行處理)。

以下是一個示例請求

curl https://:8000/v1/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "sql-lora",
        "prompt": "San Francisco is a",
        "max_tokens": 7,
        "temperature": 0
    }' | jq

動態服務 LoRA 介面卡

除了在伺服器啟動時提供 LoRA 介面卡之外,vLLM 伺服器還支援透過專用的 API 端點和外掛在執行時動態配置 LoRA 介面卡。當需要即時更改模型的靈活性時,此功能可能特別有用。

注意:在生產環境中啟用此功能存在風險,因為使用者可能會參與模型介面卡的管理。

要啟用動態 LoRA 配置,請確保將環境變數 VLLM_ALLOW_RUNTIME_LORA_UPDATING 設定為 True

export VLLM_ALLOW_RUNTIME_LORA_UPDATING=True

使用 API 端點

載入 LoRA 介面卡

要動態載入 LoRA 介面卡,請向 /v1/load_lora_adapter 端點發送 POST 請求,並提供要載入的介面卡的必要詳細資訊。請求負載應包括 LoRA 介面卡的名稱和路徑。

載入 LoRA 介面卡的示例請求

curl -X POST https://:8000/v1/load_lora_adapter \
-H "Content-Type: application/json" \
-d '{
    "lora_name": "sql_adapter",
    "lora_path": "/path/to/sql-lora-adapter"
}'

成功請求後,API 將從 vllm serve 返回 200 OK 狀態碼,curl 返回響應體:Success: LoRA adapter 'sql_adapter' added successfully。如果發生錯誤,例如找不到或無法載入介面卡,則會返回適當的錯誤訊息。

解除安裝 LoRA 介面卡

要解除安裝先前載入的 LoRA 介面卡,請向 /v1/unload_lora_adapter 端點發送 POST 請求,並提供要解除安裝的介面卡的名稱或 ID。

成功請求後,API 將從 vllm serve 返回 200 OK 狀態碼,curl 返回響應體:Success: LoRA adapter 'sql_adapter' removed successfully

解除安裝 LoRA 介面卡的示例請求

curl -X POST https://:8000/v1/unload_lora_adapter \
-H "Content-Type: application/json" \
-d '{
    "lora_name": "sql_adapter"
}'

使用外掛

或者,您可以使用 LoRAResolver 外掛動態載入 LoRA 介面卡。LoRAResolver 外掛使您能夠從本地和遠端源(如本地檔案系統和 S3)載入 LoRA 介面卡。在每個請求上,當有尚未載入的新模型名稱時,LoRAResolver 將嘗試解析並載入相應的 LoRA 介面卡。

如果您想從不同源載入 LoRA 介面卡,可以設定多個 LoRAResolver 外掛。例如,您可以有一個用於本地檔案的解析器,另一個用於 S3 儲存。vLLM 將載入它找到的第一個 LoRA 介面卡。

您可以安裝現有外掛或實現自己的外掛。預設情況下,vLLM 提供一個 用於從本地目錄載入 LoRA 介面卡的解析器外掛。 要啟用此解析器,請將 VLLM_ALLOW_RUNTIME_LORA_UPDATING 設定為 True,將 VLLM_PLUGINS 設定為包含 lora_filesystem_resolver,然後將 VLLM_LORA_RESOLVER_CACHE_DIR 設定為本地目錄。當 vLLM 收到使用 LoRA 介面卡 foobar 的請求時,它會首先在本地目錄中查詢名為 foobar 的目錄,並嘗試將該目錄的內容作為 LoRA 介面卡載入。如果成功,請求將正常完成,並且該介面卡將可用於伺服器上的正常使用。

或者,請按照以下示例步驟實現自己的外掛

  1. 實現 LoRAResolver 介面。

    簡單的 S3 LoRAResolver 實現示例
    import os
    import s3fs
    from vllm.lora.request import LoRARequest
    from vllm.lora.resolver import LoRAResolver
    
    class S3LoRAResolver(LoRAResolver):
        def __init__(self):
            self.s3 = s3fs.S3FileSystem()
            self.s3_path_format = os.getenv("S3_PATH_TEMPLATE")
            self.local_path_format = os.getenv("LOCAL_PATH_TEMPLATE")
    
        async def resolve_lora(self, base_model_name, lora_name):
            s3_path = self.s3_path_format.format(base_model_name=base_model_name, lora_name=lora_name)
            local_path = self.local_path_format.format(base_model_name=base_model_name, lora_name=lora_name)
    
            # Download the LoRA from S3 to the local path
            await self.s3._get(
                s3_path, local_path, recursive=True, maxdepth=1
            )
    
            lora_request = LoRARequest(
                lora_name=lora_name,
                lora_path=local_path,
                lora_int_id=abs(hash(lora_name)),
            )
            return lora_request
    
  2. 註冊 LoRAResolver 外掛。

    from vllm.lora.resolver import LoRAResolverRegistry
    
    s3_resolver = S3LoRAResolver()
    LoRAResolverRegistry.register_resolver("s3_resolver", s3_resolver)
    

    有關更多詳細資訊,請參閱 vLLM 的外掛系統

--lora-modules 的新格式

在以前的版本中,使用者會透過以下格式提供 LoRA 模組,可以是鍵值對或 JSON 格式。例如:

--lora-modules sql-lora=$HOME/.cache/huggingface/hub/models--yard1--llama-2-7b-sql-lora-test/snapshots/0dfa347e8877a4d4ed19ee56c140fa518470028c/

這僅包含每個 LoRA 模組的 namepath,但沒有提供指定 base_model_name 的方法。現在,您可以使用 JSON 格式在名稱和路徑旁邊指定 base_model_name。例如:

--lora-modules '{"name": "sql-lora", "path": "/path/to/lora", "base_model_name": "meta-llama/Llama-2-7b"}'

為了提供向後相容支援,您仍然可以使用舊的鍵值格式(name=path),但 base_model_name 在這種情況下將保持未指定。

模型卡中的 LoRA 模型譜系

--lora-modules 的新格式主要是為了支援在模型卡中顯示父模型資訊。以下是對您當前響應如何支援此功能的解釋:

  • LoRA 模型 sql-loraparent 欄位現在連結到其基礎模型 meta-llama/Llama-2-7b-hf。這正確地反映了基礎模型和 LoRA 介面卡之間的分層關係。
  • root 欄位指向 LoRA 介面卡的工件位置。
命令輸出
$ curl https://:8000/v1/models

{
    "object": "list",
    "data": [
        {
        "id": "meta-llama/Llama-2-7b-hf",
        "object": "model",
        "created": 1715644056,
        "owned_by": "vllm",
        "root": "~/.cache/huggingface/hub/models--meta-llama--Llama-2-7b-hf/snapshots/01c7f73d771dfac7d292323805ebc428287df4f9/",
        "parent": null,
        "permission": [
            {
            .....
            }
        ]
        },
        {
        "id": "sql-lora",
        "object": "model",
        "created": 1715644056,
        "owned_by": "vllm",
        "root": "~/.cache/huggingface/hub/models--yard1--llama-2-7b-sql-lora-test/snapshots/0dfa347e8877a4d4ed19ee56c140fa518470028c/",
        "parent": meta-llama/Llama-2-7b-hf,
        "permission": [
            {
            ....
            }
        ]
        }
    ]
}

多模態模型的預設 LoRA 模型

某些模型,例如 Granite SpeechPhi-4-multimodal-instruct 多模態模型,包含 LoRA 介面卡,當存在給定模態時,預期始終應用這些介面卡。使用上述方法管理起來可能有點麻煩,因為它需要使用者傳送 LoRARequest(離線)或根據請求的多模態資料內容在基礎模型和 LoRA 模型(伺服器)之間過濾請求。

為此,我們允許註冊預設的多模態 LoRA 以自動處理此問題,使用者可以將每種模態對映到一個 LoRA 介面卡,以便在存在相應輸入時自動應用它。請注意,目前,我們每個提示只允許一個 LoRA;如果提供了多個模態,並且每種模態都註冊了一個給定的模態,則它們都不會被應用。

離線推理的示例用法
from transformers import AutoTokenizer
from vllm import LLM, SamplingParams
from vllm.assets.audio import AudioAsset

model_id = "ibm-granite/granite-speech-3.3-2b"
tokenizer = AutoTokenizer.from_pretrained(model_id)

def get_prompt(question: str, has_audio: bool):
    """Build the input prompt to send to vLLM."""
    if has_audio:
        question = f"<|audio|>{question}"
    chat = [
        {"role": "user", "content": question},
    ]
    return tokenizer.apply_chat_template(chat, tokenize=False)


llm = LLM(
    model=model_id,
    enable_lora=True,
    max_lora_rank=64,
    max_model_len=2048,
    limit_mm_per_prompt={"audio": 1},
    # Will always pass a `LoRARequest` with the `model_id`
    # whenever audio is contained in the request data.
    default_mm_loras = {"audio": model_id},
    enforce_eager=True,
)

question = "can you transcribe the speech into a written format?"
prompt_with_audio = get_prompt(
    question=question,
    has_audio=True,
)
audio = AudioAsset("mary_had_lamb").audio_and_sample_rate

inputs = {
    "prompt": prompt_with_audio,
    "multi_modal_data": {
        "audio": audio,
    }
}


outputs = llm.generate(
    inputs,
    sampling_params=SamplingParams(
        temperature=0.2,
        max_tokens=64,
    ),
)

您還可以傳遞一個 --default-mm-loras 的 JSON 字典,將模態對映到 LoRA 模型 ID。例如,在啟動伺服器時:

vllm serve ibm-granite/granite-speech-3.3-2b \
    --max-model-len 2048 \
    --enable-lora \
    --default-mm-loras '{"audio":"ibm-granite/granite-speech-3.3-2b"}' \
    --max-lora-rank 64

注意:預設多模態 LoRA 目前僅適用於 .generate 和聊天補全。

使用技巧

配置 max_lora_rank

--max-lora-rank 引數控制 LoRA 介面卡允許的最大秩。此設定會影響記憶體分配和效能。

  • 將其設定為最大秩 — 您計劃使用的所有 LoRA 介面卡中的最大值。
  • 避免設定過高 — 使用遠大於所需的值會浪費記憶體並可能導致效能問題。

例如,如果您的 LoRA 介面卡具有秩 [16, 32, 64],則使用 --max-lora-rank 64 而不是 256。

# Good: matches actual maximum rank
vllm serve model --enable-lora --max-lora-rank 64

# Bad: unnecessarily high, wastes memory
vllm serve model --enable-lora --max-lora-rank 256