ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

airllm分层加载:单卡8GB显存跑70B大模型推理实战

airllm分层加载:单卡8GB显存跑70B大模型推理实战

1. 项目缘起与核心思路拆解

1.1 这个项目到底在解决什么问题

大模型推理这件事,卡在显存上的人太多了。我见过不少朋友,手里只有一张消费级显卡,8GB 或者 12GB 显存,想跑一个 70B 参数的模型,结果模型权重文件一加载就直接爆显存,连推理的门都摸不到。airllm 这个项目就是冲着这个痛点来的——它让单张显存有限的显卡也能跑起大参数量的模型,核心思路是分层加载、按需换入换出,而不是一次性把整个模型塞进显存。

说白了,传统做法是把整个模型权重全部加载到显存里,70B 的模型用 fp16 存储大概需要 140GB 显存,这显然不现实。airllm 的做法是:模型在磁盘上按层切分存储,推理时只把当前需要的那一层加载进显存,算完就释放,再加载下一层。这样显存占用就被压到了“单层权重”的量级,而不是“整个模型”的量级。

这个项目适合谁?我觉得有三类人值得关注:一是手里只有消费级显卡但想跑大模型做实验的开发者;二是想在本地做模型推理验证、不想依赖云端 API 的研究人员;三是想理解大模型推理底层机制、学习分层加载策略的技术爱好者。它不是一个追求极致推理速度的方案,而是一个用时间换空间的务实解法。

1.2 为什么选择分层加载而不是量化压缩

很多人第一反应是:显存不够,那就量化呗,4bit 量化能把 70B 模型压到 35GB 左右。但问题是,35GB 对于 8GB 显存的卡来说还是太大。量化能压缩模型体积,但压缩是有下限的,而且量化本身会带来精度损失,有些任务对精度敏感,量化后效果会打折扣。

airllm 选择的分层加载策略,本质上是一种内存调度优化。它不改变模型本身的精度,fp16 还是 fp16,只是把“一次性加载”变成了“流式加载”。这个思路的优势在于:精度无损,显存占用可控,理论上只要磁盘空间够,多大的模型都能跑。代价也很明显——推理速度会慢很多,因为每一层都要从磁盘读取,磁盘 I/O 成了瓶颈。

我实测下来,分层加载的推理速度大概是全量加载的十分之一到五分之一,具体取决于磁盘类型。SSD 上会快一些,机械硬盘上就明显更慢。所以这个方案的定位很清晰:不是用来做生产级高并发推理的,而是用来做本地验证、实验、学习的。你得接受它慢,但它确实能让你在有限的硬件上跑起原本跑不动的模型。

1.3 整体架构的设计考量

airllm 的架构设计有几个关键决策值得拆开讲。第一个决策是按层切分存储。模型在保存时,每一层的权重单独存成一个文件,这样加载时就可以精确控制只读哪一层。为什么按层切分而不是按其他维度?因为 Transformer 架构本身就是层叠结构,每一层的计算依赖上一层的输出,天然适合流式处理。

第二个决策是显存与内存的协同调度。airllm 并不是只把权重放在磁盘上,它会在内存里做一定的缓存。具体来说,它会预取下一层的权重到内存,这样当显存需要加载下一层时,直接从内存拷贝比从磁盘读取快得多。这个预取策略是性能优化的关键,预取层数设得太少,磁盘 I/O 等待时间长;设得太多,内存又扛不住。

第三个决策是与主流推理框架的兼容。airllm 并没有另起炉灶搞一套全新的推理引擎,而是尽量复用现有的模型加载和推理逻辑,只是在权重加载环节做了替换。这样做的好处是,大部分基于 Transformer 的模型都能比较方便地接入,不需要为每个模型单独适配。我试过用它跑几种不同架构的模型,只要模型结构是标准的层叠 Transformer,基本都能跑通。

2. 核心细节解析与实操要点

2.1 模型切分与存储格式

airllm 在使用前需要先对模型做一次切分处理。原始模型通常是一个大的权重文件,或者几个分片文件,airllm 会把它按层拆成单独的文件。这个过程只需要做一次,之后推理时就直接用切分好的文件。

切分后的目录结构大概是这样的:每一层对应一个文件,文件名里包含层号,另外还有一个配置文件记录模型的整体结构和切分信息。为什么要单独存一个配置文件?因为推理时需要知道模型有多少层、每层的结构是什么、权重文件的命名规则是什么,这些信息都得有个地方记录。

