显存不够还要跑大模型,这个话题几乎每个玩本地模型的都绕不开。你可能已经看到过各种“加载时直接塞爆显存”的惨案,也搜过不少省显存的手段。airllm 这个名字,最近在社区里出现的频率很高,靠的是一个反直觉的思路:它不把整个模型一次性装进显卡,而是把模型按层拆开,算完一层就丢弃一层,然后让显卡继续读下一层。这个思路听起来很朴素,实际用下来却真的能在普通家用机上跑起几十B级别的模型。
这篇文章就会带你从原理到实操完整过一遍 airllm,包括显存怎么省下来的、环境怎么搭、代码怎么跑,以及我踩过的那些坑。适合手里只有小显存显卡、又想体验大模型的玩家,也适合想搞懂“分层推理”到底是怎么回事的开发者。
1. 低显存跑大模型:airllm 的核心定位与适用场景
1.1 显存是本地推理的“命门”
本地跑大模型,第一道坎永远是显存。一个 7B 参数的模型,用 FP16 精度存储,权重文件本身就接近 14GB。也就是说,就算你不管激活值、不管 KV Cache,光是把权重塞进去,一张 8GB 显存的显卡就已经爆了。更别说 13B、70B 这种更大规模的模型,权重文件动不动几十GB甚至上百GB,普通消费级显卡看一眼就跑不动。
可能有人会说,现在很多模型都有量化版,4bit 量化之后 7B 模型能压到 4GB 左右,13B 模型大概 8GB,看起来似乎有救。但量化不是万能的,尤其是跑长上下文、大 batch 的时候,激活值和 KV Cache 会像涨潮一样把显存余量吃掉。更麻烦的是,很多你想用的模型压根没有现成的量化版本,或者量化后效果不符合预期。
这个时候,airllm 就提供了一个不太一样的选择:不再要求“显存装得下整个模型”,而是要求“显存装得下单层模型”。只要单层参数小于可用显存,理论上就能跑。
1.2 airllm 的设计目标和解决思路
airllm 是围绕“layer-wise inference”设计的推理框架,核心做法可以总结成八个字:算一层,丢一层。模型的全部权重并不需要常驻显存,而是放在系统内存里。真正在 GPU 上执行的只有当前正在计算的那一层。这一层算完,把输出结果保留下来,权重立刻腾出位置,再加载下一层。
这个做法等于把“显存不够”的问题,转换成了“内存够不够 + 带宽够不够”的问题。很多人的机器可能显卡一般,但 CPU 内存还是挺多的。比如一台 32GB 内存的机器,跑一个 7B FP16 模型,权重占 14GB,内存完全放得下。airllm 就是利用这一点,让显卡只当“计算车间”,而把内存当成“仓库”。
用生活里的例子来类比:你要在一张小桌子上组装一个大型书架,桌面放不下所有木板。airllm 的做法是,把木板按层堆在旁边,每次只拿一层上来装,装完一层就拿走,再换下一层。桌子小没关系,只要每次需要用的那层能放得下就行。
1.3 什么人适合用 airllm
根据我这段时间的体验,airllm 最适合下面几类场景:
- 显存只有 4GB 到 8GB,但又想跑 7B、13B 甚至更大模型的人。
- 对推理速度要求不高,主要做离线批量处理,比如给一批文本打标签、做摘要、生成结构化数据。
- 比较在意数据隐私,不想把内容传到云上,所有推理都必须留在本地机器完成。
- 手头有一台“老破小”机器,没有 GPU 或者 GPU 非常弱,想试试纯 CPU 推理。
不太适合的场景也很明显。如果你的目标是给线上用户提供即时对话服务,或者对单 token 延迟极其敏感,airllm 这种“边算边读权重”的模式会很吃力。它更像个“能用”,而不是“快用”。
2. 显存不够怎么办:airllm 分层推理的原理与容量计算
2.1 常规推理的显存都花在哪了
要理解 airllm 为什么省显存,得先搞清楚大模型推理时显存到底被谁占用了。以 decoder-only 架构为例,推理阶段的显存消耗主要由这几块组成:
第一是模型权重。这是最大的一块固定开销。模型有多少参数,乘以每个参数占用的字节数,就是权重大小。FP16 精度下每个参数占 2 字节,所以 7B 模型大约是 14GB,13B 模型大约是 26GB,70B 模型直接冲到 140GB。
第二是激活值。前向传播过程中,每一层都会产生中间结果,这些中间结果在层与层之间传递的时候,也要占用显存。激活值的大小和输入序列长度、batch size 强相关。序列越长,batch 越大,激活值就越大,甚至会超过权重成为主要瓶颈。
第三是 KV Cache。在自回归生成阶段,每生成一个新 token,都要把之前所有 token 的 Key 和 Value 缓存下来,用于计算注意力。这个缓存也会随序列长度线性增长。
第四是 CUDA context 和各种算子临时缓冲区。这部分看起来不起眼,但也会吃掉几百MB甚至上GB显存,在低显存设备上尤其致命。
常规推理框架的做法很简单粗暴:把模型权重连同激活、缓存一起塞进显存。塞得下就跑,塞不下就报错。
2.2 layer-wise:算一层、丢一层
airllm 的思路是,把传统推理中的“权重全程驻留显存”改成“权重按层流式进驻显存”。整个过程大致是这样的:
模型权重并不直接加载到 GPU,而是作为一个整体权重池存在于 CPU 内存。开始前向传播时,airllm 先读取第一层的权重,拷贝到 GPU 显存。GPU 用这层权重计算输入,得到第一层的输出激活。这一层算完,立刻释放显存中的这层权重。然后读取第二层权重,结合第一层的输出继续计算。如此循环,直到跑完所有层。
也就是说,GPU 显存里同时存在的权重,永远不超过单层权重的大小。这与全量加载方式相比,显存占用几乎只跟“单层参数量”有关,而不是跟“模型总参数量”有关。
很多第一次接触 airllm 的人会问一个问题:这样反复在 CPU 和 GPU 之间搬权重,是不是很低效?确实是低效的,但这正是 airllm 的设计取舍。它牺牲了速度,换取了极强的显存兼容性。对于显存只有 4GB 的设备,这是可能跑大模型的少数可行路线之一。
2.3 一张4GB显卡能跑多大模型
我先给一个粗略的估算公式。假设一个 70B 模型有 80 层(实际不同架构会有差异),那么平均每层参数量大约是 70B 除以 80,也就是 8.75 亿参数。用 FP16 存储,单层权重大约是 1.75GB。一张 4GB 显存的显卡,扣除 CUDA context 和系统预留后,实际可用显存可能只有 3.5GB 左右,剩下大约 1.7GB 可以给激活值和 KV Cache。只要序列不太长,这个方案是能跑起来的。
再看 7B 模型,假设 32 层,单层参数量约 2.19 亿参数,FP16 下单层权重约 0.44GB。这意味着哪怕显存只有 2GB,跑 7B 模型也有理论空间。很多人手里那种 4GB 显存的老卡,跑 7B 模型完全可以做到不爆显存。
当然这只是一个账面估算。实际运行时,注意力计算中的临时张量、算子内部缓冲区、模型输出层和 embedding 层,都会额外吃显存。而且 KV Cache 会随着生成长度不停增长。所以实际使用时要给余量,不要把显存算得刚刚好。
2.4 性能代价:拿带宽换显存
airllm 省显存的代价,是推理速度明显变慢,因为每层权重都要从 CPU 内存搬运到 GPU。尤其是自回归生成阶段,每生成一个新 token,都要重新执行一遍完整的前向传播,也就是要把所有层的权重再从头到尾搬运一遍。
用一个具体数字来感受一下。70B 模型的 FP16 权重有 140GB,在 PCIe 3.0 x16 的带宽下,实际传输速率乐观估计也就 12GB/s 左右。仅仅把权重完整读一遍,就要 10 秒以上。每生成一个 token 都来这么一次,生成 50 个 token 就是 500 秒以上,这个速度确实让人着急。
如果是 7B 模型,权重只有 14GB,传输一遍大约 1 秒多。虽然也不快,但至少是能接受的范围。这也解释了为什么 airllm 更适合中小规模模型,或者配合量化把权重体积压下来后再用。一旦上到 70B 这种级别,速度就会到让人怀疑机器的程度。
3. 环境准备与模型落地:安装、硬件要求和文件格式
3.1 硬件与系统环境建议
虽然 airllm 降低了显存门槛,但不代表随便一台机器都能跑得很好。我自己的体感和建议是:
显存方面,2GB 可以尝试跑 7B 级别,4GB 会比较从容,8GB 就可以尝试更大规模的模型,还能留一些余量给激活和 KV Cache。没有独显也可以跑,airllm 有纯 CPU 推理的路径,但速度会进一步下降,适合“只要能出结果就行”的场景。
内存方面,CPU 内存至少要能放下模型权重。7B 模型 FP16 权重 14GB,建议机器至少有 32GB 内存。如果跑 13B FP16 模型,至少要有 32GB 以上内存。如果内存不够,系统会动用 swap,性能直接崩盘。所以内存在这里不是“锦上添花”,而是“生死攸关”。
存储方面,强烈推荐使用 SSD。airllm 每次推理都要反复读取权重文件,机械硬盘的顺序读写可能还可以,但混合了多线程读取和系统缓存之后,IO 延迟会被明显放大。我第一次用机械硬盘跑的时候,生成速度慢到像卡死,后来把模型挪到 SSD 上才算能正常使用。
系统方面,Linux 下安装和运行最顺畅,Windows 也能跑,但可能出现路径分隔符、权限、缓存目录等问题。如果你有 Linux 机器,建议直接用 Linux。
3.2 安装 airllm 与虚拟环境
安装 airllm 本身不复杂,用 pip 就能完成。但有一点非常重要:一定要用独立的虚拟环境,不要直接往系统 Python 里装。
我在实际折腾中吃过亏。airllm 会自动拉取不少深度学习相关的依赖,如果和你机器上已有的框架版本冲突,很容易出现装完 import 报错,或者某个算子编译失败。虚拟环境可以把这个风险隔离掉。
python -m venv airllm-env source airllm-env/bin/activate pip install airllm安装过程可能会下载较多依赖,期间如果看到比较长的日志,不用着急。装完之后可以先确认一下版本是否正常,比如在 Python 里直接 import 一下。
安装过程中如果遇到网络问题导致下载失败,可以考虑把依赖包提前下载到本地再安装。具体的依赖清单可以直接从安装日志里看到。
3.3 模型来源、本地目录和缓存策略
airllm 支持直接加载本地模型目录。这个目录里一般需要包含模型权重文件、配置文件、分词器文件等完整内容。你可以从通用模型仓库下载,也可以把自己已有的模型目录直接传进去。
我建议所有模型都提前下载到本地,再用本地路径加载。不要写一行代码在线拉取,因为推理过程本身已经很依赖 IO 了,再叠加在线下载的不确定性,出了问题很难排查是网络原因还是模型原因。
airllm 第一次加载一个模型时,可能还会做额外处理,比如转换格式、建立缓存。所以第一次运行的时间通常会比第二次长很多。如果你第一次跑的时候等了很久没动静,先别急着杀掉进程,可以观察一下磁盘和 CPU 占用,看模型是不是正在被预处理。
模型目录的路径尽量用纯英文,不要带空格和特殊符号。这个建议通用,不是我随便说的。Windows 下带空格路径偶尔会引发读取异常,Linux 下虽然问题不大,但配合一些脚本工具时也可能踩坑。
为了离线环境,最好把模型文件、依赖包都准备成“全离线可用”的状态。这样即使之后断了网,程序也能正常加载和推理。
4. 从零跑通一个模型:最小示例与关键细节
4.1 最小可运行代码
airllm 提供了一个类似主流模型加载框架的接口,核心就是AutoModel。下面是一个最简示例,你可以直接复制修改:
from airllm import AutoModel MODEL_DIR = "/data/models/example-7b-chat" # 加载本地模型 model = AutoModel.from_pretrained(MODEL_DIR) # 分词器按模型生态的标准方式加载 tokenizer = load_tokenizer(MODEL_DIR) prompt = "写一句关于秋日的短句。" input_ids = tokenizer(prompt, return_tensors="pt").input_ids output_ids = model.generate(input_ids, max_new_tokens=64) print(tokenizer.decode(output_ids[0], skip_special_tokens=True))这里的load_tokenizer是我用来占位的写法,实际项目中你应该使用与你模型配套的标准分词器加载方式。重点是AutoModel.from_pretrained(MODEL_DIR)这一行,只要模型目录正确,airllm 就会接管后续的分层加载和推理。
generate方法的max_new_tokens参数建议一开始设置为 32 或 64。这样做有两个好处:一是生成时间短,方便验证流程是否通;二是生成越长,KV Cache 占用越高,显存更容易爆。
4.2 离线加载与断网运行
airllm 加载本地目录时,默认应该优先使用本地文件。所以你需要在准备模型目录时,把模型真正需要的所有文件都放进去。有些模型从仓库下载下来之后,可能是 split 成多个分片的,目录结构看起来比较散,不用合并,直接传目录就行。
完全断网的情况下,需要注意一点:不要使用任何会触发在线校验的参数或调用。保持模型目录完整,代码里传本地路径,推理时就不会有额外的网络请求。
还有一种比较隐蔽的情况:如果模型配置里引用了外部文件,而外部文件没有下载完整,加载时会报出一些奇怪的错误。遇到这种情况,先检查模型目录是不是完整,尤其是配置文件里指定的文件是否存在。
4.3 量化配置:让显存压力再降一档
如果内存或显存仍然紧张,可以考虑给 airllm 叠加量化。airllm 在部分版本提供了加载时的压缩/量化配置,例如把权重压到 4bit。权重变小后,不仅 CPU 内存压力下降,单层加载到 GPU 的体积也随之减小,显存余量会更多。
不过量化是有代价的。位宽越低,模型输出的质量波动越大。尤其是那些对细节敏感的任务,比如代码生成、数学推理,量化后可能出现明显的逻辑错误或者表达混乱。
我个人的建议是:先把不量化的版本跑通,记住它的表现。然后如果有必要,再开量化对比一下。如果任务对结果质量要求不高,只是想要个文本草稿,量化完全可接受;如果任务很严肃,建议保留更高精度。
具体参数名在版本之间可能有差异,使用前可以用help(AutoModel.from_pretrained)或查阅当前版本文档来确认。不要硬套网上老版本代码里的参数,报错时优先看接口签名。
4.4 新手上路最容易踩的三个坑
第一个坑是拿着在线仓库的模型 ID 直接加载。airllm 虽然可以处理在线 ID,但在网络不稳的情况下,你可能等半天只看到报错。最稳妥的办法是:先下载,再加载,始终用本地路径。
第二个坑是低估内存需求。有位朋友用 8GB 内存的机器跑 7B 模型,结果系统疯狂使用 swap,整个机器卡到几乎无法操作。airllm 虽然不要求显存大,但内存必须能装下权重。如果你内存明显不够,考虑用更小的模型,或者开量化。
第三个坑是同时运行太多占显存的程序。浏览器多个标签页、其他深度学习脚本,都会把显存吃掉一部分。airllm 虽然省显存,但又不是完全不占显存。跑之前用显存查看命令确认一下可用余量,省得莫名其妙的 OOM 中断。
5. 性能调优与进阶配置:让 airllm 跑得又快又稳
5.1 生成长度与上下文控制
airllm 的性能瓶颈主要在权重搬运,而不在计算本身。所以任何能减少前向传播次数的做法,都会直接缩短总耗时。最有效的参数就是max_new_tokens。
自回归推理每生成一个 token,都要重新走一遍全层前向传播。在 airllm 的架构下,也就等于重新读一遍所有层的权重。生成 100 个 token 和生成 1000 个 token 相比,后者光权重搬运就多花 10 倍时间。
如果你的任务允许,尽量把生成长度控制在 128 以内。需要长文本时,可以分段生成,每次生成一段,再把之前的内容拼进去继续生成。这样虽然不会减少总 token 数,但能在每一段之间做缓存和检查,降低中途 OOM 的概率。
另外,输入上下文越长,激活值和 KV Cache 开销越大。如果你的显存本来就紧张,不要一上来就灌几千字的上下文。先用短文本跑通,再逐步加长,找到自己设备的临界点。
5.2 batch size 与并发策略
airllm 这种流式加载机制,对 batch size 非常敏感。batch 越大,单层权重占用的显存不会变,但激活值会成倍增长。在低显存环境下,大 batch 很容易直接爆掉显存。
我自己实测下来的经验是:batch size 保持 1,永远是最稳的选择。即使你的显存看起来还有余量,也不要增加 batch。因为 airllm 的收益模型里,batch 增大带来的显存压力增加远大于吞吐提升,性价比很低。
如果你确实需要处理大量样本,比如批量给几百条文本做摘要,不要用大 batch,而是用一个循环逐条处理。每处理完一条,显存就释放一部分,不容易累积到爆。
5.3 存储与内存的取舍
硬盘类型对 airllm 体验影响极大。第一次我放在机械硬盘上跑 13B 模型,加载权重时磁盘狂转,生成速度惨不忍睹。后来换到 SSD 之后,体感明显改善。如果你的模型文件超过几十GB,尽量保证模型目录落在 SSD 分区。
内存方面,这里有一个容易被忽略的小技巧:如果你的模型权重是 FP16,内存占用基本等于文件大小。可以在加载前先看内存剩余量够不够。如果内存不够,系统开始 swap,那么无论 SSD 多快,整个推理速度都会被拖到不可用。
Linux 下可以观察内存占用曲线。如果发现进程占用内存一路上涨,而且磁盘读写在 swap 分区上有持续活动,那就说明内存已经成瓶颈了。这时候优先考虑换小模型或开量化,而不是继续加任务。
5.4 和其他推理方案的对比选型
我把常见的几种本地推理方案放到一起做个对比:
| 方案 | 显存要求 | 速度表现 | 适合场景 |
|---|---|---|---|
| 全量加载推理 | 很高 | 最快 | 显存充足、对延迟敏感 |
| 普通量化推理 | 中低 | 较快 | 显存有限、追求速度 |
| airllm 原始模式 | 很低 | 慢 | 显存极小、能接受离线速度 |
| airllm + 量化 | 极低 | 较慢但可用 | 内存和显存都不宽裕 |
| 云端 API | 无本地要求 | 取决于网络 | 不想本地部署、数据不敏感 |
这个表格不一定涵盖所有方案,但足够帮你做初步决策。如果你显存有 24GB,根本没必要用 airllm;如果你的显存只有 4GB,airllm 就是一个很实在的选择。
6. 踩坑实录:典型问题与排查方法
6.1 显存爆了:CUDA out of memory
这是最常见的报错之一。很多人的第一反应是“airllm 不是省显存吗,为什么还会爆?”原因往往不是权重,而是激活值和 KV Cache,也可能是 CUDA context 占用了太多显存。
排查思路很简单。先看看推理之前显存还剩多少。如果其他程序已经占了大部分显存,airllm 连单层权重都放不下。然后尝试把max_new_tokens降到 16,看看是否还爆。如果降了就不爆,说明是 KV Cache 或激活的问题。
如果你已经开了量化,仍然后台有一堆程序占显存,可以考虑临时关闭浏览器硬件加速、其他实验脚本,或者换一张显存更大的卡。不要指望 airllm 在“完全没有显存”的情况下工作。
6.2 速度慢到怀疑人生
airllm 本身就慢,但慢到像卡死,通常还有别的问题。最常见的是内存不足导致的 swap。系统一边读权重,一边换页,整个机器都跟着卡顿。第二个常见原因是磁盘读取慢,特别是机械硬盘。
排查方式是观察系统资源监控。如果 CPU 占用不高,但磁盘读取长时间接近 100%,说明瓶颈在存储。如果内存占用满了,磁盘读写还包含 swap 分区,说明瓶颈在内存。
另外,首次加载模型需要预处理和建缓存,那段时间可能长时间没有输出,并不是真卡死。可以先跑一个短生成任务,如果第二次跑明显比第一次快,那就是预处理导致的延迟。
6.3 模型加载失败或不支持的架构
airllm 不是能加载所有模型架构。一些比较新的架构、MoE 结构、或者改动很大的模型,在加载时可能出现兼容报错。这种情况大概率不是你的操作问题,而是框架的适配范围还不够。
解决的思路:第一,检查当前 airllm 版本,确认是否支持你要加载的模型架构。第二,尽量选择主流的 Decoder-only 架构模型,像是常见的 LLaMA 系、Mistral 系、Qwen 系,这类模型通常兼容性比较好。第三,如果非要用新架构,去项目仓库看看有没有相关进展。
遇到“模型加载错误”不要慌,先看日志。日志里如果出现明显的不支持某个算子或者某个属性,说明是架构适配问题,而不是环境问题。
6.4 生成质量变差怎么排查
如果你之前用全量加载推理时生成结果很正常,换成 airllm 后明显变差,先不要怪 airllm 分层机制。因为分层加载只影响显存占用,不改变模型精度,理论上输出和全量加载应该一致。
这时候先检查你是否开了量化。量化是影响质量的最大嫌疑。其次检查分词器是否匹配。感觉空气了些,但 mismatched tokenizer 确实会让输出变得奇怪,比如出现大量重复无意义的 token。最后检查生成参数,比如温度是否设得太高,可能导致文本发散。
如果你需要排除干扰,最直接的办法是不开量化、保持默认采样参数,跑一个短文本对比一下。如果输出仍然正常,那问题就出在量化或采样参数上。
6.5 常见问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| CUDA out of memory | 激活 / KV Cache / 其他进程占显存 | 减小序列长度、减max_new_tokens、关其他进程 |
| 速度极慢 | HDD、swap、首次预处理 | 换 SSD、确认内存足够、跑第二次对比 |
| 加载报错 | 架构不支持、文件不完整 | 更新版本、换主流架构、检查模型目录 |
| 输出乱码 | 分词器不匹配、量化过重 | 对齐分词器、降低量化强度 |
| 内存被吃满 | 权重占用太大 | 换小模型、开量化、加物理内存 |
这张表我尽量写的是高频问题。如果你遇到其他问题,建议把完整日志保留下来,搜索问题时直接搜关键报错信息,比搜“airllm 不工作”要有效得多。
7. 写在最后的实操心得
我在实际使用中最大的体会是,airllm 不搞什么花哨技巧,就是把“放不下”的问题变成“多跑几趟”。这个思路在面对显存瓶颈时,反而比各种花式优化更可靠。你把显存需求从百GB级压到单层几GB级,很多原本只能看别人跑的模型,自己也能上手试了。
最后分享一个小技巧。用 airllm 跑大模型之前,先在命令行里调用一次短文本生成,确认模型目录、分词器、显存余量都正常,再投入正式任务。不要一上来就长文本、大批量,否则第一次失败会让你分不清是模型问题还是参数问题。先跑通,再优化,这是所有本地推理工具通用的顺序。
airllm 不是万能的,它不能把 4GB 显卡变成 48GB 的顶级卡,但它给了你一个在低显存下真正摸到大模型的机会。如果你手头正好有台“跑不动”的机器,不妨按这篇文章的流程试一试。显存不够不是终点,换一种思路,家用电脑也能跑大模型。