一份基座,N 套人格:LoRA 热插拔的原理、接口与真实成本
一份基座,N 套人格:LoRA 热插拔的原理、接口与真实成本
如果你曾经为"想要三个不同风格/不同领域的模型"而准备下载三份模型文件,那这篇文章能帮你省下大量磁盘、显存和等待时间。
核心结论先给出:一份基座模型 + 若干个几十 MB 的 LoRA 适配器,可以在运行时自由切换甚至叠加,不重启、不重新导出、不复制大文件。 在生产环境里,这套做法已经有人把每月的 GPU 成本从约 2.4 万美元降到约 1200 美元。
一、LoRA 是什么:给大模型贴一张"专用贴纸"
微调(Fine-tuning)传统上指:拿一份预训练模型,用你自己的数据继续训练,得到一份完整的新模型文件。问题很明显——一个 8B 模型就是 16 GB,你想要三个风格,就要存三份 48 GB,跑起来还要三份显存。
LoRA(Low-Rank Adaptation,低秩适配)的做法完全不同。它发现了一个经验规律:模型从"通用"变成"某个专用领域",权重需要改变的部分其实很少。于是它不动原来的权重,而是额外学两个很小的矩阵。
用数学写出来非常简单:
原始输出: y = x · W
加上 LoRA: y = x · W + scale · x · A · B
其中 W 是原来那个巨大的权重矩阵(冻结、不动),而 A 和 B 是两个"瘦长"的小矩阵。它们的尺寸是刻意设计成很窄的——中间那个很窄的维度就是 rank(秩),通常取 16 到 64。
它为什么能这么小?举个例子:一个 8B 模型在 rank 16 下的可训练参数约 1500 万,而模型本身有 80 亿冻结参数——你只训练了不到 0.2% 的参数,最终产物就是几千万个数字,落到磁盘上大约 40 MB(rank 16)。
让 2B 当侦察兵:投机解码的原理、实测数据与失效边界
让 2B 当侦察兵:投机解码的原理、实测数据与失效边界
上一篇我们提到,2B 小模型最"值钱"的用法之一是给大模型当草稿机。这个技术的正式名字叫投机解码(Speculative Decoding)。它的效果相当戏剧化:有人实测把 27B 模型的生成速度从 25 tok/s 提到 45 tok/s,也有人实测把 60 tok/s 掉到 45 tok/s——用的是同一个技术。
本文面向没有推理优化背景的读者,先讲清"为什么能加速",再讲清"为什么有时反而变慢",中间穿插多组真实实测数据。
一、先搞懂一个反直觉的事实:大模型生成慢,不是因为算不过来
这是理解投机解码的唯一前提。
大模型生成文字是一个字一个字往外蹦的(术语叫自回归)。每蹦一个字,都要把整个模型的全部权重从显存里完整读一遍。以 27B 模型 Q4 量化为例,权重约 16 GB——也就是说,每生成一个字,就要搬运 16 GB 数据。
这带来一个关键结论:瓶颈在内存带宽,不在计算能力。显卡的计算单元绝大部分时间在空转等数据。用一张带宽 936 GB/s 的 RTX 3090 举例,理论上限就是 936 ÷ 16 ≈ 58 tok/s,无论这张卡的算力有多强。
所以加速的方向有两条:
- 减少要搬运的数据量(量化、用小模型)——代价是质量。
- 让空转的算力干点活——这正是投机解码的思路,且不损失任何质量。
二、核心思路:让便宜的小模型去"猜",让贵的大模型去"批改"
打个比方。大模型是个写文章很慢但水平很高的资深编辑,小模型是个打字飞快但水平一般的实习生。
- 传统方式:编辑一个字一个字亲自写,写一个字想一下,慢。
- 投机解码:实习生先一口气猜出接下来 5 个字,编辑拿过来一次性扫一眼——“前 3 个对,第 4 个不对”。于是前 3 个字直接采纳(几乎白赚),第 4 个字由编辑自己改,第 5 个字扔掉重来。
关键在于:编辑一次性检查 5 个字的成本,和检查 1 个字的成本几乎一样。因为权重的搬运量在一次前向传播里是固定的,检查 5 个 token 只是把这条数据多算几遍而已——而算力本来就是空闲的。
别把 2B 当小号 GPT-4:llama.cpp + Qwen 2B 的六种实战玩法
别把 2B 当小号 GPT-4:llama.cpp + Qwen 2B 的六种实战玩法
在本地跑一个 2B 级别的模型(Qwen2.5-1.5B、Qwen3-1.7B 这一类),最常见的失望是「它连个像样的回答都给不出来」。于是很容易得出一个结论:小模型没用,还得上 32B 甚至 72B。
这个结论错在前提。2B 的定位从来不是"小号 GPT-4",而是流水线里一个廉价、快速、输出确定的专用零件。
在一台普通笔记本上,Qwen 2B(Q4_K_M 量化)的生成速度可以轻松跑到 30 tokens/s 以上,显存占用不到 2GB。这意味着它一秒能吞下几十条请求。真正的技术含量不在于它有多聪明,而在于你如何把任务切到它擅长的那一格。
一句话概括本文的判断:不要让小模型去"思考",要让它去"填空"。
先确认你的跑法
下文所有玩法都建立在 llama-server 上——它提供 OpenAI 兼容接口,任何现成的 SDK 都能直接接。一个够用的启动命令:
./llama-server \
-m models/Qwen2.5-1.5B-Instruct-Q4_K_M.gguf \
-c 4096 \
-np 8 --cont-batching \
--jinja \
--host 127.0.0.1 --port 8080
几个参数值得单独说明:
| 参数 | 作用 |
|---|---|
-c | 上下文长度,小模型不要盲目开大 |
-np --cont-batching | 并行槽位 + 连续批处理,是批量吞吐的关键 |
--jinja | 启用模型自带对话模板,工具调用能力依赖它 |
-ctk q8_0 -ctv q8_0 | KV Cache 量化,长上下文时能省一半显存 |
--mmproj xxx.gguf | 加载视觉投影层,进入多模态模式 |
玩法一:语法约束输出器 —— 最高杠杆的一招
llama.cpp 原生支持 GBNF 语法与 JSON Schema 约束解码。这是它相对于 vLLM、Ollama 最显著的差异点,也是让 2B 从"勉强能用"跳到"近乎可靠"的关键。