注意:切分过程会额外占用磁盘空间,因为原始模型文件和切分后的文件会同时存在。如果磁盘空间紧张,切分完成后可以删掉原始文件,但建议先确认切分后的模型能正常推理再删。

切分时有一个参数值得关注:切分粒度。默认是按 Transformer 的层来切,但有些模型层内结构比较复杂,比如注意力层和前馈层可以分开切。切得更细,显存占用峰值更低,但文件数量更多,磁盘 I/O 次数也更多。我一般建议按整层切分就够了,除非显存特别紧张,才考虑更细的粒度。

2.2 显存占用与预取策略

显存占用是 airllm 最核心的指标。理论上,显存占用约等于“单层权重的大小 + 推理时的中间激活值”。单层权重的大小取决于模型隐藏层维度和层内结构,中间激活值取决于 batch size 和序列长度。

以某个 70B 级别的模型为例,单层权重在 fp16 下大概是 1GB 到 2GB 的量级,中间激活值如果 batch size 设为 1、序列长度设为 512,大概几百 MB。加起来显存占用在 2GB 到 3GB 左右,8GB 显存的卡完全能跑。如果把 batch size 调大或者序列长度拉长,激活值会线性增长,显存占用也会跟着涨。

预取策略是影响推理速度的关键。airllm 支持设置预取层数,比如预取 2 层,意思是当前层在计算时,后面两层已经在内存里准备好了。预取层数越多,磁盘 I/O 等待越少,但内存占用越大。我一般建议预取 2 到 4 层,具体看内存大小。内存 32GB 的话,预取 4 层问题不大;内存 16GB 的话,预取 2 层比较稳妥。

2.3 磁盘类型对推理速度的影响

磁盘 I/O 是 airllm 推理速度的主要瓶颈,所以磁盘类型的影响非常明显。我做过对比测试,同样的模型、同样的硬件配置,只换磁盘类型,推理速度差异很大。

磁盘类型相对推理速度适用场景
NVMe SSD基准速度追求速度,预算充足
SATA SSD基准的 60% 到 80%性价比之选
机械硬盘基准的 20% 到 40%不推荐,除非只做验证
网络存储基准的 10% 到 30%不推荐,延迟太高

这个表格里的数据是我实测的大致范围,具体数值会因模型大小、层数、预取策略不同而有变化。但趋势是明确的:NVMe SSD 是最低要求,机械硬盘上跑 airllm 会慢到让人怀疑人生。

提示:如果磁盘空间够大,可以把切分后的模型放在 NVMe SSD 上,同时把预取层数调大一些,这样能明显改善推理速度。内存够大的话,甚至可以考虑把整个切分后的模型缓存到内存文件系统里,速度会更快。

2.4 模型兼容性与适配要点

airllm 主要面向标准 Transformer 架构的模型,但不同模型在细节上会有差异。比如有些模型用了分组查询注意力,有些用了旋转位置编码,有些用了不同的激活函数。这些差异在切分和推理时都需要考虑。

我试过几种不同架构的模型,大部分能直接跑通,少数需要做一些适配。适配的主要工作是:确认模型层结构是否标准、确认权重命名规则是否一致、确认是否有特殊的层间依赖。如果模型结构比较特殊,比如有跨层连接或者动态路由,airllm 的分层加载策略可能就不太适用了。

注意:在正式使用前,建议先用小模型做一次完整流程验证,确认切分、加载、推理都能正常跑通,再上大模型。小模型验证成本低,出问题也容易排查。

3. 实操过程与核心环节实现

3.1 环境准备与依赖安装

开始之前,先把环境准备好。airllm 本身是一个 Python 库,安装方式比较直接。我一般建议用虚拟环境,避免和系统里的其他包冲突。

python -m venv airllm_env source airllm_env/bin/activate pip install airllm

安装完成后,还需要确认几个关键依赖的版本。PyTorch 的版本要和 CUDA 版本匹配,transformers 库的版本要和模型兼容。我遇到过因为 transformers 版本太新导致模型加载失败的情况,回退到稍早的版本就正常了。

提示:安装 airllm 时,它会自动拉取依赖,但 PyTorch 建议手动安装,选择和自己 CUDA 版本匹配的版本。用pip install torch --index-url指定对应的源,能避免装错版本。

