1. 这次要说的是个什么项目
先说结论:AirLLM 是一个让你在仅有 4GB 显存的普通显卡(甚至没有独显的电脑)上,跑起 70B 级别大模型的开源推理方案。它做的事情说白了就一句话——把“一张卡装不下大模型”这个物理限制,用分层加载的土办法绕过去。
很多人一听“70B 模型”,第一反应是“这不得 8 张 A100?”,因为按照常规计算,70B 参数的光是权重文件用 FP16 精度存下来就要 140GB,任意一张消费级显卡的显存都装不下。但 AirLLM 的思路跟常规的大模型推理框架完全不同:它不要求把整个模型一次性塞进显存,而是每次只把一个 Transformer 层的权重加载到 GPU 上,算完这一层就把结果存回内存,再换下一层进来。打个不太严谨的比方,别家是“把整本书摊在桌面上读”,AirLLM 是“一次只翻开一页,看完合上再翻下一页”。
这篇内容适合谁看?一类是手里只有老旧显卡、显存捉襟见肘,但想实际跑一跑 LLaMA、Qwen 这类开源大模型做实验的开发者;另一类是想理解模型推理时显存到底怎么被吃掉的原理党。整个项目在 GitHub 上可以直接搜到,代码量不大,结构也清楚,用它来入门“大模型推理优化”这个方向,比一上来就啃 vLLM 或者 TensorRT-LLM 的源码要友好得多。
2. 为什么 4GB 显存能跑 70B?原理拆解
2.1 常规推理的显存开销花在哪
在聊 AirLLM 之前,得先搞清楚一个问题:正常跑大模型推理,显存究竟被什么吃掉了。大模型推理的显存占用可以分成三大块:
- 模型权重:这是大头。以 LLAMA2-70B 为例,FP16 精度下每个参数占 2 字节,70B 参数就是 140GB 权重。哪怕是量化到 4bit,也得 35GB 左右,单张消费级显卡依旧无解。
- KV Cache:生成每个 token 时,注意力机制需要缓存历史 token 的 Key 和 Value 向量。序列越长,缓存越大,这部分跟 batch size 和序列长度成正比。
- 中间激活值:前向传播时每一层输出的临时张量,虽然存到显存里的生命周期短,但计算图中间结果的峰值不可忽视。
常规框架为了推理速度,会把这些东西全部常驻显存,缓存越多越好。所以普通显卡跑不了大模型不是算力不够,是显存这个“仓库”太小,货放不下。
2.2 分层推理:用时间换空间
AirLLM 的核心思路来自一篇论文——LLM in a flash: Efficient Large Language Model Inference with Limited Memory(苹果那篇把模型权重存储在闪存里做推理的工作)。AirLLM 把它工程化了,落地成了一个直接能用的库。
它的做法是:把模型按层切开,GPU 上只保留当前正在计算的那一层,其他层的权重全部放在 CPU 内存或者磁盘上。前向传播的过程大致是这样:
- Embedding 层加载到 GPU,把输入 token 转成向量,算完就把 embedding 层的权重从显存挪走。
- 计算第 1 个 Transformer 层时,把第 1 层的权重从内存加载到显存,做完前向计算,拿到输出张量,然后立刻把第 1 层权重清出显存。
- 再加载第 2 层,重复上述过程,一直到所有层都算完。
- 最后加载 LM Head(输出层),得到下一个 token 的概率分布。
这样每一时刻显存里只有一个 transformer layer 的权重加少量中间结果,而一个 70B 模型的单层权重大约是 1.5GB 到 2GB(视模型结构而定),4GB 显存刚好够用。
2.3 代价是什么:慢,但能用
这种方案的本质是用带宽换容量,用时间换空间。GPU 和 CPU 内存之间通过 PCIe 传输数据,速度比显存内部的带宽慢一到两个数量级,每算一层就要交换一次权重,所以速度肯定不会快。
但它的价值在于“从 0 到 1”——在没有 AirLLM 之前,4GB 显存连 7B 模型都跑不起来,有了它之后至少能跑出结果。对于做实验、验证想法、学习原理的场景,这个“慢”是完全能接受的。
3. 动手前要准备什么
3.1 硬件与系统的底线要求
AirLLM 本身对 GPU 的要求极其宽容,官方说 4GB 显存就能跑,亲测 2GB 的 GTX 1050 Ti 也能跑,只是更慢。核心需求其实在内存上,因为模型权重大部分时间放在 CPU 内存里:
- 70B 模型 FP16 权重需要约 140GB 内存,量化版需要约 35GB 到 70GB。
- 7B 模型 FP16 约 14GB,量化约 4GB 左右。如果只是初学,建议先拿小模型跑通,再挑战大的。
- 磁盘空间同样要留够,因为 HuggingFace 下载的原始权重文件就是这么大。
系统方面,Windows 和 Linux 都支持,但个人推荐 Linux,主要是内存管理和文件缓存策略更可控。当然,如果你只是想在 Windows 上快速试一下,也能跑,项目源码是纯 Python + PyTorch 的,跨平台。
3.2 Python 环境与依赖
AirLLM 的实现非常轻量,没有引入重型框架,依赖主要是 PyTorch、transformers、huggingface_hub。建议用 conda 建一个干净环境:
conda create -n airllm python=3.10 -y conda activate airllm pip install airllm安装在 PyPI 上直接有包,GitHub 源码地址是https://github.com/lyogavin/airllm,如果安装 PyPI 版本遇到问题,也可以直接 clone 源码,把airllm目录放到项目里 import。
3.3 模型选择建议
AirLLM 支持的模型主要以 LLaMA 架构为主,包括 Llama 2、Llama 3、Qwen、Mistral、Yi 等常见开源模型。官方做了适配的模型在 HuggingFace 上有标识,建议优先选官方验证过的模型,省去自己适配的麻烦。
新手第一次跑,建议别直接上 70B,先用 7B 或 13B 模型把流程跑通。原因很简单:70B 模型光下载就要等半天,如果代码里有个小报错,反复调试的成本会让人崩溃。7B 模型下载快、调试快,把 API 用熟了再换大模型不迟。
4. 完整实操:从安装到跑出第一个 token
4.1 基础推理:用 HuggingFace 模型直接跑
AirLLM 的使用方式和 transformers 非常接近。核心接口就两个:AirLLMLlama2和LlamaConfig。下面以 Llama 2 7B 为例:
from airllm import AirLLMLlama2, AutoModel MAX_LENGTH = 128 model = AirLLMLlama2.from_pretrained( "garage-bAInd/Platypus2-7B", max_length=MAX_LENGTH, device="cuda", ) input_text = "What is the capital of the United States?" input_tokens = model.tokenizer( input_text, return_tensors="pt", return_attention_mask=False, ).to("cuda") generation_output = model.generate( input_tokens, max_new_tokens=64, use_cache=True, return_dict_in_generate=True, ) output = generation_output.sequences[0] print(model.tokenizer.decode(output, skip_special_tokens=True))这段代码里有几个细节要说明:
from_pretrained的第一个参数是 HuggingFace 上的模型 ID,AirLLM 会自动下载权重文件。max_length是模型支持的最大序列长度,AirLLM 会根据这个参数分配 KV Cache 的显存空间,设太大会爆显存,设太小则长文本生成会截断。return_attention_mask=False是 AirLLM 的一个小偏好,它的 tokenizer 封装不依赖 attention mask。use_cache=True启用 KV Cache,虽然 AirLLM 的分层推理让 cache 的命中率打了折扣,但保留缓存依然能省去不少重复计算。
4.2 模型实际加载过程全流程
当你执行from_pretrained时,AirLLM 内部做的事情比 transformers 多得多。拆开来看大概是:
- 检查本地缓存目录,如果模型权重没有下载过,就从 HuggingFace 拉取(支持断点续传,但大文件建议用
hf download提前下载)。 - 把模型的 embedding 层和所有 transformer layer 的权重文件映射到内存(mmap),而不是一次性读进内存——这也是它能处理 140GB 文件的关键。
- 初始化一个空的模型结构,但权重全部置空,只保留层与层之间的计算图和连接关系。
- 在 GPU 上准备好一个“层缓存区”,大小正好容纳单层权重。
也就是说,from_pretrained返回的 model 对象并不是通常意义上“权重已加载”的模型,而是一个空壳 + 权重索引。真正发生权重搬运是在generate调用时,逐层在前向传播中完成。用nvidia-smi观察显存占用,会看到显存使用随层数推进呈现“锯齿状”波动,这就是分层加载的直观证据。
4.3 70B 模型的实测过程和速度预期
跑通小模型后,可以挑战 70B。以 Llama 2 70B 为例,你需要先准备好足够的内存,然后修改代码:
from airllm import AirLLMLlama2 model = AirLLMLlama2.from_pretrained( "garage-bAInd/Platypus2-70B", max_length=128, device="cuda", )在只有 4GB 显存、64GB 内存的机器上,生成 64 个 token 的耗时会非常感人——实测下来大约 1 token/s 到 2 token/s,也就是生成一句话可能要等一两分钟。这个速度没法做实时对话,但跑离线分析、批量处理短文本是可行的。如果你的内存只有 32GB,那就得找 4bit 量化版本的模型,AirLLM 对 GPTQ 和 AWQ 格式的模型也做了兼容,但需要额外传参指定量化类型。
4.4 量化模型怎么用
AirLLM 支持加载量化权重,主要是 GPTQ 和 AWQ 两种格式。用法上只多一步:
from airllm import AirLLMLlama2 model = AirLLMLlama2.from_pretrained( "TheBloke/Llama-2-13B-GPTQ", quantization="gptq", device="cuda", )quantization参数接受"gptq"或"awq"。量化模型的最大好处是权重体积缩减到 FP16 的四分之一左右,内存压力大幅降低,13B 的量化模型只需要 7GB 内存就能跑。代价是生成质量轻微下降,但对绝大多数任务来说,4bit 量化的损失几乎感知不到。
5. AirLLM 实测中的常见问题与避坑记录
5.1 显存不足:高于预期
有读者反馈“不是说 4GB 就能跑吗,为什么我 4GB 还是会 OOM?”这里要把话说清楚:分层推理省的是权重占用的显存,但不省激活值和 KV Cache。如果你的max_length设得很大,或者生成长度设得很长,缓存区一样可能爆显存。
避坑建议:
max_length从小的开始调,比如 64 或 128,跑通了再往上加。batch_size始终保持 1,AirLLM 的设计不支持大批量推理。- 如果显存极紧(比如 2GB),可以给模型的激活值计算开启 checkpointing,AirLLM 的
LlamaConfig里提供了checkpointing相关选项,本质是用重复计算换显存。
5.2 加载速度极慢:权重预下载
70B 模型首次运行,最耗时间的往往不是推理,而是下载。HuggingFace 的权重传输虽然支持多线程,但 140GB 文件在普通网络环境下可能要下好几个小时。而且如果中途断网,部分文件损坏,又要重新来。
我的做法是先用hf download把模型拉到本地缓存:
pip install huggingface_hub hf download garage-bAInd/Platypus2-70B --local-dir ./platypus-70b然后再在代码里从本地路径加载:
model = AirLLMLlama2.from_pretrained( "./platypus-70b", max_length=128, device="cuda", )这样至少可以避免下载过程中断导致的重试烦恼,而且后续反复实验都走本地文件,速度快很多。
5.3 速度慢到怀疑人生,正常吗
AirLLM 跑 70B 的速度就是很慢,这是方案本身决定的,不是代码 bug。但有几个因素会进一步拖慢速度:
- PCIe 版本和通道数:PCIe 3.0 x16 和 PCIe 4.0 x16 的差别很大,后者带宽翻倍,层切换耗时明显减少。
- CPU 内存频率:内存是权重的中转站,频率越高越好。如果内存跑在低频上,会成为新瓶颈。
- 系统内存 Swap:如果内存不足,系统会动用磁盘 Swap,速度会骤降到不可用的程度。建议关掉 Swap,或者加大内存。
实测数据供参考:同一台机器上,PCIe 3.0 下跑 7B 模型约 5 token/s,换到 PCIe 4.0 后约 8 token/s;70B 模型则从 1 token/s 提升到约 1.8 token/s。
5.4 模型不兼容:报错怎么办
AirLLM 不是所有模型都能直接跑,它的架构假设是“标准 LLaMA 结构”。如果你拿一个做了结构改动的新模型(比如加了 MoE 层、换了 attention 实现的模型),大概率会报加载错误。
处理思路有三种:
- 找官方 issues 里是否有人适配过同一个模型。
- 自己改
airllm/modeling_llama.py,把结构对齐到你的模型,但这需要对模型源码有足够理解。 - 退而求其次,选择它已经验证过的模型列表里的替代品。
5.5 常见问题速查表
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 显存 OOM | max_length 或生成长度过大 | 调小 max_length 和 max_new_tokens,batch_size 保持 1 |
| 加载时间极长 | 权重文件未预下载 | 用 hf download 提前拉到本地,再走本地路径 |
| 生成速度 < 0.5 token/s | CPU 内存或磁盘 Swap 瓶颈 | 加内存、关 Swap、检查 PCIe 版本 |
| 报错 Unknown layer type | 模型结构非标准 LLaMA | 换成官方验证过的模型或自行适配 |
| 模型权重文件校验失败 | 下载不完整 | 删除缓存重新下载,或换 hf 镜像源 |
6. 除了慢,这个项目还教会了我们什么
AirLLM 的价值不该只用“跑得慢不慢”来衡量。我实际用过之后,反而觉得它对学习大模型推理原理的帮助特别大。
通过它的源码,你能直观看到 Transformer 模型推理时权重是怎么流动的、显存和内存的边界在哪、KV Cache 又是如何工作的。这些东西在常规推理框架里被各种缓存优化掩盖了,只有在这种“穷酸跑法”里才暴露得清清楚楚。
另外,它的代码量不大,核心文件airllm/modeling_llama.py是纯 Python 实现的,配合注释读下来,基本就能理解大模型的前向传播全流程。对于想深入大模型底层原理又怕啃不动源码的人来说,AirLLM 是一个绝佳的入门素材。
还有一个容易被忽略的使用场景:CPU-only 机器。AirLLM 也支持完全没有 GPU 的机器,把device设为"cpu"就可以。虽然速度更慢,但至少让只有普通笔记本的人也能跑大模型做实验。
7. 内存不足时的替代方案:AirLLM 的量化感知加载
如果你的内存真的不够(比如 70B 量化版也需要 35GB 内存,但机器只有 16GB),AirLLM 还有一个隐藏功能——支持把权重放在磁盘上而不是内存里。
具体用法是在加载时传一个本地目录的路径,AirLLM 会用 mmap 方式直接从磁盘映射权重文件,而不是读入内存。代价是每层切换时都要从磁盘读取,速度进一步下降。但作为“极限环境下的最后手段”,这个功能在 16GB 内存的笔记本上跑 70B 模型时真的能救命。
不过说实话,这种模式下体验确实比较差,生成一个 token 可能等上几十秒,只适合离线跑批。我更推荐的做法是:如果机器配置真的很低,优先考虑更小的模型(比如 7B 或 13B),而不是硬上 70B。模型大小和任务需求匹配,比“越大越好”更实用。
8. 个人实测后的真实感受
先说结论:AirLLM 不是生产环境的答案,它是“穷玩”大模型的一个可行性验证。它证明了在硬件资源极度受限的情况下,通过聪明的工程手段,依然可以把超大模型跑出结果——哪怕慢到让人怀疑人生。
我用它跑了两个礼拜,最大的收获不是成功生成了多少文本,而是真正理解了“显存、内存、磁盘”这三层存储在推理中的角色分工。以前看各种显存优化文章总是一知半解,亲手跑过 AirLLM 之后,那些概念全都对上了。
最后给一个实操延伸建议:如果你跑通了 AirLLM,下一步可以试试它同一个作者开源的另一套工具——把 AirLLM 和 PEFT(LoRA)微调结合。AirLLM 的代码里已经预留了训练相关的接口,能在不换卡的前提下做极小 batch 的 LoRA 微调。这又是一个“本来需要很多显卡才能做的事被压缩到一张低端卡上”的实验,玩起来很有意思。
说到底,大模型入门这件事,门槛高是事实,但没高到非要几万元显卡才能动手的地步。AirLLM 让 4GB 的老显卡重新变成了生产力工具,这件事本身就值得点个赞。