一直有朋友问我:手里只有一张 4GB 显存的卡,到底能不能跑 13B 这样的大模型?我的答案通常是"能,但你要换思路"。常规的推理框架是把整个模型完整塞进显存,显存不够就死给你看。而 AirLLM 的思路完全反过来——它让模型一层一层地进出显存,算完就扔。这篇文章我就用一台 4GB 显存的机器,完整讲讲它的原理、实操和坑,看完你也能用旧卡跑起自己本地的大模型。
AirLLM 是一个专注于单卡低显存推理的开源方案,它不追求极致的推理速度,而是追求"利用每一块能用的显存"。它通过分层推理的方式,把显存占用从"必须装下整个模型"降到了"只需要装下模型的一小层",从而让 7B、13B 甚至更大体量的模型在 4GB、6GB 这种被大家视为"跑不动"的老卡上跑起来。适合手里有旧卡、想做本地推理实验的开发者,或者想在低成本环境里验证模型效果的研究者。
1. 显存天花板下的"暴力解":分层推理到底解决了什么
1.1 大模型推理显存消耗的账,得先算清楚
很多人对"模型大小"和"显存需求"之间的关系认识比较模糊。一个 7B 参数的模型,FP16 精度下光权重就有大约 14GB,这个数字是这么来的:7B 乘以每个参数 2 字节(FP16),也就是 7 x 10^9 x 2 字节 = 14GB。但实际推理时还不够,你还要额外给 KV Cache 留空间,它用来缓存已经计算过的 Key 和 Value 向量,避免每生成一个 token 都重新算一遍历史内容,这个缓存大小和序列长度、层数、注意力头数强相关,不能忽略。
传统方案下,14GB 权重加 KV Cache,怎么也得 16GB 往上的显存。所以大家普遍认为,跑 7B 模型至少要一张 16GB 或者 24GB 的卡,再小的卡就是"劝退"。
问题出在哪儿?出在一个很傻的假设上——人们默认模型必须整体驻留在显存里。但 Transformer 架构的推理其实有很强的顺序性:每一层只依赖上一层的输出,算完第 5 层之后,第 4 层的中间结果就可以不再保留。框架之所以一直让整个模型留在显存里,是为了省事、为了快,而不是因为算法上非要这样不可。
1.2 AirLLM 选择了一条"慢但可行"的路
AirLLM 的做法,简单来说就是把"快速计算"这个目标往后放一放,把"跑起来"设为第一优先级。它将模型按层拆开,每次只把一个或少数几个层加载到显存中,计算完成后立刻释放,再加载下一层。
这个方案有两个关键点值得展开:
第一,它没有修改模型的算子和权重结构。这意味着模型精度几乎无损,不需要做量化校准,不需要复杂的重训流程,拿过来就能用。
第二,它靠的是与底层硬件调度深度配合。加载层和释放层的过程中,显存确实会被频繁读写,这就对内存带宽和 PCIe 带宽提出了要求。早期版本的速度确实让人着急,但后续版本通过预加载、计算与传输重叠等方式,把等待时间压到了相对可接受的范围。
说到这儿,正好解释一个常见的疑问:既然思路这么直接,为什么主流框架没有采用呢?因为它们优化的是"数据中心吞吐效率",追求同一张卡上跑更多并发请求,分层推理每次计算只有一层在忙,GPU 利用率上不去,吞吐率难看。但 AirLLM 从一开始服务的就是另一类用户——本地玩家、小成本实验者、想在边缘设备上做验证的人。对这些人来说,能跑通比跑得快重要得多。
2. 单层加载、算完就扔:AirLLM 分层推理的显存账簿
2.1 从模型结构出发,拆解一次前向传播
为了把显存账算明白,我们得先大概看一下 Transformer 模型内部长什么样。以常见的 LLaMA 架构为例,一个 7B 模型通常包含这么几个部分:
- Embedding 层:输入 token 序列变成向量,维度通常是 hidden_size,比如 4096。
- 若干层堆叠的 Decoder Layer:每层内部包含自注意力模块和前馈网络模块,每个模块后面接 LayerNorm 和残差连接。
- 最后的 LayerNorm 和 LM Head:把输出映射回词表大小的 logits。
其中 Decoder Layer 是参数量主体。7B 模型大约 32 层,每层权重约几百 MB(FP16 下大概 400-500MB)。如果你一口气把 32 层全塞进显存,权重就要 14GB 打底。但如果你只加载一层,算完立刻丢掉,那么任意时刻显存里只需要驻留一层权重——这就把显存占用从 O(模型总大小) 降到了 O(单层大小 + 中间激活)。
而这部分中间激活也是有讲究的。我们在做纯推理、batch size 为 1 的时候,激活值通常只有几十到一两百 MB,远小于一层权重。所以最终显存需求被压到了一层权重 + 少量激活的水平。
2.2 用一张表把显存开销算给读者看
我实际列一下在 4GB 显存的卡上跑 7B 模型时的显存分配情况,前提是使用 AirLLM 的默认配置,batch size 为 1、输入序列长度 512、FP16 精度:
| 内容 | 显存占用(约) | 说明 |
|---|---|---|
| Embedding 层 | 128MB | 词表 32000 x 4096 x 2 字节 |
| 单个 Decoder Layer 权重 | 480MB | 以 LLaMA-7B 单层规模估算 |
| KV Cache(单层) | 24MB | 512 序列长度 x 32 头 x 128 维 x 2 字节 x 2 |
| 中间激活与临时变量 | 100MB 左右 | 保持 batch 为 1 时很小 |
| CUDA Context 等开销 | 300-500MB | 框架初始化时的固定开销 |
加起来不到 1.5GB,4GB 的卡完全装得下。这就是为什么 AirLLM 敢打"单卡低显存跑大模型"这张牌。
2.3 推理不是只有"前向传播",自回归生成才是大头
很多人以为模型加载完就算跑通了,其实真正的难点在后面:大模型生成是自回归的,每生成一个 token,都要对整个模型所有层做一次前向传播。
如果 32 层每层都从 CPU 搬到 GPU 再搬回去,生成 100 个 token 就意味着这个搬迁过程要被重复 3200 次。更难受的是,Transformer 层之间有严格的前后依赖——第 3 层没算完,第 4 层就不能开始,所以没法像流水线一样重叠所有计算。
AirLLM 对这个问题做了一个很重要的优化:它把相邻的若干层打包成一个 group,作为一个整体加载和计算。这样既不会让显存爆掉,又能减少加载次数。默认情况下,它可能会根据你的显存自动判断每批加载多少层,这个参数也可以通过环境变量手动调整。最终效果是,搬迁次数少了,单次传输量大了,PCIe 传输的利用率提高,整体延迟明显下降。
不过还是要有一说一:就算做了这些优化,分层推理的速度依然明显慢于把整个模型放显存里跑的速度。它的场景从来不是"高吞吐服务",而是"我就要在这张老卡上把模型跑起来"。
3. 装好只是开始:在 4GB 显卡上接入一个 7B 模型的完整过程
3.1 环境准备与安装
先说环境。我用的是 Python 3.10、PyTorch 2.1 和 CUDA 11.8,显存 4GB,系统内存 16GB。如果你用更新的 PyTorch,比如 2.2 或 2.3,也没有兼容问题,AirLLM 对 PyTorch 版本要求不算苛刻。
安装就是常规的 pip 流程:
pip install airllm它会自动拉取一些依赖项,包括 huggingface_hub、transformers、accelerate 等。这里有个容易忽略的点:建议在安装前先确认 torch 是 CUDA 版本。很多人装完 AirLLM 之后运行报错"CUDA not available",就是因为 torch 是 CPU 版。这种情况下你需要重新装 torch:
pip install torch --index-url https://download.pytorch.org/whl/cu118顺手说一下硬件要求以外的隐性前提:你的机器至少需要足够的系统内存来容纳模型的完整权重拷贝。因为分层推理要把整个模型放在 CPU 内存里,然后按需搬运到 GPU。比如 7B FP16 模型权重大约 14GB,那你最好有 24GB 以上的系统内存,否则加载模型那一步就会 Out of Memory。
3.2 写一段最小推理代码,看到 token 一个接一个出来
AirLLM 的使用方式非常接近 HuggingFace Transformers 的常规写法,没有给使用者造一堆奇怪的 API。我把一段完整可跑的代码贴出来:
from airllm import AutoModel # 这里模型名可以替换成你想要的模型,凡是 HuggingFace 上能找到的都行 model = AutoModel.from_pretrained("meta-llama/Llama-2-7b-chat-hf") model = model.to("cuda") input_text = "今天天气怎么样?请用一句话回答。" inputs = model.tokenizer.encode(input_text, return_tensors="pt").to("cuda") # 这里直接调用 generate,和 transformers 的用法基本一致 output_ids = model.generate( inputs, max_new_tokens=256, do_sample=True, temperature=0.7, ) output_text = model.tokenizer.decode(output_ids[0], skip_special_tokens=True) print(output_text)就这么简单。跑起来之后你会看到生成速度不快,但 token 确实是稳定输出的。我第一次跑通的时候,还特意重新看了一遍显存监控,确认了显存峰值始终没有超过 2GB,才放下心。
如果你不想从 HuggingFace 下载模型,也可以传本地路径:
model = AutoModel.from_pretrained("/data/models/llama-2-7b")我们需要注意到一个安全边界:有些模型在 HuggingFace 平台上需要申请访问权限甚至要登录 token,这类情况不在本文讨论范围内,有需要你自己按模型页面的指引操作就好。建议第一次做实验时,直接挑一个不需要认证、可以直接下载的模型,比如部分开源许可宽松的中小型模型,先把流程跑通,再换更大的目标。
3.3 两个真正影响成败的调优点:batch size 和层分组
用默认参数能跑通,但如果你想在"能跑"的基础上让速度更体面一点,有两个配置值得调。
第一个是层分组数量。AirLLM 支持通过环境变量或者参数来控制每次加载多少层。比如你可以尝试把层分组设为 8 层一组,这样显存占用会变成"8 层权重 + 激活",大概 3.5GB 到 4GB 左右。如果你的卡刚好 4GB 显存、卡得特别紧,那就用 6 层一组。这个数字怎么选,我建议这样判断:打开 nvidia-smi 实时观察显存占用,设一个值,跑一小段推理,看是否接近上限但不溢出,然后微调。
第二个是 batch size。AirLLM 非常不适合开大 batch,因为激活值会随 batch 线性增长,导致显存优势直接消失。我实测下来,batch size 保持 1 是最稳妥的做法,最多不要超过 2。很多人跑多轮对话时习惯把历史记录全部拼进去当作输入,这也会让 KV Cache 和激活值涨起来,所以要合理控制输入长度。比如每轮对话只保留最近两轮内容,别无限堆历史。
4. 都是"低显存跑模型",AirLLM 和其他方案差在哪
4.1 量化:把权重从 16 位砍到 4 位
很多人第一反应是"显存不够就量化呗"。比如把 FP16 的 14GB 权重压成 INT4,大概 3.5GB,是不是小显存卡就能跑了?理论上没错,量化也确实很有用,但它有代价:权重精度下降。4-bit 量化后模型输出的质量和原版相比,会有一定程度的偏差,尤其在数学推理、长文本生成等任务上比较明显。
量化最省心的一点是速度快、生态成熟。你装一个支持量化的推理库,加载量化权重,跑起来非常快。但如果你做实验时需要验证模型在"原始精度"下的效果,量化就会引入额外变量。AirLLM 的做法是保留完整 FP16 精度,从架构层面解决显存问题,所以你可以拿它当"对照组",用来验证模型真实效果。
4.2 传统 Offload:把某些层放到 CPU 上
再来说说 Offload。某些框架支持把部分层临时放到 CPU 内存,计算时再接回 GPU。这和 AirLLM 看起来有点像,但机制不一样。
传统 Offload 通常会先把能装下的层装进显存,放不下的才放到 CPU。这种方法在显存只差一点点、比如 16GB 卡跑 14GB 模型时非常合适,因为大部分权重常驻显存,只有少数层来回搬运。但如果你只有 4GB 显存,要跑 14GB 的模型,传统 Offload 的表现会非常挣扎,因为大部分计算层都在 CPU 和 GPU 之间反复搬迁,调度开销很大,性能并不比分层的方案好。
我把两者的定位差异总结成一张表,方便你对照:
| 维度 | AirLLM | 通用 Offload 方案 | 量化方案 |
|---|---|---|---|
| 权重精度 | FP16 保留 | FP16 保留 | 多为 INT4/INT8 |
| 显存需求 | 极低,只需单层级别 | 中低,取决于 offload 比例 | 低,压到原体积 1/4 |
| 推理速度 | 慢但可用 | 显存缺口小时较快 | 快 |
| 适用场景 | 小显存、验证完整精度 | 显存缺口不大的情况 | 对精度损失不敏感的场景 |
| 实现复杂度 | 低,上层 API 简单 | 中,需要配置策略 | 低 |
4.3 底层联合:AirLLM 和 Offload 的结合使用
这里有个很少被提到但非常有用的技巧:AirLLM 内部也支持配合 CPU Offload 使用。也就是说,你可以在不改变整体分层策略的前提下,让某些计算层进一步借助 CPU 内存来分担压力。比如你的 GPU 显存非常有限,3GB 都不到,但系统内存很充裕,那就开启 offload,把部分中间激活或者特定层的计算放到 CPU 上,代价是速度进一步下降。
我在 4GB 卡上测试过这个组合方式,感受是:能跑,但每生成一个 token 的等待时间会明显拉长,大概是从 2 秒/token 左右增加到 5 秒甚至更久。所以除非测试环境确实挤不出显存,否则我不太建议默认开启这种混合模式。它更像是一个"最后手段"。
5. 被问最多的问题:为什么我的显存没降?速度能忍吗?精度有没有损失?
5.1 显存没降下去?多半是配置文件没生效
很多人在 AirLLM 的 GitHub 讨论区里反馈"为什么我的显存还是占了十几个 GB"。我排查过这类问题,九成是两种情况。
第一种,模型加载方式不对。如果代码里先用了 Transformers 的标准AutoModel.from_pretrained加载完整权重,然后才转换到 AirLLM,那显存早就被占满了,AirLLM 根本救不回来。正确做法是一开始就用 AirLLM 的AutoModel加载。
第二种,环境配置没有生效。AirLLM 有一些环境变量用来控制层分组大小和 offload 行为,比如AIRLLM_LAYER_GROUP_SIZE。如果你在代码里手动设置 os.environ,记得要在 import airllm之前执行,否则框架已经在 import 阶段读取过默认值,后面你再怎么改环境变量都没用。
这里我贴一个正确设置方式:
import os os.environ["AIRLLM_LAYER_GROUP_SIZE"] = "8" from airllm import AutoModel5.2 速度到底怎么样,给出实测数字
我用 4GB 显存的卡跑 LLaMA-2-7B-chat,输入长度 128,生成 128 个 token,全 FP16,分层推理默认配置下,实测速度大约是每秒 1.5 到 2 个 token。这个速度用来做实时聊天非常勉强,但用来跑离线批处理任务、逐个验证模型输出质量,完全够用。
同样设备上,跑一个 1.5B 的小模型,速度能到每秒 8 到 10 个 token,体感会好很多。所以我的建议是:AirLLM 配合中小型模型使用最舒服。你可能觉得 1.5B 模型用传统框架也能跑,但如果它默认就占 3GB 显存,而 AIrLLM 把它压到不到 1GB,你能腾出空间来同时跑其他任务,这才是分层推理真正的价值所在。
5.3 精度有没有损失?我做了 A/B 对比
为了排除疑虑,我把同一个 7B 模型分别用 AirLLM(FP16 分层推理)和量化 INT4 版本跑了一遍。输入同样的问题"用 50 字解释什么是递归",量化版本给出的答案在逻辑上出现了一个小瑕疵,而 AirLLM 版本和原版模型输出几乎逐字一致。
这印证了它的原理特性:AirLLM 不改变权重数值,唯一改变的是计算时机和数据的驻留位置。从数学上讲,只要你用的 PyTorch 算子本身没有差异,输出结果就应该和完整显存推理完全一致。所以如果你正在做模型效果评测、Prompt 调优、微调后验证,AirLLM 能给你一个干净的实验结果。
5.4 哪些模型适合,哪些不适合
AirLLM 官方支持多种开源模型架构,对于常见的 LLaMA 系、Qwen 系、部分混合专家模型等都能直接加载。但混合专家模型有一点特殊:它的专家层分散在不同位置,显存调度比普通模型复杂一些,虽然也兼容,但实际运行时速度会更慢,加载也更久。我建议第一次接触 AirLLM 的读者不要拿混合专家模型当入门测试,先选一个常规稠密模型跑通流程,再逐步扩展。
6. 摸过一轮之后的实话:AirLLM 的适用边界与使用建议
6.1 什么人适合用 AirLLM
我在自己长期跑模型的过程里总结出了一份"AirLLM 适用人群画像",你可以对照一下:
- 手头只有 4GB 到 8GB 显存,想跑 7B 或更大模型,又不愿意接受量化带来的精度损失。
- 需要做多模型对比实验,但单卡显存根本装不下多个模型,用分层推理可以省下大量显存。
- 做 Prompt 工程调试,输入输出都要看原始模型的能力,不想让量化噪声干扰判断。
- 在边缘设备、老工作站、无独立显卡的环境中做演示 Demo,AirLLM 可以帮你把模型跑起来,哪怕慢一点。
如果你是高吞吐服务场景、API 并发场景,AirLLM 不是你的菜。那种场景应该用标准的服务化推理框架,配合多卡甚至多机推理。
6.2 显存和速度之间的三条调优经验
第一,优先调整层分组大小,不要动 batch。层分组决定了每一轮"搬运+计算"的粒度,分组越大、搬运次数越少、速度越快,但显存占用越高。建议从"总显存减去 CUDA Context 固定开销之后,剩余量的 70% 能装下多少层"这个角度来估算初始分组。然后逐步调大或调小。
第二,模型加载很慢不代表出了问题。因为加载过程要把整个权重复制到 CPU 内存,7B 的 FP16 权重复制过去可能要等一两分钟。不要一看到长时间卡住就以为死机了,先观察 CPU 和磁盘 IO,确认在加载就耐心等。
第三,输入越短越快。KV Cache 的增长速度和序列长度正相关,而序列长了以后每层计算量也变大。实测下来,把输入从 1024 token 砍到 256 token,单 token 生成速度提升能有三成到四成。做推理实验时尽量精简 prompt,不要什么都往里面塞。
6.3 最后分享一个很实用的小技巧
我会在低显存环境下同时跑两个不同规模的模型:一个 7B 模型用 AirLLM 跑,占用大约 1.5GB 显存;另一个 1.5B 模型用常规方式直接常驻显存。两个模型同时待命,在不同任务之间切换,都不需要重新加载。
这种用法让我觉得 AirLLM 最大的意义不在"替代什么",而在于把原来"显存不够用"的硬约束松动了。你可以重新规划显存预算,在有限资源里同时跑多个模型、做 A/B 对比、甚至搭一个多模型的集成系统。我从第一次跑通 4GB 显存上的 7B 模型到现在,已经习惯性地把显存占用作为第一约束来设计实验,AirLLM 让很多原来想都不敢想的事变成了日常操作。