引言
"4GB 显存跑 70B,8GB 跑 405B,3.7GB 跑 2.8 万亿参数的 Kimi K3。"
这是「每日一个开源项目」系列的第 174 篇。今天的项目是 AirLLM —— 一个让消费级 GPU 运行工业级大模型的 Python 库,技术核心是逐层推理(layer-wise inference)。
运行一个 70B 模型通常需要什么?fp16 精度下参数本身就有 140GB,加上 KV Cache 和中间激活值,至少需要两张 A100(各 80GB 显存)才能完整加载。AirLLM 的切入点不一样:Transformer 模型的各层在推理时是严格串行执行的,上一层的输出才是下一层的输入。既然同一时刻只有一层在计算,那么只需要把一层加载进显存就够了,其他层可以留在磁盘上。
结果是:70B 模型在 4GB 显存上运行,405B 在 8GB 显存上运行,2.8 万亿参数的 Kimi K3 在 3.72GB 显存上运行。
25,800 颗 Star,Apache 2.0,pip install airllm 即开。
你会学到什么
- 逐层推理的原理:为什么一层就够用
- 各模型的显存需求数据,以及 MoE 模型为什么更省
- 可选的块级压缩(4bit/8bit):3 倍速提升但不损失精度的原因
- 支持的模型范围:Llama 4、Qwen3、DeepSeek-V3/R1、Kimi K3 全覆盖
- Apple Silicon 和 CPU 推理的支持情况
- 局限性:比普通推理慢多少,适合什么场景
前提知识
- 了解 PyTorch 基础操作
- 对 Transformer 架构有基本认知(知道"层"是什么)
- 有 GPU/VRAM 的基础概念
项目背景
核心问题:大模型和小显卡之间的鸿沟
运行大语言模型的通常方式:把全部参数加载进显存,推理时在 GPU 上完整执行每个 forward pass。
这意味着 70B 模型(fp16,140GB)需要两张 A100 显卡。对研究机构和大公司来说这不是问题,但对个人开发者、独立研究者、学生而言,这个门槛实际上等于"不可使用"。
现有的绕过方案:
- 量化(Quantization):把 fp16 权重压缩到 4bit,显存需求降低 4 倍,但精度有所损失
- 蒸馏(Distillation):训练一个小模型模仿大模型的行为,换了一个模型
- 剪枝(Pruning):删除参数,模型能力下降
AirLLM 选了第四条路:不改变模型本身,改变加载和执行的方式。
逐层推理的核心原理
Transformer 的推理过程是严格串行的:
输入 tokens
↓
Embedding 层
↓
第 1 个 Transformer 层(Attention + FFN)
↓
第 2 个 Transformer 层
↓
...
↓
第 N 个 Transformer 层
↓
输出 logits → 采样 → 生成 token每一层的输入只依赖于上一层的输出,不依赖于其他层的权重。因此在任意时刻,只有当前正在执行的那一层需要在显存里。
AirLLM 把这个观察变成工程实现:
- 切分模型:把每一层的权重单独保存为磁盘上的 shard 文件
- 推理时逐层加载:执行第 k 层时,把第 k 层的 shard 从磁盘读入显存
- 执行完立即释放:第 k 层计算完成后,释放其显存,再加载第 k+1 层
- 只保留中间激活:各层之间传递的激活值(activation)很小,常驻显存
显存占用峰值 = 单层最大参数量 + 激活值,而不是整个模型大小。
作者信息
- 作者: lyogavin(GitHub)
- 许可证: Apache-2.0
- PyPI 包:
airllm
项目数据
- ⭐ GitHub Stars: 25,800+
- 🍴 Forks: 2,900+
- 📄 许可证: Apache-2.0
显存需求数据
这是 AirLLM 实测的各规模模型显存需求:
| 模型 | 参数量 | 所需显存 |
|---|---|---|
| ~8B 模型(Llama 3.1 8B 等) | 8B | ~1–2 GB |
| Qwen3-30B / Mixtral MoE | 30–47B | ~1–3 GB |
| Qwen3-235B(MoE) | 235B | ~3 GB |
| Llama 3.x 70B | 70B | ~4 GB |
| Llama 3.1 405B | 405B | ~8 GB |
| DeepSeek-V3 | 671B | ~12 GB |
| Kimi K3 | 2.8T | ~3.72 GB |
Kimi K3 只需 3.72 GB 的原因:它是 MoE(Mixture of Experts)架构,2.8 万亿是总参数量,每次推理只激活其中一小部分 expert。AirLLM 对 MoE 模型进一步优化——不是逐层加载,而是逐 expert 加载,每次推理只把被激活的那些 expert 权重读入显存。
快速开始
安装
pip install airllm
# 需要量化压缩时额外安装
pip install -U bitsandbytes基础用法
from airllm import AutoModel
# AutoModel 自动识别 HuggingFace repo 中的模型类型
model = AutoModel.from_pretrained("meta-llama/Meta-Llama-3.1-70B-Instruct")
input_text = ["Tell me about layer-wise inference."]
input_tokens = model.tokenizer(
input_text,
return_tensors="pt",
truncation=True,
max_length=128,
)
generation_output = model.generate(
input_tokens["input_ids"].cuda(),
max_new_tokens=20,
use_cache=True,
return_dict_in_generate=True,
)
output = model.tokenizer.decode(generation_output.sequences[0])
print(output)第一次运行时,AirLLM 会把模型按层切分保存为 shard 文件(需要一次性磁盘空间)。之后每次推理直接从 shard 加载,不再需要原始模型文件(可以用 delete_original=True 删除原文件回收磁盘空间)。
启用量化压缩(可选,约 3 倍加速)
model = AutoModel.from_pretrained(
"meta-llama/Meta-Llama-3.1-70B-Instruct",
compression="4bit", # 或 "8bit"
)压缩只作用于权重,不改变激活值。理由:AirLLM 的主要瓶颈是磁盘到 GPU 的加载带宽,权重压缩直接降低每层加载的数据量,速度提升显著,精度损失比全量化(权重+激活同时压缩)更小。
运行 Qwen3-32B
from airllm import AutoModel
model = AutoModel.from_pretrained("Qwen/Qwen3-32B")
input_tokens = model.tokenizer(
["你好,请介绍一下你自己。"],
return_tensors="pt",
truncation=True,
max_length=128,
)
generation_output = model.generate(
input_tokens["input_ids"].cuda(),
max_new_tokens=50,
use_cache=True,
return_dict_in_generate=True,
)
print(model.tokenizer.decode(generation_output.sequences[0]))运行 DeepSeek-V3(671B)
from airllm import AutoModel
model = AutoModel.from_pretrained("deepseek-ai/DeepSeek-V3")
input_tokens = model.tokenizer(
["Explain the concept of mixture of experts."],
return_tensors="pt",
truncation=True,
max_length=128,
)
generation_output = model.generate(
input_tokens["input_ids"].cuda(),
max_new_tokens=30,
return_dict_in_generate=True,
)
print(model.tokenizer.decode(generation_output.sequences[0]))Apple Silicon(macOS)
# 需要 MLX 后端 + 原生 Python(非 Anaconda Python)
pip install airllm mlxfrom airllm import AutoModel
# macOS 上自动使用 MLX 后端,利用 Apple Silicon 统一内存
model = AutoModel.from_pretrained("Qwen/Qwen3-8B")CPU 推理(v2.10.1+)
from airllm import AutoModel
# 不需要 GPU,但速度更慢
model = AutoModel.from_pretrained("meta-llama/Llama-2-7b-hf", device="cpu")支持的模型
AirLLM 覆盖了主流开源模型的大部分系列:
| 系列 | 版本 |
|---|---|
| Meta Llama | 2、3、3.1、3.3、4 |
| Alibaba Qwen | 1、2、2.5、3(含 MoE 和 FP8 变体) |
| DeepSeek | V2、V3、R1 |
| Mistral / Mixtral | 全系列 |
| Microsoft Phi | 全系列 |
| Google Gemma | 全系列 |
| ChatGLM / Baichuan / InternLM / Yi | 国内主流模型 |
v3.0 起支持 FP8 精度模型(DeepSeek-V3 默认格式)。
核心设计细节
预加载(Prefetching)
纯粹的串行"加载→计算→释放"在 GPU 和磁盘读取之间会有大量等待。AirLLM 实现了预加载优化:
正在计算第 k 层时,后台异步开始读取第 k+1 层的 shardGPU 计算和磁盘 I/O 重叠执行,减少等待时间。
Meta Tensor 初始化
AirLLM 使用 PyTorch 的 meta device 初始化模型骨架——建立整个模型的结构(层数、维度、配置),但不分配实际参数内存(使用"幽灵张量"占位)。真实权重只在执行到对应层时才从磁盘 shard 按需加载。这让 from_pretrained 可以在几秒内完成,而不是等几分钟把 140GB 全部读入内存。
MoE 模型的 Expert 级别加载
对于 Mixtral、Qwen3 MoE、DeepSeek-V3、Kimi K3 这类 MoE 架构,AirLLM 不是按整层加载,而是按 expert 加载:
MoE 层推理过程:
token → Router → 选择 top-k experts → 只加载这 k 个 expert 的权重 → 计算 → 释放Kimi K3 总参数 2.8T,但每次推理激活的 expert 只占总参数的一小部分,因此实际所需显存远低于直觉上的预期。
局限性
速度:逐层推理的代价是磁盘 I/O 变成推理的主要瓶颈。每生成一个 token 需要把全部 N 层都从磁盘依次加载执行一遍。速度远低于完整加载到显存的推理方式。AirLLM 解决的是"能不能运行"的问题,而不是"能不能快速运行"。
磁盘空间:切分保存的 shard 文件需要大量磁盘空间(和原始模型差不多大),完成切分后可以删除原始模型文件。
Kimi K3 的额外依赖:flash-attn、CUDA 12、transformers 4.56.x,依赖较为严格。
适合的场景:
- 探索、测试、体验新模型,对速度不敏感
- 没有足够显存的开发机和个人电脑
- 想用大模型做低频推理(比如每天生成几次报告)
- Apple Silicon Mac 上运行大模型
不适合的场景:
- 需要实时响应的生产环境(磁盘 I/O 太慢)
- 高并发服务(逐层推理不支持高效的 KV Cache 共享)
项目地址与资源
- 🌟 GitHub: lyogavin/airllm
- 📦 PyPI: pypi.org/project/airllm
- 📖 HuggingFace 博客: huggingface.co/blog/lyogavin/airllm
总结
AirLLM 回答了一个简单的问题:如果 Transformer 各层在推理时是严格串行执行的,为什么要把整个模型都放进显存?
逐层加载把显存峰值从"整个模型大小"压缩到"单层最大大小"。对 70B 模型来说,差距是 140GB 对 4GB。对 MoE 架构的 Kimi K3 来说,2.8T 的总参数在 3.7GB 显存里运行。
这个方案的代价是速度:磁盘 I/O 成为新瓶颈,推理比全内存慢。但它解锁的是可行性。对于没有服务器资源的个人开发者,能跑起来比跑得快更重要。
块级权重压缩(4bit/8bit)可以把加载带宽需求缩小到原来的 1/4,换来约 3 倍速提升,同时精度影响比全量化更小。这个设计抓住了正确的瓶颈:压缩需要解决的是磁盘读取速度,不是计算精度。
pip install airllm,然后把你想用的模型名传给 AutoModel.from_pretrained,剩下的 AirLLM 自动处理。
探索 PrimeSkills —— 精选 AI Agent 与技能的市场,每一个都经过真实企业工作流验证,去掉浮夸,留下真正有用的。
欢迎访问我的个人主页,发现更多有价值的见解和有趣的产品。