环境准备好之后,先跑一个简单的测试脚本,确认基础功能正常。测试脚本不需要加载大模型,用一个小模型验证切分和推理流程即可。

3.2 模型切分实操

模型切分是第一步。假设你已经下载好了原始模型,放在某个目录下,切分的代码大概是这样:

from airllm import AutoModel model = AutoModel.from_pretrained( "path/to/original/model", layer_shards_saving_path="path/to/sharded/model" )

这段代码会读取原始模型,按层切分后保存到指定目录。切分过程需要的时间取决于模型大小和磁盘速度,70B 级别的模型在 NVMe SSD 上大概需要十几分钟到半小时。

切分过程中有几个点需要注意。第一,确保磁盘空间足够,切分后的文件总大小和原始模型差不多,甚至可能略大。第二,切分过程中不要中断,中断可能导致切分不完整,需要重新来。第三,切分完成后,检查一下切分目录里的文件数量和模型层数是否一致,不一致说明切分有问题。

注意:有些模型本身已经是分片存储的,比如按层分成了多个文件。这种情况下,airllm 的切分过程会更快,因为它只需要重新组织文件结构,不需要重新读写全部权重。

3.3 推理配置与参数调优

切分完成后,就可以用切分后的模型做推理了。推理时的配置参数直接影响显存占用和推理速度,需要根据硬件情况调优。

from airllm import AutoModel model = AutoModel.from_pretrained( "path/to/sharded/model", max_seq_len=512, layer_shards_saving_path="path/to/sharded/model" ) input_text = "你的输入文本" input_tokens = model.tokenizer( input_text, return_tensors="pt", return_attention_mask=False, truncation=True, max_length=512 ) output = model.generate( input_tokens["input_ids"].cuda(), max_new_tokens=128, use_cache=True, do_sample=False )

这里有几个参数值得展开讲。max_seq_len控制最大序列长度,设得越大,中间激活值越大,显存占用越高。max_new_tokens控制生成的最大 token 数,设得越大,推理时间越长。use_cache建议开启,它能缓存已计算的键值对,避免重复计算,对速度有正面影响。

预取层数的配置在模型加载时指定,不同版本的 airllm 参数名可能略有差异,具体看文档。我一般从 2 开始试,如果内存充足且磁盘速度慢,可以加到 4。

3.4 推理过程监控与性能观察

推理过程中,我习惯用nvidia-smi监控显存占用,用iostat监控磁盘 I/O。这样能直观看到瓶颈在哪里。

如果显存占用接近上限,说明 batch size 或序列长度设得太大了,需要调小。如果磁盘 I/O 持续跑满,说明预取层数不够或者磁盘速度太慢,可以考虑增加预取层数或换更快的磁盘。如果 GPU 利用率很低,说明大部分时间都在等磁盘 I/O,这是 airllm 的典型特征,接受它或者换方案。

我实测下来,70B 模型在单张 8GB 显存的卡上,用 NVMe SSD,预取 4 层,生成 128 个 token 大概需要几分钟。这个速度做交互式对话肯定不够,但做批量实验或者验证性推理是可以接受的。

提示:如果只是做验证,可以把max_new_tokens设小一些,比如 32 或 64,这样能快速看到结果,节省时间。正式生成时再调大。

4. 常见问题与排查技巧实录

4.1 显存溢出问题排查

显存溢出是 airllm 使用中最常见的问题之一。虽然 airllm 的设计目标就是降低显存占用,但如果配置不当,仍然可能溢出。

排查思路是这样的:先确认单层权重的大小,再确认中间激活值的大小,两者相加就是理论显存占用。如果理论值已经接近显存上限,那说明配置本身就有问题,需要调小 batch size 或序列长度。如果理论值远小于显存上限但仍然溢出,那可能是显存碎片或者有其他进程占用了显存。

我遇到过一次显存溢出,排查后发现是序列长度设得太大了。把max_seq_len从 2048 调到 512 后,显存占用直接降了一半多。所以序列长度对显存的影响是很大的,不要一上来就设很大。

问题现象可能原因解决方法
加载模型时显存溢出单层权重太大换更细的切分粒度
推理时显存溢出序列长度或 batch size 太大调小 max_seq_len 或 batch size
推理中途显存溢出显存碎片重启进程,减少其他显存占用
显存占用波动大预取策略不稳定调整预取层数

4.2 推理速度过慢的优化方向

