跳到內容

LoRA 介面卡

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

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 適配模型也可以透過相容 Open-AI 的 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,以確保使用正確。

伺服器入口點接受所有其他 LoRA 配置引數(如 max_loras, max_lora_rank, max_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 設定得足夠高,則也與它們並行處理)。

以下是一個請求示例

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 和聊天補全。