Multi-LoRA 部署#

使用 Ray Serve LLM 高效部署多个微调的 LoRA 适配器。

理解 Multi-LoRA 部署#

Multi-LoRA 允许您的模型在运行时切换不同的微调适配器,而无需重新加载基础模型。

当您的应用程序需要使用单个共享模型后端支持多个域、用户或任务时,请使用 multi-LoRA。以下是您可能希望在工作流程中添加适配器的主要原因:

  • 参数效率:LoRA 适配器很小,通常不到基础模型大小的 1%。这使得它们存储成本低、加载速度快,并且在推理过程中易于进行交换,这在内存紧张的情况下尤其有用。

  • 运行时自适应:使用 multi-LoRA,您可以在推理时切换不同的适配器,而无需重新加载基础模型。这使得能够根据用户、任务、域或上下文动态行为,所有这些都来自单个部署。

  • 简化的 MLOps:Multi-LoRA 通过将推理集中在一个模型上来降低基础设施复杂性和成本。

请求路由如何工作#

当收到针对特定 LoRA 适配器的请求时,Ray Serve 会

  1. 检查是否有任何副本已加载该适配器

  2. 查找一个已加载适配器但未过载的副本,并将请求路由到该副本

  3. 如果所有带有该适配器的副本都已过载,则将请求路由到一个较不繁忙的副本,该副本将加载适配器

  4. 如果没有副本加载该适配器,则根据默认请求路由逻辑(例如“Power of 2”)将请求路由到一个副本,并在那里加载它

Ray Serve LLM 然后缓存该适配器以供后续请求使用。Ray Serve LLM 通过具有最大大小的最近最少使用 (LRU) 机制控制每个副本上的 LoRA 适配器缓存,您可以通过 max_num_adapters_per_replica 变量来控制它。

使用 Multi-LoRA 配置 Ray Serve LLM#

要在您的部署上启用 multi-LoRA,请使用以下附加设置更新您的 Ray Serve LLM 配置。

LoRA 配置#

dynamic_lora_loading_path 设置为您的 AWS 或 GCS 存储路径

lora_config=dict(
    dynamic_lora_loading_path="s3://my_dynamic_lora_path",
    max_num_adapters_per_replica=16,  # Optional: limit adapters per replica
)
  • dynamic_lora_loading_path:包含 LoRA 检查点子目录的目录路径。

  • max_num_adapters_per_replica:每个副本缓存的 LoRA 适配器的最大数量。必须与 max_loras 匹配。

引擎参数#

将这些参数转发到您的 vLLM 引擎

engine_kwargs=dict(
    enable_lora=True,
    max_lora_rank=32,  # Set to the highest LoRA rank you plan to use
    max_loras=16,      # Must match max_num_adapters_per_replica
)
  • enable_lora:在 vLLM 引擎中启用 LoRA 支持。

  • max_lora_rank:支持的最大 LoRA 秩。设置为您计划使用的最高秩。

  • max_loras:每个批次的最大 LoRA 数量。必须与 max_num_adapters_per_replica 匹配。

示例#

以下示例展示了一个完整的 multi-LoRA 配置

from ray import serve
from ray.serve.llm import LLMConfig, build_openai_app

# Configure the model with LoRA
llm_config = LLMConfig(
    model_loading_config=dict(
        model_id="qwen-0.5b",
        model_source="Qwen/Qwen2.5-0.5B-Instruct",
    ),
    lora_config=dict(
        # Assume this is where LoRA weights are stored on S3.
        # For example
        # s3://my_dynamic_lora_path/lora_model_1_ckpt
        # s3://my_dynamic_lora_path/lora_model_2_ckpt
        # are two of the LoRA checkpoints
        dynamic_lora_loading_path="s3://my_dynamic_lora_path",
        max_num_adapters_per_replica=16, # Need to set this to the same value as `max_loras`.
    ),
    engine_kwargs=dict(
        enable_lora=True,
        max_loras=16, # Need to set this to the same value as `max_num_adapters_per_replica`.
    ),
    deployment_config=dict(
        autoscaling_config=dict(
            min_replicas=1,
            max_replicas=2,
        )
    ),
    accelerator_type="A10G",
)

# Build and deploy the model
app = build_openai_app({"llm_configs": [llm_config]})
serve.run(app, blocking=True)

将请求发送到 Multi-LoRA 适配器#

要查询基础模型,请像往常一样调用您的服务。

要在推理时使用特定的 LoRA 适配器,请在请求中使用以下格式包含适配器名称

<base_model_id>:<adapter_name>

其中

  • <base_model_id> 是您在 Ray Serve LLM 配置中定义的 model_id

  • <adapter_name> 是您云存储中适配器的文件夹名称

示例查询#

查询基础模型和不同的 LoRA 适配器

from openai import OpenAI

client = OpenAI(base_url="https://:8000/v1", api_key="fake-key")

# Base model request (no adapter)
response = client.chat.completions.create(
    model="qwen-0.5b",  # No adapter
    messages=[{"role": "user", "content": "Hello!"}],
)

# Adapter 1
response = client.chat.completions.create(
    model="qwen-0.5b:adapter_name_1",  # Follow naming convention in your cloud storage
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True,
)

for chunk in response:
    if chunk.choices[0].delta.content is not None:
        print(chunk.choices[0].delta.content, end="", flush=True)

# Adapter 2
response = client.chat.completions.create(
    model="qwen-0.5b:adapter_name_2",
    messages=[{"role": "user", "content": "Hello!"}],
)

另请参阅#