推理速度慢是 airllm 的固有特征,但可以通过一些手段优化。第一个方向是换更快的磁盘,NVMe SSD 比 SATA SSD 快很多,SATA SSD 比机械硬盘快更多。第二个方向是增加预取层数,让磁盘 I/O 和 GPU 计算更好地重叠。第三个方向是减小模型规模,如果 70B 太慢,可以试试 13B 或 30B 的模型。

还有一个容易被忽略的点:模型切分后的文件布局。如果切分后的文件在磁盘上碎片化严重,读取速度会受影响。我一般建议把切分后的模型放在一个独立的目录里,避免和其他文件混在一起。如果磁盘支持,做一次磁盘整理也有帮助。

注意:不要为了追求速度把预取层数设得太大,内存不够的话会触发交换,反而更慢。预取层数的上限是内存能容纳的层数,超过这个上限就会适得其反。

4.3 模型加载失败的常见原因

模型加载失败的原因比较多,我整理了几种常见情况。第一种是模型格式不兼容,airllm 主要支持 PyTorch 格式的模型,如果模型是其他格式,需要先转换。第二种是模型结构不支持,比如有些模型用了非标准的层结构,airllm 的分层加载策略可能不适用。第三种是权重文件损坏或不完整,下载过程中断可能导致文件损坏,需要重新下载。

排查时,先看错误信息,错误信息通常会指出是哪一步出了问题。如果是加载权重时出错,检查权重文件是否完整。如果是模型结构解析出错,检查模型配置文件是否正确。如果是显存相关错误,参考前面的显存溢出排查思路。

我踩过的一个坑是:模型配置文件里的层数和实际权重文件的层数不一致。这种情况通常发生在模型被修改过或者下载不完整的时候。解决方法是重新下载模型,或者手动修正配置文件。

4.4 实操心得与避坑清单

用了这段时间,我总结了几条实操心得,都是踩过坑之后才明白的。

第一条:先用小模型验证流程。不要一上来就拿 70B 模型试,用小模型把切分、加载、推理整个流程跑通,确认没问题再上大模型。小模型出问题容易排查,大模型出问题排查成本高。

第二条:磁盘空间要留足。切分后的模型和原始模型会同时存在,磁盘空间至少要是模型大小的两倍。如果还要存其他东西,留三倍更稳妥。

第三条:预取层数不要贪多。预取层数增加能提升速度,但内存占用也会增加。内存不够时,系统会触发交换,速度反而下降。我一般设 2 到 4 层,根据内存大小调整。

第四条:序列长度从短到长试。先设 128 或 256,确认能跑通再逐步加长。直接设 2048 很容易显存溢出,而且排查起来麻烦。

第五条:推理速度要有合理预期。airllm 不是用来做实时推理的,它的定位是“让跑不动的模型能跑起来”,而不是“让跑得慢的模型跑得快”。接受它的速度,用它做该做的事。

提示:如果要做批量推理,可以考虑把多个输入拼成一个 batch,这样能摊薄每层的加载开销。但 batch size 太大会增加显存占用,需要权衡。

4.5 与其他方案的对比选择

airllm 不是唯一的显存优化方案,实际选择时要看具体需求。量化方案适合对精度要求不极端、追求速度的场景;airllm 适合对精度有要求、能接受慢速的场景;模型并行适合有多张显卡的场景;云端 API 适合不想折腾本地的场景。

方案显存占用推理速度精度硬件要求
全量加载极高最快无损大显存
量化中等较快有损中等显存
airllm低慢无损小显存
模型并行中等较快无损多卡
云端 API无快无损网络

这个对比不是绝对的,具体数值会因模型和硬件不同而有变化。但大方向是清楚的:airllm 在显存占用上有优势,在速度上有劣势,精度上无损。选择时看自己最在意哪个维度。

我个人在实际操作中的体会是,airllm 最适合的场景是“本地验证”——你有一个大模型,想快速验证它在某个任务上的表现,但手头没有大显存的卡。这种情况下,airllm 能让你在有限硬件上跑起来,虽然慢,但能出结果。如果是要做生产级部署,airllm 的速度瓶颈很难绕过,还是得考虑其他方案。

最后再分享一个小技巧:如果内存足够大,可以把切分后的模型放到内存文件系统里,比如 Linux 下的/dev/shm。这样磁盘 I/O 就变成了内存读写,速度会有明显提升。当然,内存文件系统重启后会清空,适合临时使用。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进