加载基座大模型
首先需要加载模型Llama 3 8b,并向vLLM表明我们将使用LoRA,同时还需要设置max_lora_rank。
from vllm import LLM, SamplingParams from vllm.lora.request import LoRARequest from huggingface_hub import snapshot_download llm = LLM(model="meta-llama/Meta-Llama-3-8B", enable_lora=True, max_lora_rank=16) |
|---|
创建LoRARequest
chat adapter
sampling_params_oasst = SamplingParams(temperature=0.7, top_p=0.9, max_tokens=500) oasst_lora_id = "kaitchup/Meta-Llama-3-8B-oasst-Adapter" oasst_lora_path = snapshot_download(repo_id=oasst_lora_id) oasstLR = LoRARequest("oasst", 1, oasst_lora_path) |
|---|
函数adapter
sampling_params_xlam = SamplingParams(temperature=0.0, max_tokens=500) xlam_lora_id = "kaitchup/Meta-Llama-3-8B-xLAM-Adapter" xlam_lora_path = snapshot_download(repo_id=xlam_lora_id) xlamLR = LoRARequest("xlam", 2, xlam_lora_path) |
|---|
生成token
chat adapter
prompts_oasst = [ "### Human: Check if the numbers 8 and 1233 are powers of two.### Assistant:", "### Human: What is the division result of 75 divided by 1555?### Assistant:", ] outputs = llm.generate(prompts_oasst, sampling_params_oasst, lora_request=oasstLR) for output in outputs: generated_text = output.outputs[0].text print(generated_text) print('------') |
|---|
函数adapter
prompts_xlam = [ "<user>Check if the numbers 8 and 1233 are powers of two.</user>\n\n<tools>", "<user>What is the division result of 75 divided by 1555?</user>\n\n<tools>", ] outputs = llm.generate(prompts_xlam, sampling_params_xlam, lora_request=xlamLR) for output in outputs: generated_text = output.outputs[0].text print(generated_text) print('------') |
|---|
基座模型只加载一次,并在内存中也是共享的。每个用户的请求,可以指定使用不同的lora adapter,一个请求只能指定一个lora adapter。
单基础模型可以同时挂载多个LoRA适配器(如聊天/函数调用),不同请求的lora adapter切换延迟低于1ms。
支持5+adapter并行服务。
以Llama 3 8B的微调模型kaitchup/Meta-Llama-3-8B-oasst-Adapter为例:
网络层 | 算子名 |
|---|---|
attention | self_attn.q_proj |
self_attn.k_proj | |
self_attn.v_proj | |
self_attn.o_proj | |
mlp | mlp.down_proj |
mlp.gate_proj | |
mlp.up_proj |

从vllm源码来看,可以支持包括embedding、qkv linear、mlp、lmhead相关的几乎所有矩阵乘运算。

实现思想来自于论文Punica: Multi-Tenant LoRA Serving,该论文主要解决多租户LoRA服务问题,Punica设计原则:
1.GPU很昂贵,因此需要尽量少的使用 GPU,尽量提高 GPU 利用率
2.Batching是提高性能和 GPU 利用率的有效手段
3.Decoding 阶段是模型服务成本的主要因素,因此只需关注 Decoding 性能即可,其他部分可以应用最简单的方案
Punica整体框架图:

Frontends:可面向用户提供 RESTful API,并将用户的请求转发给 Punica 调度器(Scheduler)。用户的请求包含 LoRA 模型的标识符和提示(prompt)。
Scheduler:将请求分发给GPU。
Runner:每个 GPU 都会启动一个 Runner,该 Runner 与 Scheduler 通信并控制所有 GPU 的执行。
在Punica中,每个 GPU 都会加载基座模型,很大一部分 GPU 显存保留给 KV cache。只有模型的 LoRA 组件在需要时从远端存储(比如 host 内存、硬盘)中传输到 GPU,这也就允许模型服务的快速冷启动。
Punica关键技术--SGMVM(Segmented Gather Matrix-Vecotr Multiplication):
对于有多个 LoRA 模型的系统,除了基座模型可以batching外,针对同一个LoRA模型的输入也可以batching起来一起处理。
如下图所示,s1表示第一个 LoRA 模型,sn表示第 n 个 LoRA 模型,si表示batch size。

对于第二部分计算,作者将其称为SGMV,并实现了一个高效的CUDA kernel来实现。

Expand kernel负责调度不同输入给相应的Ai,计算输出Vi
Shrink kernel负责调度不同的Vi给相应的Bi,计算输出Yi。

模型加载的时候,只加载正常的权重,loraAB初始化为0,并且初始化LoRAManager管理多个lora模型。

每个请求(prompt)可以使用不同的lora模型,根据请求里带的lora模型,添加到LoRAManager里。

槽位管理:支持多个LoRA同时加载,通过槽位索引管理 LRU缓存:自动管理LoRA内存,淘汰最少使用的适配器 批处理优化:Punica算法支持高效的批处理LoRA计算 内存优化:支持CPU/GPU内存分层存储 多租户支持:同时服务多个不同LoRA的请求
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。