跳到内容

LoRA 适配器

本文档向您展示如何在使用 vLLM 的基础模型之上使用 LoRA 适配器

LoRA 适配器可与任何实现了 SupportsLoRA 的 vLLM 模型一起使用。

适配器可以按请求高效地进行部署,且开销极小。首先,我们下载适配器并将其保存在本地:

from huggingface_hub import snapshot_download

sql_lora_path = snapshot_download(repo_id="jeeejeee/llama32-3b-text2sql-spider")

然后,我们实例化基础模型并传入 enable_lora=True 标志。

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

llm = LLM(model="meta-llama/Llama-3.2-3B-Instruct", 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 API 的 vLLM 服务器进行部署。为此,我们在启动服务器时使用 --lora-modules {name}={path} {name}={path} 来指定每个 LoRA 模块。

vllm serve meta-llama/Llama-3.2-3B-Instruct \
    --enable-lora \
    --lora-modules sql-lora=jeeejeee/llama32-3b-text2sql-spider

服务器入口点接受所有其他 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-3.2-3B-Instruct",
            "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 适配器的解析器插件,以及一个从 Hugging Face Hub 存储库加载 LoRA 适配器的解析器插件。要启用这些解析器中的任何一个,必须将 VLLM_ALLOW_RUNTIME_LORA_UPDATING 设置为 True。

  • 要利用本地目录,请设置 VLLM_PLUGINS 以包含 lora_filesystem_resolver,并将 VLLM_LORA_RESOLVER_CACHE_DIR 设置为本地目录路径。当 vLLM 收到使用 LoRA 适配器 foobar 的请求时,它会首先在本地目录中查找 foobar 目录,并尝试将其内容加载为 LoRA 适配器。如果成功,请求将正常完成,并且该适配器随后可用于服务器的正常使用。
  • 要利用 Hugging Face Hub 上的存储库,请设置 VLLM_PLUGINS 以包含 lora_hf_hub_resolver,并将 VLLM_LORA_RESOLVER_HF_REPO_LIST 设置为 Hugging Face Hub 上存储库 ID 的逗号分隔列表。当 vLLM 收到对 LoRA 适配器 my/repo/subpath 的请求时,如果该路径存在且包含 adapter_config.json,它将下载 my/reposubpath 的适配器,然后为该适配器构建一个缓存目录请求,类似于 lora_filesystem_resolver。请注意,启用远程下载是不安全的,不适用于生产环境。

或者,按照以下示例步骤实现您自己的插件:

  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 重载

动态加载 LoRA 适配器时,您可能需要在保持名称不变的情况下,用更新后的权重替换现有适配器。load_inplace 参数可实现此功能。这常见于异步强化学习设置中,适配器会不断更新并被替换,而不会中断正在进行的推理。

load_inplace=True 时,vLLM 将用新适配器替换现有适配器。

加载或替换同名 LoRA 适配器的请求示例:

curl -X POST https://:8000/v1/load_lora_adapter \
-H "Content-Type: application/json" \
-d '{
    "lora_name": "my-adapter",
    "lora_path": "/path/to/adapter/v2",
    "load_inplace": true
}'

--lora-modules 的新格式

在以前的版本中,用户通过以下格式(键值对或 JSON 格式)提供 LoRA 模块。例如:

--lora-modules  sql-lora=jeeejeee/llama32-3b-text2sql-spider

这仅包含了每个 LoRA 模块的 namepath,但没有提供指定 base_model_name 的方法。现在,您可以使用 JSON 格式在名称和路径之外指定 base_model_name。例如:

--lora-modules '{"name": "sql-lora", "path": "jeeejeee/llama32-3b-text2sql-spider", "base_model_name": "meta-llama/Llama-3.2-3B-Instruct"}'

为了提供向后兼容性支持,您仍然可以使用旧的键值格式 (name=path),但在那种情况下 base_model_name 将保持未指定。

模型卡片中的 LoRA 模型谱系

--lora-modules 的新格式主要是为了支持在模型卡片中显示父模型信息。以下是您当前的响应如何支持这一点的说明:

  • LoRA 模型 sql-loraparent 字段现在链接到其基础模型 meta-llama/Llama-3.2-3B-Instruct。这正确地反映了基础模型与 LoRA 适配器之间的层级关系。
  • root 字段指向 LoRA 适配器的制品位置。
命令输出:
$ curl https://:8000/v1/models

{
    "object": "list",
    "data": [
        {
        "id": "meta-llama/Llama-3.2-3B-Instruct",
        "object": "model",
        "created": 1715644056,
        "owned_by": "vllm",
        "root": "meta-llama/Llama-3.2-3B-Instruct",
        "parent": null,
        "permission": [
            {
            .....
            }
        ]
        },
        {
        "id": "sql-lora",
        "object": "model",
        "created": 1715644056,
        "owned_by": "vllm",
        "root": "jeeejeee/llama32-3b-text2sql-spider",
        "parent": "meta-llama/Llama-3.2-3B-Instruct",
        "permission": [
            {
            ....
            }
        ]
        }
    ]
}

多模态模型 Tower 和 Connector 的 LoRA 支持

目前,vLLM 实验性地支持多模态模型的 Tower 和 Connector 组件的 LoRA。要启用此功能,您需要为 Tower 和 Connector 实现相应的 token 辅助函数。有关此方法背后的原理,请参阅 PR 26674。我们欢迎各界贡献,以将 LoRA 支持扩展到更多模型的 Tower 和 Connector。请参考 Issue 31479 查看当前模型的支持状态。

多模态模型的默认 LoRA 模型

某些模型,例如 Granite SpeechPhi-4-multimodal-instruct 多模态模型,包含在给定模态出现时始终需要应用的 LoRA 适配器。使用上述方法管理这些可能会很繁琐,因为这要求用户(离线)发送 LoRARequest,或者(服务器)根据请求的多模态内容在基础模型和 LoRA 模型之间过滤请求。

为此,我们允许注册默认的多模态 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`][vllm.lora.request.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

将 LoRA 限制在特定模块

--lora-target-modules 参数允许您在部署时限制 LoRA 应用于哪些模型模块。当您仅需在特定层上使用 LoRA 时,这对性能调优非常有用。

# Apply LoRA only to output projection layers
vllm serve model --enable-lora --lora-target-modules o_proj

# Apply LoRA to multiple specific modules
vllm serve model --enable-lora --lora-target-modules o_proj qkv_proj down_proj

如果未指定 --lora-target-modules,LoRA 将应用于模型中所有支持的模块。此参数接受模块后缀(模块名称的最后一部分),例如 o_proj, qkv_proj, gate_proj 等。