AirLLM:单张显卡也能跑70B大模型的分层推理显存优化方案

发布时间:2026/10/18 16:45:39
AirLLM:单张显卡也能跑70B大模型的分层推理显存优化方案
1. AirLLM 到底是什么一个把“大模型塞进小显存”的暴力优雅方案QLoRA、GPTQ、GGUF这些名字想必大家都不陌生。这几年为了在消费级显卡上跑大模型各路方案层出不穷核心思路无非两种量化压缩模型权重或者把模型拆分到 CPU 和 GPU 上协同计算。而 AirLLM 走了一条完全不同的路它不压缩任何东西不拆半分显存给 CPU而是按照模型本身的层结构一层一层地算算完一层就把结果存到磁盘释放显存再加载下一层继续算。我在第一次看到这个项目的时候第一反应是“这也能行”做过深度学习的人都知道PyTorch 训练和推理时整个模型图会被加载到显存里权重、激活值、中间计算图全都堆在一起一个 70B 的模型光权重 fp16 就要 140GB 显存普通人有几张 A100AirLLM 的做法说白了就是“把计算图拆了每次只让一小部分驻留显存”。它的核心价值非常明确让单张普通显卡也能跑超大参数模型。官方给出的演示里单张 4GB 显存的卡就能推理 70B 级别的 Llama 模型这放在一年前是难以想象的。我实测下来这个项目真正适合三类人第一类是显存不大但有探索欲望的个人开发者比如手里只有一张 8GB 或者 12GB 游戏显卡的人想研究 70B 模型的输出风格、做 prompt 调试、跑一些离线推理实验AirLLM 是目前门槛最低的入口之一。第二类是需要在受限硬件环境做模型验证的团队有时候云厂商给的开发机显存有限但又有临时跑大模型的刚需AirLLM 可以在不改动服务器配置的前提下完成验证。第三类纯粹是技术爱好者想知道“不量化、不蒸馏、不投机取巧靠工程手段把显存占用压下来”是怎么做到的这个项目的设计思路本身就很有学习价值。它解决的问题就是两个字显存。过去你想跑一个 70B 模型要么花钱租 A100/H100要么下载量化到 4bit 的版本用 CPU 慢慢磨。AirLLM 提供了一条中间路径而且整个使用体验可以做到像普通的 transformers 推理一样简单几乎是无感切换。2. 设计思路拆解分层推理为什么这么绕却很有效2.1 先搞清楚显存到底是怎么被“吃”掉的在理解 AirLLM 之前得先搞清楚传统推理里显存都花到哪里去了。以大模型推理为例显存的占用大致分为几块首先是模型权重本身一个 7B 的模型用 fp16 存储大概是 14GB70B 就是 140GB这一部分是最硬的成本其次是激活值也就是每层 Transformer 计算过程中产生的中间张量这部分和序列长度、batch size 直接挂钩虽然单层看起来不大但几十层积少成多也很可观再有就是框架本身的开销比如 CUDA context、推理缓存、PyTorch 的计算图管理等等。传统方案是怎么应对的量化是第一条路把 fp16 换成 int8 或者 int4权重显存直接按比例缩减代价是精度损失虽然现在有各种奇奇怪怪的量化算法把它做得很接近原版但严格来说还是有损的。第二条路是 offload把一部分权重放到 CPU 内存甚至磁盘上用的时候再换进去这个方案本质上是用带宽换显存CPU 到 GPU 的 PCIe 带宽是有限的所以跑起来会比较慢。AirLLM 的思路完全不同。它观察到 Transformer 模型的结构特点层与层之间是顺序依赖的上一层的输出是下一层的输入中间没有任何跨层的信息交互。那么理论上我完全可以只保留一层的权重在显存里算完这一层把输出结果保存下来然后把这一层的权重清掉加载下一层的权重继续算。这个思路说起来简单但工程实现有很多坑要踩。比如计算图怎么动态构建梯度怎么管理每层的输入输出怎么衔接AirLLM 把这些都封装好了用户不需要关心这些细节但实际上它的核心实现是在 PyTorch 的模块层面做了拦截把通常的一次性 forward 改造成了逐层的 forward 循环。2.2 Layer-wise 怎么做到“只用一点点显存”AirLLM 的推理流程和传统推理有明显的区别。传统模式下你调用 model.generate()整个过程在 GPU 端一气呵成。AirLLM 模式下整个过程变成了一个循环第一层加载到 GPU输入 embedding 结果喂进去得到第一层输出立刻把这个输出拷贝到磁盘或者内存缓冲区然后把第一层权重从显存里释放。第二层加载读取第一层输出继续前向计算再存第二层输出再释放。这样循环往复直到最后一层计算完毕得到最终的 logits。每一步的显存占用是多少呢只需要容纳一层 Transformer 的权重加上当前的激活值即可。一个 70B 模型通常有个几十层每层权重平均下来可能只有几个 GB再加上激活值和临时缓冲总显存压力就从“整个模型”降到了“单层模型”的量级。这就是为什么 4GB 显存都能跑 70B 模型的原因——单层权重可能只有 3GB 不到剩下的空间装激活值绰绰有余。但代价也很明显磁盘 I/O 和加载耗时成了新的瓶颈。每算一层都要从磁盘读取几个 GB 的权重而且每个 token 的生成都要完整走一遍所有层假设模型有 80 层每层权重 2GB那生成一个 token 至少要读取 160GB 的权重数据。这个 I/O 开销是巨大的所以 AirLLM 的速度注定不会很快。这是它架构上的本质特征不是优化不到位而是方案本身的取舍。2.3 沙盒文件系统与中间结果管理AirLLM 里有一个容易被忽略但非常重要的组件沙盒文件系统。因为分层推理会产生大量的中间结果文件如果随便写到磁盘一方面机箱里的可用空间可能不够另一方面大量小文件的读写会拖慢 I/O 性能甚至造成磁盘碎片。沙盒文件系统的作用是把这些中间结果管理起来在推理完成后自动清理不让垃圾文件污染你的磁盘。还有一个细节是中间结果不一定非要写磁盘。如果你有足够的内存AirLLM 也可以把中间结果放在内存里这样速度会快不少因为 RAM 的带宽远高于磁盘。但是这个项目最初的定位是“单卡低显存运行”所以它优先考虑了磁盘存储的方案毕竟很多低配环境中内存也不算宽裕。3. 环境准备依赖、安装与显存评估3.1 硬件和系统要求AirLLM 的硬件要求真的不高但有几个点需要提前明确。首先必须有一块 NVIDIA 显卡且安装了相应的 CUDA 环境。AMD 显卡理论上可以通过一些兼容层来跑但没在官方支持范围内建议不要折腾。其次显卡显存不是硬性指标从官方和我自己的测试来看4GB 以上就能跑8GB 可以比较从容16GB 以上就能覆盖绝大多数的使用场景了。系统环境方面官方推荐 Linux。Windows 下能跑但也确实更容易踩一些环境坑主要是 CUDA 和 PyTorch 的版本匹配问题。我是强烈建议用 WSL2 的几乎可以当作原生 Linux 来用而且和 Windows 文件系统的互操作也比较方便。磁盘空间方面需要预留一些余地因为一个 70B 模型的原始 fp16 权重文件就有大概 140GB即使是量化版本也有几十 GB模型加上中产结果缓存建议至少预留模型文件大小 1.5 倍以上的空间。这点经常被人忽略等跑起来才发现磁盘不够就有点尴尬了。3.2 安装过程与依赖安装还是比较简单的本质上就是装一个 Python 包。建议先创建一个干净的虚拟环境Python 版本要求在 3.9 以上实测 3.10 和 3.11 都比较稳。pip install airllm如果网络环境不太好建议用国内镜像源加速。装完之后最好验证一下依赖是否完整主要的依赖包括 torch、transformers、huggingface_hub 等如果之前已经装过可能版本冲突建议在虚拟环境里重新装一遍比较省心。你可能会遇到几个小坑比如 torch 和 CUDA 版本不匹配导致导入时报 CUDA 相关错误这个需要根据你的显卡驱动先定好 CUDA 版本再装对应的 torch 版本。我的习惯是conda create -n airllm python3.10 conda activate airllm pip install torch --index-url https://download.pytorch.org/whl/cu118 pip install airllm这样能保证 torch 和 CUDA 是配套的省去后面老出一些说不清道不明的报错。3.3 选模型之前先算显存账选定模型之前最好先心里有个预期。AirLLM 虽然能大幅降低显存需求但不同模型对显存的要求还是有差异的。核心变量是单层权重大小和层数。7B 模型可能单层只有 200MB13B 模型大约 300-400MB70B 模型单层可能达到 2-3GB。再加上激活值、注意力计算中的 KV cache以及框架的临时缓冲你可以粗略估算实际推理所需显存 ≈ 单层权重大小 × 2 到 3 倍。实操中我遇到过一种情况4GB 显存跑 70B 的量化版本可以但换成 fp16 版本就报 CUDA out of memory。原因就是 fp16 的单层权重更大加上激活值就超了。所以如果显存确实非常有限建议优先选择量化过的版本即使 AirLLM 本身不依赖量化但量化版本的权重复制到显存时会更小间接降低了每层的显存峰值。大致的对应关系可以做成一个参考表模型规模典型层数单层权重(f16)建议显存7B32约 0.4GB2GB 可用13B40约 0.6GB3GB 可用33B60约 1GB4GB 可用70B80约 2.5GB8GB 可用这只是粗略参考实际运行时 KV cache、输入长度和 batch 都会影响最终显存占用。所以我在跑新模型时习惯先输入一段短文本跑一下看看显存峰值再决定能不能加长输入或增大 batch。4. 实操单卡跑 70B 模型全流程4.1 基础推理写一段最简单的推理代码AirLLM 的使用方式和 HuggingFace transformers 高度相似。定义一个模型类指定模型路径和显存配置然后调用 generate。我自己最常用的最小化代码如下from airllm import AutoModel import torch MAX_LENGTH 128 model AutoModel.from_pretrained( meta-llama/Llama-2-7b-chat-hf, compression4bit # 可选不压缩就删掉这行 ) model model.to(cuda:0) model.set_max_length(MAX_LENGTH) input_text What is the capital of France? input_ids model.tokenizer.encode(input_text, return_tensorspt).to(cuda:0) output_ids model.generate(input_ids, max_lengthMAX_LENGTH, do_sampleFalse) output_text model.tokenizer.decode(output_ids[0], skip_special_tokensTrue) print(output_text)别被 to(cuda:0) 骗了这不是说整个模型都搬进显存而是让模型对象处于 CUDA 设备上下文实际每一层的加载和释放都是内部处理的。from_pretrained 里的 compression 参数可以传 4bit 或 8bit它会在加载时对权重做量化压缩进一步降低每层的峰值显存。如果模型本身已经是量化格式这里也可以不传保持原样。4.2 从 HuggingFace 加载也可以加载本地模型在实际工作中我更多的场景是从 HuggingFace 下载模型后在本地复用。因为 70B 模型下载一次不容易每次都走在线下载不太现实。AirLLM 也支持本地路径的加载方式model AutoModel.from_pretrained( /path/to/local/model_directory, compression4bit )需要注意模型目录里要有完整的模型文件包括 config.json、pytorch_model.bin 或 safetensors 格式的权重文件。如果你的模型是以 safetensors 格式存储AirLLM 也能识别。不过有一点值得注意AirLLM 目前主要适配 Llama 架构的模型对于其他架构虽然理论上可行但兼容性可能存在问题。我试过用它加载一个基于 Mistral 架构的模型就没能直接跑通所以如果你的模型不是 Llama 系建议先查一下官方文档或 issue 区确认支持情况再做尝试。4.3 关键配置项max_length、batch size 和量化压缩set_max_length 这个方法在官方文档里被轻描淡写但在实际使用中非常重要。它会预先分配 KV cache 的空间直接影响显存占用和运行效率。如果你的推理长度需求是 512 token那就设 512不要设 2048否则显存会被白白占掉一大块。但是设小了也不行如果输入加输出的实际长度超过 max_length程序可能报错或者截断结果。所以我的建议是先根据你的需求设定一个略有余量的值跑一次之后看实际显存占用再动态调整。batch size 的调整空间不大。因为单层推理已经是显存受限的增大 batch 会让激活值和 KV cache 成倍增长直接把显存干爆。我在 8GB 显卡上测试过70B 模型 batch 设 2 勉强可以但已经很危险batch 再大必然 OOM。因此如果没有特殊需求建议始终保持在 batch size 1。compression 参数是 AirLLM 的一个亮点它不是一个简单的“省显存模式”而是把 PyTorch 的 module 在加载时动态替换为带量化功能的封装模块。使用 4bit 压缩后单层权重的体积大约缩小到原来的 1/4 左右可以显著减少磁盘 I/O 时间。我第一次跑 70B 模型时没加压缩每层加载要等三四秒加了这个之后压缩时间缩短到一秒左右总时长直接缩短了大半体验提升明显。5. 实测表现速度、显存和场景适配5.1 速度到底有多慢客观说AirLLM 最劝退的点就是慢。我在单卡 RTX 3090 上跑 Llama-2-70B-chat 的 4bit 压缩版输入的生成速度大约在每秒 0.5 到 1 个 token 之间。这个速度什么概念生成一段 200 字的回答你需要等三五分钟。如果你平时用惯了每秒几十 token 的 7B/13B 模型这个落差可能会让你怀疑人生。但这背后的原因是物理层面的每个 token 需要完整过一遍所有层每层都要从磁盘重新加载权重。假设模型 80 层单层 4bit 量化后约 1.3GB生成一个 token 就要读取约 100GB 的数据。哪怕 PCIe 4.0 的速度是每秒 7GB/s光传输就得 14 秒再加上计算时间这个速度已经是优化后的结果了。有个优化技巧值得分享把模型放到内存盘tmpfs/ramdisk里或者把中间结果和模型权重放到快速 SSD 上。我第一次在机械硬盘上跑的时候生成速度只有每秒 0.1 个 token基本没法用。换成 NVMe SSD 之后速度快了五六倍。所以如果你要跑大模型先确认你的磁盘是 SSD这个差距是倍数级的。5.2 显存占用实测数据最激动人心的部分当然是看着 nvidia-smi 里显存占用始终保持很低的水平。我做了几组实验在 8GB 显存的卡上加载 70B 模型 4bit 压缩版推理过程中显存峰值大约在 5GB 左右在 4GB 显存的卡上也能运行但更紧张峰值接近极限。而未压缩的 fp16 版本8GB 显存跑起来就勉强了4GB 基本无望。一个有趣的观察是AirLLM 并不依赖显存大小而是依赖加载速度和磁盘速度。因为单层推理模式下决定速度的瓶颈在数据搬运而不在选择停留。只有模型能放进单层权重的显存容量才算关键指标而这在现代显卡上都不是问题。显存分布的大致规律是Embedding 层和 LM Head 层因为词表很大单层权重往往比普通 Transformer 层大不少。如果你的显存特别紧张可以关注一下这两个部分的显存占用必要时通过 quantization 配置来单独压缩。AirLLM 内部有相关的处理机制但对于特别大的词表模型还是有 OOM 的风险。5.3 什么场景该用它什么场景不该用它经过实际测试我自己总结了 AirLLM 的适用边界供大家参考。适合的场景包括离线批量推理比如跑一批 prompt 做分类或摘要提取不需要实时的交互体验模型效果验证对比不同 prompt 模板或者 few-shot 组合对超大模型的影响这时候速度慢一点也没关系教学演示向学生或者同事展示大模型运行的完整推理链路AirLLM 的逐层模式反而更容易理解以及极端环境下的紧急推理比如临时没有大显存机器但必须用大模型跑一次结果。不适合的场景任何对响应速度有要求的在线服务AirLLM 的架构天然不适合实时对话需要反复交互调试的场景比如你在调 prompt 的过程中每一步都要等很久效率是很低的还有大批量的并行任务也不合适因为单 4GB 卡同时只能跑一个任务吞吐量完全比不上高显存卡上的并行推理。如果你的目标是一个对话机器人或者高频实验平台那 AirLLM 不是正确答案。你应该去买显存或者用量化KV cache offload 的成熟推理框架比如 vLLM 或 TensorRT-LLM这些在大吞吐场景下的效率要高出几个量级。AirLLM 解决的是“有没有”的问题而不是“好不好”的问题。6. 常见问题、踩坑记录与优化技巧6.1 加载模型时 CUDA out of memory这个是我遇到的最多的报错。注意这里指的并不是整个模型放不进显存而是某一层加载之后加上激活值超出了显存容量。解决思路有几条一是开启 compression 参数用 4bit 或 8bit 压缩单层权重体积二是降低 max_length减少 KV cache 预留空间三是减小输入长度因为输入越长激活值占用越高四是检查是否有其他进程占用了显存用一个简单的 nvidia-smi 看看显存使用率。之前遇到过有个后台的 Jupyter kernel 一直占着 2GB 显存关掉之后阈值就正常了。6.2 速度比预期还慢是不是哪里没对如果生成速度比预想的慢很多先检查三个地方确认模型文件存放的磁盘是不是 SSD机械硬盘在这个场景下真的不可用确认是否用了 compression4bit 压缩能显著减少 I/O 量速度提升可能不止一倍确认 max_length 是否设置得过大因为 max_length 直接影响 KV cache 的大小。另外关闭其他大带宽应用。我还试过另一个技巧在加载模型之前把系统缓存页缓存page cache给“预热”一下也就是先手动把模型文件读一遍到内核缓存后续推理时磁盘读取可能会命中系统缓存速度有小幅改善。具体做法就用 Linux 的 dd 命令dd ifmodel.safetensors of/dev/null bs1M这个效果不是必然的因为系统缓存毕竟是有限的70B 模型的文件太大可能缓存区域不够但如果你重复推理多次前面的命中率会高一些。6.3 加载本地模型报错未知的模型类型或Layer类型这是模型架构兼容性问题。AirLLM 对 Llama 架构做了专门的适配如果你加载的是 Qwen、Mistral、Baichuan 等模型架构即便它们很多派生自 Llama也会因为层定义细节不一致而报错。我当时试过加载一个非 Llama 架构的模型就遇到了类似的报错排查后确认是架构不匹配。解决方案是尽量选用 Llama-based 模型或者检查该架构在 AirLLM 官方支持列表中。6.4 KV cache 与 max_length 的配置心得再来单独讲讲 KV cache 这个容易踩坑的地方。AirLLM 在初始化阶段不会预先分配完整的 KV cache而是在 set_max_length 的时候预留空间。这意味着你设置的 max_length 决定了显存里 KV cache 的最大占用。如果设得过大会在开始推理时就出现显存不足即使你实际的输入输出没那么多如果设得过小推理过程中会出现“长度超限”之类的错误或者输出被提前截断。一个比较保守的方法先设一个较小的 max_length比如 128跑通一遍后看显存占用的余量再逐步往上加。这个过程看起来很简单但能帮你准确把握显卡的内存极限。我在切换不同模型时都会这样调一轮大约花几分钟但能避免后面的反复报错。6.5 磁盘空间管理前面提到过AirLLM 会把中间结果写进沙盒文件系统它们不会永久留存推理结束会自动清理。但沙盒机制本身需要临时空间如果你的模型目录和临时目录不在同一个磁盘上可能导致空间计算不准。更麻烦的是如果推理进程异常终止沙盒里的垃圾文件可能不会被清理下次启动时残留文件占用磁盘空间。我的经验是定期检查一次临时目录手动清理残留文件。尤其在长时间、多次跑实验之后这个检查值得养成习惯。7. 一些想说的心里话AirLLM 这个项目让我重新审视了一个问题大模型推理的瓶颈到底是不是显存过去我们都觉得显存不够就一切免谈。但 AirLLM 用工程手段告诉我们只要愿意用时间换空间很多“不可能”其实都能变成“可以”。当然它肯定不是万能的。我在实际使用中最大的感受就是AirLLM 是那种“正确但小众”的方案。它的速度决定了它不会成为生产环境的常客但它提供的分层推理思路在模型越来越大、单卡显存越来越不够用的未来很可能会有更广泛的借鉴意义。甚至在边缘计算、嵌入式设备上这种“极致省显存”的工程思路会越来越值钱。我在一次实验里用 AirLLM 在 4GB 老显卡上跑完了一个 70B 模型的完整生成流程那一刻的感觉很奇妙——技术上这不算什么奇迹它只是把一个非常简单的道理贯彻到了极致。但在显卡价格居高不下的今天这种把每一 MB 显存都“精打细算”的精神确实值得每个搞 AI 应用的人了解和学习。如果你只是在网上看别人讨论 70B 模型多震撼不妨亲手用 AirLLM 跑一次。不追求速度只求验证思路你会在等待每一个 token 生成的过程中对 Transformer 的层结构、显存管理、I/O 性能有远超课本的理解。这一点是我认为这个项目最大的价值所在。