想象你是一家面馆的老板。灶台上有一锅熬好的老汤——鲜甜温和,什么客人都能接受,但谈不上特色。为了让汤更贴合特定客群,你请了微调师傅,他不改动老汤的配方,只另外调了一小包“秘制调料”:有人调出麻辣包,有人调出葱油包。这包调料体积很小,却决定了一碗面的灵魂——它,就是 LoRA。 现在问题来了:客人点单时,总不能让人家自己拆调料包吧?你有两条路:把调料直接煮进老汤,从此这就是“麻辣汤底”,这叫合并;或者保留原汤,客人点单时现场加料,这叫动态加载。如果同时请了多位大厨、各有一包调料,讲究就更多了:全部倒进去(线性合并)?开个民主会取共识(TIES合并)?还是大胆断舍离(DARE合并)?调料下锅会不会把汤熬坏(精度损失)?什么情况该煮、什么情况该现加(部署取舍)? 这篇文章,就把这些问题一个个讲透——让你从“训练完 LoRA 就不知道下一步”到“能独立设计生产级部署方案”。

岗位关联

学完这篇,你将理解 LoRA 从训练到上线的完整路径,能按业务场景选择合并还是动态加载,并看懂 vLLM、TGI、SGLang 等推理框架的 LoRA 支持方案——这是大模型工程落地的关键一环。

目录
  1. 可视化演示
  2. 合并本质与原理
  3. 三种合并方式
  4. 精度损失问题
  5. 多LoRA热切换
  6. 推理框架支持
  7. 合并与部署取舍
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

合并本质与原理

面馆老汤之所以能变成“麻辣汤底”,靠的是那包秘制调料。但调料不是凭空出现的——微调师傅在定制口味时,只动过两个小本子:一本记录食材清单(低秩矩阵 A),一本记录配比心得(低秩矩阵 B)。两个本子合在一起,才是一份完整的调料增量 ΔW。基础模型的全部参数就像整锅老汤,一点没动,动的只是这两个小本子。

在 LoRA 微调中,基础权重 W 被冻结,只训练 A、B 两个低秩矩阵:A 把输入压缩到低秩空间,B 再把低秩空间映射回原始维度,两者乘积正是权重增量 ΔW。所谓“合并”,核心就是把这个增量写回原始权重:

合并完成后,LoRA 补丁的使命就结束了:推理时不再需要单独加载 LoRA 文件,模型本身就是“带风味的老汤”。这也是生产中把“微调产物”变成“可交付模型”的第一步。

W′=W+αr⋅B×A\displaystyle W' = W + \frac{\alpha}{r}\cdot B \times A
这个公式翻译成人话:新权重 = 老汤原味 + 按比例调好的调料。其中 α/r 管的是“放几勺”。
  1. 原始权重 W基础模型预训练得到的权重矩阵,对应老汤的汤底:
    W\displaystyle W
  2. 低秩矩阵 B、ALoRA 微调唯一训练的两个小矩阵,两者乘积近似微调产生的变化量,即“调料”本身:
    B×A=ΔW\displaystyle B \times A = \Delta W
  3. 缩放系数 α/rα 是超参数(alpha),r 是秩(rank),比值决定 LoRA 增量施加的强度,对应“放几勺调料”:
    αr\displaystyle \frac{\alpha}{r}
  4. 叠加求和 W'把缩放后的增量加到原始权重上,得到合并后的新权重,对应“调料煮进汤里”:
    W′=W+αr⋅B×A\displaystyle W' = W + \frac{\alpha}{r}\cdot B \times A

三种合并方式

如果面馆同时请了三位大厨,分别调出麻辣、葱油、菌菇三包调料,但客人只想要一碗口味统一的招牌面——这时就有三种“合料”策略,对应 LoRA 合并的三种主流方法。

线性合并(Linear Merge):最简单粗暴,把调料包直接倒进同一锅汤。数学上就是把 LoRA 增量加到基础权重上:

线性合并实现最简单,但多个 LoRA 直接相加可能“串味”——比如一个想增强数学能力、一个想增强文学风格,两者在某些神经元上方向相反,互相抵消。

TIES 合并(Trim Elect Sign & Merge):倒入前先开“民主协商会”。第一步修剪(Trim):把每包里分量很小的原料直接扔掉;第二步选举符号(Elect Sign):对每一种原料,看多数大厨认为该加还是该减;第三步合并(Merge):只把达成共识的原料按平均分量入锅:

TIES 适合多个 LoRA 融合,能显著减少“串味”,是社区融合多任务能力的常用方案。

DARE 合并(Drop And REscale):思路更大胆——三包调料里 90% 的原料都是“噪音”,随机丢掉,剩下 10% 按比例放大 10 倍再入锅。因为随机丢弃不改变期望值,放大后的净效果与原配料等价:

三种方式对应三种“合料哲学”:线性是全盘接受,TIES 是民主共识,DARE 是断舍离后聚焦精华。选哪种,取决于你有几包调料、调料之间是否犯冲。

W′=W+∑k=1nΔWk\displaystyle W' = W + \sum_{k=1}^{n} \Delta W_k
  1. 随机掩码 Mask按丢弃率 p 随机把大多数增量置为 0:
    Maski={0,以概率 p1,以概率 1−p\displaystyle \text{Mask}_i = \begin{cases} 0, & \text{以概率 } p \\ 1, & \text{以概率 } 1-p \end{cases}
  2. 缩放因子 1/(1-p)对保留下来的增量放大,以弥补丢弃损失,保持期望不变:
    11−p\displaystyle \frac{1}{1-p}
  3. 逐元素相乘只保留少数“精华”增量并放大:
    ΔWDARE=11−p⋅Mask⊙ΔW\displaystyle \Delta W_{\text{DARE}} = \frac{1}{1-p}\cdot \text{Mask} \odot \Delta W

精度损失问题

调料在加工翻炒时可能氧化变味,合并权重也一样——数值计算不是“零损耗”的。首先是数值类型转换:合并时如果基础权重是 FP16、LoRA 是 BF16,或者相反,低精度浮点数在加法时会舍入,产生微小偏差。其次是量化模型合并:很多部署用 4bit 量化后的模型跑推理,但量化权重是整数表示,直接叠加 LoRA 增量会“对不上账”,正确的做法是先把量化权重反量化回 FP16,合并完成后再视需要重新量化。

怎么判断“变味”是否可接受?用一个简单的相对误差指标,拿同一批输入分别过“带 LoRA 的模型”和“合并后的模型”,比较输出:

精度验证是合并后上线前的“试菜环节”:先自己尝一口,确认味道没跑偏,再端给客人。一旦误差超标,优先检查数值类型是否统一、是否做了不必要的多次转换。

err=∥fmerge(x)−flora(x)∥2∥flora(x)∥2\displaystyle \text{err} = \frac{\left\| f_{\text{merge}}(x) - f_{\text{lora}}(x) \right\|_2}{\left\| f_{\text{lora}}(x) \right\|_2}
这个指标衡量的是:合并前后的输出差了多少。工程上如果相对误差在 1e-5 量级,属于正常的浮点漂移;如果到了 1e-2 量级,就要检查类型转换或量化步骤了。
  1. 合并后输出 f_merge(x)模型权重合并完成后的前向输出:
    fmerge(x)\displaystyle f_{\text{merge}}(x)
  2. 带 LoRA 输出 f_lora(x)合并前、运行时叠加 LoRA 的前向输出:
    flora(x)\displaystyle f_{\text{lora}}(x)
  3. 差值的二范数逐元素差平方求和再开方,衡量整体差异大小:
    ∥fmerge(x)−flora(x)∥2\displaystyle \left\| f_{\text{merge}}(x) - f_{\text{lora}}(x) \right\|_2
  4. 归一化分母除以原始输出的范数,消除量纲影响,得到可比较的相对误差:
    ∥flora(x)∥2\displaystyle \left\| f_{\text{lora}}(x) \right\|_2

多LoRA热切换

面馆客人众口难调,与其为每种口味都煮一锅汤,不如保留原汤,准备一排调料盒:客人点麻辣就加麻辣,点葱油就加葱油,一锅老汤服务所有客人。这就是多 LoRA 热切换的智慧。

实现上,基础模型权重常驻显存,多个 LoRA 适配器(adapter)则是“小调料盒”:每个请求带着自己的任务标识(如 lora_id),推理框架根据标识把对应适配器临时“叠”到基础模型上,算完即走。显存开销也很直观:

请求调度上,框架通常用 LRU(最近最少使用)策略管理适配器:长时间没被点到的调料盒自动撤下,释放显存;新的请求来了再快速装上。多个任务之间互不干扰,每个请求用的是自己那份 LoRA 增量,这就是任务隔离性——相当于每位客人的调料盒是独立的,不会串味。

Mtotal=Mbase+∑i=1kMadapteri\displaystyle M_{\text{total}} = M_{\text{base}} + \sum_{i=1}^{k} M_{\text{adapter}_i}
意思是:总显存 ≈ 基础模型占用 + 当前已加载的所有 LoRA 适配器占用之和。好在单个 LoRA 通常只有基础模型的 1% 左右,几十个任务共用一个基础模型在显存上非常划算。
  1. 基础模型显存 M_base一锅老汤的固定成本,无论挂多少调料盒都得付:
    Mbase\displaystyle M_{\text{base}}
  2. 适配器显存 M_adapter每个 LoRA 适配器在显存中的占用:
    Madapteri\displaystyle M_{\text{adapter}_i}
  3. 求和关系总显存等于基础模型加上所有已加载适配器之和:
    Mtotal=Mbase+∑i=1kMadapteri\displaystyle M_{\text{total}} = M_{\text{base}} + \sum_{i=1}^{k} M_{\text{adapter}_i}

推理框架支持

有了热切换的构想,还得有靠谱的“前厅服务员团队”来执行。目前主流推理框架对 LoRA 的动态加载支持已经很成熟。

vLLM:目前社区使用最广。启动时加 --enable-lora 参数开启 LoRA 支持,之后每个请求可以在参数里指定 lora_request,vLLM 会调度对应适配器。它还支持批量请求中不同 LoRA 混合排队,吞吐表现优秀。

Hugging Face TGI(Text Generation Inference):原生支持通过 API 动态加载/卸载适配器(adapter)。适合已经深度使用 Hugging Face 生态、需要灵活管理多个 LoRA 版本的团队。

SGLang:主打结构化推理加速,对多 LoRA 场景做了专门的调度优化,高并发下多任务切换的开销更低,适合对延迟要求苛刻的多租户场景。

兼容性对比:需要明确一点——合并后的模型是“纯基础模型”,任何推理框架、任何部署方式都能跑;而动态加载 LoRA 依赖框架的原生支持。如果团队用的是自研推理引擎或精简部署环境,合并几乎是唯一选择;如果用的是上述主流框架,动态加载则能省下不少显存和运维成本。

合并与部署取舍

现在到了做决策的时刻:这锅汤,到底该煮进去(合并),还是现加料(动态加载)?答案取决于你的生意模式。

适合合并的场景:单任务、口味固定、客流稳定。典型如一个客服机器人只服务一个业务线,LoRA 微调后合并进基础模型,推理时零额外开销,延迟最低,且与框架无关——哪怕是纯 C++ 的轻量部署环境也能跑。适合动态加载的场景:多任务、口味多变、需要频繁上新。典型如一个模型同时服务文案、翻译、摘要十几个业务线,每个任务一个 LoRA,共享基础模型能大幅节省显存。

两者的延迟差异也很直观:

实际决策时,可以画一张简单的对照表:任务数(多于 5 个优先动态)、QPS(高并发优先合并)、显存预算(紧张优先动态)、上新频率(频繁优先动态)。没有绝对的对错,只有适不适合你的面馆。

Ldynamic=Linfer+Lswitch\displaystyle L_{\text{dynamic}} = L_{\text{infer}} + L_{\text{switch}}
动态加载的延迟 = 正常推理延迟 + 切换/加载适配器的开销。合并方案没有后者,所以单次请求延迟更低;但动态加载省下了为每个任务单独部署一套模型的显存成本。
  1. 推理延迟 L_infer模型本身计算输出的时间,两种方案差异不大:
    Linfer\displaystyle L_{\text{infer}}
  2. 切换开销 L_switch把适配器加载到显存并完成权重组合的时间,只在动态加载时发生:
    Lswitch\displaystyle L_{\text{switch}}
  3. 总延迟关系动态方案的延迟是两者之和,合并方案则没有切换项:
    Ldynamic=Linfer+Lswitch\displaystyle L_{\text{dynamic}} = L_{\text{infer}} + L_{\text{switch}}

AI 应用场景

单任务客服机器人的合并部署

阶段:模型上线部署(推理服务)

解决的问题:客服机器人用通用大模型回答不专业,团队用业务语料微调了一个 LoRA。如果每次推理都动态叠加 LoRA,框架必须支持适配器调度,且每个请求多一层计算;合并后直接用标准 vLLM 服务,零额外依赖、零切换开销。

具体案例:某电商售后客服团队基于 Qwen 基座微调了“售后话术 LoRA”,用脚本合并后部署为独立服务。相比带 LoRA 推理,合并后单请求延迟降低约 15%,QPS 提升约 30%,且模型文件可以直接交给运维同学用任意框架部署,不依赖 Python 环境装 peft。

多业务线内容平台的热切换

阶段:多租户在线推理服务

解决的问题:一个内容平台有文案生成、标题润色、摘要提取、翻译四条业务线,每条线单独微调了一个 LoRA。若各自部署完整模型,显存成本翻四倍;用多 LoRA 热切换,一个基础模型 + 四个小适配器即可服务所有业务。

具体案例:平台用 vLLM 开启 --enable-lora,同一时刻挂载 4 个 LoRA 适配器,请求按路由分配 lora_request。基础模型占 16GB 显存,4 个适配器合计仅占约 800MB,相比四套独立部署节省了约 75% 的显存成本,新业务上线只需上传新的 LoRA 文件,无需重启服务。

垂直模型的标准化交付

阶段:模型打包与交付

解决的问题:很多垂直行业客户(如银行、医院)对数据安全要求极高,模型要在客户内网部署,客户环境往往没有 Python 生态、没有 peft 库。这时把 LoRA 合并进基础模型,导出一个完整的权重文件,客户用任何推理框架都能直接加载。

具体案例:一家金融科技公司为银行做合同审阅模型,交付物是一个合并后的 7B 模型文件 + 标准 vLLM 启动脚本。银行离线环境无需安装任何微调相关依赖,一个命令就能跑起来,也规避了 LoRA 文件与基础模型版本不匹配的兼容性问题。

Python 演示

python97 行
"""LoRA merge demo: linear / TIES / DARE merging strategies.""" import torchimport torch.nn as nn  class LinearWithLoRA(nn.Module):    """A linear layer with a LoRA patch: W frozen, A/B trainable."""     def __init__(self, in_features, out_features, rank=4, alpha=8):        super().__init__()        self.in_features = in_features        self.out_features = out_features        self.rank = rank        self.alpha = alpha        self.scaling = alpha / rank        # Base weight W (frozen during fine-tuning)        self.weight = nn.Parameter(torch.randn(out_features, in_features) * 0.1)        # LoRA low-rank matrices: A random, B zero (delta starts at 0)        self.lora_A = nn.Parameter(torch.randn(rank, in_features) * 0.05)        self.lora_B = nn.Parameter(torch.zeros(out_features, rank))     def forward(self, x, use_lora=True):        """y = x W^T + scale * x A^T B^T"""        out = x @ self.weight.T        if use_lora:            out = out + self.scaling * (x @ self.lora_A.T @ self.lora_B.T)        return out     def get_delta(self):        """Extract LoRA delta matrix: delta = (alpha / r) * B @ A"""        return self.scaling * (self.lora_B @ self.lora_A)  def linear_merge(layer):    """Method 1: linear merge W' = W + delta."""    with torch.no_grad():        merged_weight = layer.weight + layer.get_delta()    new_layer = LinearWithLoRA(layer.in_features, layer.out_features,                               layer.rank, layer.alpha)    with torch.no_grad():        new_layer.weight.copy_(merged_weight)    return new_layer  def ties_merge(layers):    """Method 2: TIES merge - trim, elect sign, then average."""    deltas = torch.stack([layer.get_delta() for layer in layers])    # Trim: keep top-k magnitudes, zero out the rest    k = max(1, int(deltas.shape[-1] * 0.2))    threshold = deltas.abs().topk(k, dim=-1).values[..., -1:]    trimmed = deltas * (deltas.abs() >= threshold).float()    # Elect sign: majority sign at each position    majority_sign = trimmed.sign().sum(dim=0).sign()    # Merge: average only the positions that agree with majority    agreed = trimmed * (trimmed.sign() == majority_sign).float()    delta = agreed.sum(dim=0) / (agreed != 0).sum(dim=0).clamp(min=1)    return delta  def dare_merge(layers, drop_rate=0.9):    """Method 3: DARE merge - drop random deltas, rescale the rest."""    deltas = torch.stack([layer.get_delta() for layer in layers])    mask = torch.rand_like(deltas) > drop_rate    delta = (deltas * mask.float()).sum(dim=0) / (1.0 - drop_rate) / len(layers)    return delta  def main():    torch.manual_seed(42)    layer = LinearWithLoRA(8, 4, rank=2, alpha=4)    x = torch.randn(3, 8)     # Simulate fine-tuning: update A and B (usually learned from data)    with torch.no_grad():        layer.lora_A.add_(torch.randn_like(layer.lora_A) * 0.1)        layer.lora_B.add_(torch.randn_like(layer.lora_B) * 0.1)     y_before = layer(x)    merged_layer = linear_merge(layer)    y_after = merged_layer(x, use_lora=False)     diff = (y_before - y_after).abs().max().item()    print("max output diff after merge:", diff)    assert diff < 1e-5, "merge failed: outputs differ"    print("linear merge verified")     # Simulate three task-specific LoRAs and fuse them    layers = [LinearWithLoRA(8, 4, rank=2, alpha=4) for _ in range(3)]    delta_ties = ties_merge(layers)    delta_dare = dare_merge(layers, drop_rate=0.8)    print("TIES merged delta shape:", tuple(delta_ties.shape))    print("DARE merged delta shape:", tuple(delta_dare.shape))  if __name__ == "__main__":    main()
目的:用 PyTorch 从零实现一个带 LoRA 的线性层,演示三种合并策略(线性 / TIES / DARE)并验证合并前后输出一致。
  1. self.scaling = alpha / rank预先算好缩放系数,对应“放几勺调料”。
  2. out = out + self.scaling * (x @ self.lora_A.T @ self.lora_B.T)LoRA 前向:在基础输出上叠加低秩增量。
  3. merged_weight = layer.weight + layer.get_delta()线性合并核心:直接把增量写回基础权重。
  4. new_layer.weight.copy_(merged_weight)将合并后的权重写入新层,对应“煮好的汤倒进新锅”。
  5. threshold = deltas.abs().topk(k, dim=-1).values[..., -1:]TIES 修剪:取每个位置幅度最大的前 20% 作为保留元素。
  6. majority_sign = trimmed.sign().sum(dim=0).sign()TIES 选举符号:多个 LoRA 对同一位置取多数派的正负号。
  7. delta = (deltas * mask.float()).sum(dim=0) / (1.0 - drop_rate) / len(layers)DARE 合并:丢弃 90% 后把剩余值放大 10 倍再平均。
  8. assert diff < 1e-5, "merge failed: outputs differ"验证合并前后输出一致,对应上线前的“试菜”环节。
  9. torch.manual_seed(42)固定随机种子,保证实验可复现。

常见误区

误区把 `adapter_model.safetensors` 文件放进模型文件夹,再改一改配置,以为这样就算“合并”好了。

正解合并是数值运算——把 LoRA 增量矩阵按公式加到基础权重上,生成一个全新的权重文件。文件拷贝只是“带着补丁”,推理时框架仍需在运行时加载适配器并计算增量,性能上等于动态加载。真正的合并要用脚本或 peft 的 merge_and_unload() 完成。

为何常见:“合并”这个词容易让人联想到 git merge 或文件合并,产生“放一起就是合并”的错觉。

误区“我合并完测了一下,输出和原来差了 0.0001,是不是合并出 bug 了?”

正解数学上 W' = W + ΔW 完全等价,但实际计算有浮点精度限制:FP16 只有约 3 位有效十进制数,BF16 的尾数更少,加上反量化、多次矩阵运算的舍入累积,微小差异完全正常。工程上相对误差在 1e-5 量级可放心上线;若到 1e-2 量级,则需要排查数值类型和量化路径。

为何常见:人们默认“数学等价”就等于“位级相同”,忽略了浮点数在低精度下的表示误差。

误区“多 LoRA 能用一套模型服务十个任务,比合并高级多了,以后全部用热切换。”

正解热切换有代价:每个请求多一步适配器切换,显存要额外容纳已加载的适配器,且强依赖推理框架支持(vLLM / TGI / SGLang)。对单任务、高 QPS、延迟敏感的服务,合并后用标准架构部署更稳、更快、更省心。取舍应基于任务数、QPS、显存预算和上新频率,而不是“哪个听起来更酷”。

为何常见:新技术自带光环,容易让人忽略它的切换开销和框架依赖成本。

误区“权重一旦合并,LoRA 文件就作废了,以后想换任务只能重新微调。”

正解合并只是基于现有文件“生成”一个新权重文件,原始基础模型和 LoRA 文件都原样保留在磁盘上。你完全可以:用 LoRA A 合并出服务 A 的模型,再用 LoRA B 合并出服务 B 的模型;甚至先合并再解绑,随时切换。要换口味,重新煮一锅即可,原来的调料包并没有被烧掉。

为何常见:把“合并”类比成盐溶化进水这种不可逆的物理过程,忽略了文件层面的可复现性和可组合性。

面试题

Q1. LoRA 合并到基础模型的本质操作是什么?

单选题 · easy

  • A.将 LoRA 文件复制进模型目录
  • B.将增量矩阵叠加到基础权重上← 正确答案
  • C.重新训练一遍基础模型参数
  • D.将 LoRA 量化后保存为新文件

答案:B。合并的本质是数值叠加:W' = W + ΔW。文件拷贝只是“带着补丁”,推理时仍需动态计算;重训练和量化都与合并无关,选 B。

Q2. 线性合并的计算公式是?

单选题 · easy

  • A.W′ = W + ΔW← 正确答案
  • B.W′ = W × ΔW
  • C.W′ = W − ΔW
  • D.W′ = ΔW ÷ W

答案:A。线性合并就是把 LoRA 增量直接加到基础权重上,对应“把调料全倒进汤里”;乘法、减法、除法都不符合 LoRA 权重叠加的定义。

Q3. 以下哪种场景最适合“合并后部署”?

单选题 · easy

  • A.单一固定任务的线上服务← 正确答案
  • B.几十个任务需要随时切换
  • C.需要频繁更新任务方向的场景
  • D.共享显存的多租户推理场景

答案:A。面向单一固定任务、追求低延迟和部署简单,合并是最优选。多个任务切换、频繁上新、共享显存都是动态加载的典型场景,与热切换优势对应。

Q4. 关于缩放系数 α/r,下列说法正确的是?

单选题 · mid

  • A.它控制 LoRA 更新量的施加强度← 正确答案
  • B.它决定基础模型的网络层数
  • C.它影响训练数据的采样比例
  • D.它改变模型的词表大小和结构

答案:A。α/r 对应“放几勺调料”,决定 LoRA 增量在合并时被放大还是缩小。网络层数、采样比例、词表大小都由模型结构和训练配置决定,与缩放系数无关。

Q5. TIES 合并方法的正确步骤顺序是?

单选题 · mid

  • A.修剪 → 选举符号 → 合并← 正确答案
  • B.合并 → 修剪 → 选举符号
  • C.选举符号 → 合并 → 修剪
  • D.随机丢弃 → 缩放 → 平均

答案:A。TIES 先修剪(去掉幅度小的噪声),再选举符号(多数派决定方向),最后合并(只平均达成共识的部分)。选项 D 描述的是 DARE 合并,注意区分。

Q6. 关于 DARE 合并方法,下列说法正确的有?

多选题 · mid

  • A.随机丢弃大部分增量元素← 正确答案
  • B.剩余元素按比例放大← 正确答案
  • C.丢弃后期望值保持不变← 正确答案
  • D.丢弃比例越高效果一定越好

答案:ABC。DARE 的核心是“丢弃 + 缩放”:随机丢掉 90% 的增量,剩余乘以 1/(1-p),期望值不变(A/B/C 对)。丢弃比例并非越高越好,极端情况下会把有效信息全丢掉,D 错。

Q7. 合并后推理输出与带 LoRA 推理存在微小差异,主要原因是什么?

单选题 · hard

  • A.模型参数被随机重置导致能力下降
  • B.数值类型转换带来浮点精度误差← 正确答案
  • C.LoRA 权重被压缩后信息丢失
  • D.推理框架对模型做了有损压缩

答案:B。合并是数值运算,结果是确定的;微小差异主要来自 FP16/BF16 的舍入和反量化误差,就像调料翻炒时轻微氧化。参数重置、权重压缩、框架有损压缩都不是合并操作的固有行为。

Q8. 对 4bit 量化基础模型执行 LoRA 合并时,正确做法是?

单选题 · extreme

  • A.直接在量化权重上叠加 LoRA 增量
  • B.先反量化为高精度后再执行合并← 正确答案
  • C.将 LoRA 增量也量化成 4bit 整数
  • D.合并全程保持 INT4 精度计算

答案:B。4bit 量化权重是整数表示,LoRA 增量是浮点表示,两者不能直接相加——就像用“整包盐”和“几勺味精”没法精确配比。正确路径是反量化回 FP16 → 合并 → 按需重新量化;B 对。

Q9. 下列哪些推理框架支持 LoRA 动态加载?

多选题 · mid

  • A.vLLM 开启 LoRA 动态加载支持← 正确答案
  • B.Hugging Face TGI 动态适配器← 正确答案
  • C.SGLang 支持多 LoRA 调度← 正确答案
  • D.任何静态文件服务器都可以

答案:ABC。vLLM 用 --enable-lora 开启支持,TGI 提供动态适配器 API,SGLang 做了多 LoRA 调度优化。动态加载需要框架在运行时管理适配器权重,静态文件服务器只负责分发文件,无法在推理时叠加 LoRA。

Q10. 关于多 LoRA 热切换部署,下列说法正确的有?

多选题 · hard

  • A.基础模型权重可在多个适配器间共享← 正确答案
  • B.切换适配器需要重新加载整个模型
  • C.新 LoRA 上线无需重启推理服务← 正确答案
  • D.相比合并方案推理延迟必然更低

答案:AC。多 LoRA 共享一个基础模型,这是它省显存的核心(A 对);切换只需加载轻量适配器,不必重载基础模型(B 错);新适配器上传后即可服务,无需重启(C 对);动态加载有切换开销,延迟往往高于合并方案(D 错)。