ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

70B模型分片推理:39台笔记本组建分布式集群的工程实践

70B模型分片推理:39台笔记本组建分布式集群的工程实践 把 70B 模型拆开塞进 39 台 Intel 笔记本里让它们通过局域网协作完成一次大模型推理。这个想法听起来像是实验室里的极客行为但它背后涉及的模型并行、网络通信、内存分配和成本收益判断其实对每个做 AI 工程的人都有参考价值。这篇文章不打算只讲“39 台笔记本”这个噱头而是想拆清楚三件事第一模型分片Sharding在数据库和大模型推理中到底是不是同一个思路第二70B 模型跨笔记本推理在工程上会遇到哪些真正的瓶颈第三如果你手头正好有一批闲置笔记本或者只是想理解分布式推理的底层原理第一步该怎么走。在展开之前先给一个明确判断70B 模型跨 39 台 Intel 笔记本分片推理在技术上可行但它的价值不在于“从此不需要 GPU 了”而在于用极低的成本验证一个分布式推理原型让你在动手过程中真正理解模型并行的边界在哪里。1. 分片不是数据库专属从一张大表到一个大模型“Sharding”这个词很多后端工程师更熟悉的是数据库场景一张表太大读写扛不住就按某个字段把数据拆到不同的 MySQL 实例上再把分片数据源注册到动态数据源中让上层应用按路由规则去不同分片取数据。大模型里的 Sharding 思路几乎一样只是切的东西从“一张表”变成了“一个神经网络”。70B 模型有多大这里有一个直观的换算。以常见的 LLaMA 系列 70B 结构为例参数量约 700 亿FP16 精度下仅权重文件就需要约 140GB即使使用 INT8 量化也需要约 70GB即便压到 INT4也需要约 35GB 以上而普通 Intel 笔记本的内存通常只有 16GB、32GB顶配可能有 64GB。单台笔记本连 FP16 的 70B 模型都放不下。但 39 台笔记本如果按平均 16GB 算总内存超过 600GB装下 140GB 的权重绰绰有余。所以关键问题不是总容量而是怎么把模型拆开、放进去、协调它们干活。模型分片有两种典型维度张量并行Tensor Parallelism把一层里的矩阵乘法拆成多块。比如计算某个线性层y Wx可以把 W 按行切开分别计算再拼接结果。这种方式通信频率极高每一层前向传播都需要跨设备同步多次适合 NVLink 这类高速互连。流水线并行Pipeline Parallelism把模型按层切成很多段每一段放在一台设备上。输入先经过第一台设备的层算完把中间激活值传给第二台像流水线一样依次往后走。通信只在层与层之间发生一次频率远低于张量并行更适合笔记本之间的千兆以太网。从这个角度就能理解为什么 39 台 Intel 笔记本的组合在理论上行得通它走的必然是流水线并行的路线而不是张量并行。因为张量并行在普通局域网环境下的通信开销会直接让整个推理过程卡死在等待网络之上。2. 为什么是 39 台数字背后的工程逻辑39 台笔记本不是随手拍出来的数字它对应的是模型层数与机器数量的某种配比。70B 规模的模型通常有 60 到 80 层比如 LLaMA-2 70B 是 80 层。如果按“若干层一组”切到 39 台机器上每台负责 2 层左右刚好能把整个网络的 transformer 层分完再加上 embedding、输出头之类的模块由专门的节点或其中一两台机器负责。这样 39 台节点就恰好构成一条完整的推理流水线。从另一个角度看39 台笔记本的内存总和也是一个关键约束。如果平均每台 16GB 内存39 台合计约 624GB即使权重使用 INT8 量化只需要约 70GB也有大量余量留给 KV Cache、激活值和系统缓冲。这意味着推理过程中不太会因为单机内存不足而 OOM。但工程上还有一个容易被忽略的约束这 39 台笔记本之间必须处于同一局域网且最好是千兆有线网络。只用 Wi-Fi 通信的话带宽通常只有几百 Mbps而且波动大很难维持稳定的分布式推理。笔记本身份还带来另一个好处每台机器自带屏幕、键盘、电源无需额外采购服务器机箱方便独立运行。这在概念验证阶段非常有吸引力成本也极低。假设一台二手 Intel 笔记本价格在几百元水平39 台总成本可能只相当于一块中端显卡的价格远远低于一张几十 GB 显存的服务器 GPU。不过便宜不是免费的。后面会看到真要让这些笔记本协作干活麻烦比想象中多得多。3. 两条技术路线与环境准备如果你真的想复现“70B 模型跑在 39 台笔记本上”的实验其实有两条路可以走。第一条路直接使用现成的分布式推理框架比如 llama.cpp 的 RPC 模式。llama.cpp 是社区里非常活跃的 C 推理引擎最初目标是让 LLaMA 类模型能在普通 CPU 上运行后来逐步支持了量化、GPU 加速、RPC 远程推理等能力。它通过 RPC 相关参数把部分层分配到远程机器上形成一种流水线并行的效果。用 llama.cpp 的方式其实不需要写太多代码。先在所有参与推理的笔记本上启动 llama.cpp 的 RPC server然后在主节点上启动主进程指定模型路径、量化方式、RPC 地址列表它就会自动把模型按层分到这些远程节点。第二条路用 PyTorch 配合 accelerate 或自定义 RPC 搭建自己的推理流程。这条路更灵活但代码量会大很多适合用来理解分片的底层细节或者对接非 LLaMA 架构的模型。对于绝大多数想快速验证的开发者我建议先走第一条路。它的优点是社区文档多、配置简单遇到问题可以搜到很多解决方案。第二条路更适合喜欢研究原理的人。无论走哪条路环境准备是绕不开的。下面是针对“PyTorch 自定义 RPC 分片推理”的最小环境清单操作系统LinuxUbuntu 22.04 或 Debian 12 都可以也可以使用 Windows但 Linux 的 socket 连接更稳定Python3.10 及以上PyTorch2.x 版本1.x 也可以但 RPC 部分建议 2.xtransformers、accelerate加载 Hugging Face 模型时需要局域网内的多台笔记本彼此能够通过 IP 地址访问千兆交换机或路由器推荐有线连接这里的版本号是一个通用建议实际使用时请以你选择的框架版本为准。比如 transformers 的 API 在不同大版本之间差异不小accelerate 的load_checkpoint_and_dispatch参数也有过调整。在开始之前你需要确认每台笔记本都能通过 SSH 或至少在同一个网络里互相 ping 通。如果节点之间有防火墙要放行将要使用的 RPC 端口。另外强烈建议先在一台笔记本上单独跑一个小模型比如 7B 的量化版验证 CPU 推理和依赖安装没有问题再扩展到几十台节点。跳过这一步直接上 70B出了问题会非常难排查。4. 最小示例用 PyTorch 在单机多设备上分片加载模型在“39 台笔记本”的设定里每个节点运行的是独立 Python 进程但我们可以先从更简单的单机多设备分片讲起。理解了设备映射device_map机制跨机器的逻辑也会清晰很多。下面的示例演示如何使用accelerate库把一个大模型分成多个分片加载到同一台机器的不同内存区域或者多张 GPU 卡上。它对应的场景是“模型单机放不下但多块内存合计放得下”。# 文件路径load_shard_demo.py # 依赖pip install accelerate transformers torch from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer from accelerate import init_empty_weights, load_checkpoint_and_dispatch import torch model_path ./llama70b-fp16 # 本地路径确保权重文件存在 config AutoConfig.from_pretrained(model_path) tokenizer AutoTokenizer.from_pretrained(model_path) # 第一阶段空载初始化 # 只创建模型结构不分配权重内存避免一次性申请140GB with init_empty_weights(): model AutoModelForCausalLM.from_config(config) # 第二阶段加载checkpoint并自动分发到不同设备 # max_memory 指定每块设备最多使用多少内存 model load_checkpoint_and_dispatch( model, model_path, device_mapauto, max_memory{ 0: 20GiB, 1: 20GiB, 2: 20GiB, 3: 20GiB, 4: 20GiB, 5: 20GiB, 6: 20GiB, 7: 20GiB, }, dtypetorch.float16, ) # 推理验证无需手工指定设备 inputs tokenizer(Hello, sharding model, return_tensorspt) output model.generate(**inputs, max_new_tokens16) print(tokenizer.decode(output[0], skip_special_tokensTrue))这段代码的核心是load_checkpoint_and_dispatch。它做的事很像数据库分片中的路由注册先扫一遍模型各个子模块的尺寸按max_memory把不同层分派到不同设备上然后在 forward 时自动把中间结果从设备 0 传到设备 1、设备 2……这个机制可以理解成“把分片数据源注册到动态数据源中”的模型版本只是这次注册的不是数据源而是模型层。在单机多设备场景下设备间通信走的是 PCIe 或 NVLink速度快这个方案能跑得很顺。但跨笔记本时通信从“内存拷来拷去”变成了“网络传输”问题就不一样了。5. 进阶示例自定义 RPC 进行跨机器推理当模型的层被分散到不同笔记本上时每个笔记本应该是一个独立的 Python 进程之间通过网络通信。PyTorch 的torch.distributed.rpc是一个现成的分布式通信框架适合构建这类流水线。下面给出一个最小化的“伪分布式”示例。它假设有两台笔记本第一台负责 embedding 和前 3 层第二台负责后 3 层和 lm_head。实际扩展到多台时只需要把层数划分得更多。# 文件路径shard_worker.py # 在每台笔记本上运行通过环境变量指定rank和world_size import os import torch import torch.distributed.rpc as rpc # 全局占位实际工程中应接入真实模型权重 class DummyLayer(torch.nn.Module): def __init__(self, hidden_size1024): super().__init__() self.linear torch.nn.Linear(hidden_size, hidden_size) def forward(self, x): return self.linear(x) class RemoteShard(torch.nn.Module): def __init__(self, layer_start, layer_end): super().__init__() self.layers torch.nn.ModuleList( [DummyLayer() for _ in range(layer_end - layer_start)] ) def forward(self, hidden): for layer in self.layers: hidden layer(hidden) return hidden class Worker: def __init__(self, rank, world_size): self.rank rank self.world_size world_size # 根据rank切分模型层这里假设总共有6层 total_layers 6 layers_per_worker total_layers // (world_size - 1) self.local_layers layers_per_worker if rank (world_size - 1): # 最后一个worker负责剩余的层 self.local_layers total_layers - (world_size - 2) * layers_per_worker self.shard RemoteShard(0, self.local_layers).to(cpu) torch.no_grad() def process(self, hidden): return self.shard(hidden) def run_worker(rank, world_size): worker Worker(rank, world_size) rpc.init_rpc( namefworker{rank}, rankrank, world_sizeworld_size, ) # 把worker实例注册到RPC其他节点可以通过rpc.remote调用 rpc.shutdown() if __name__ __main__: rank int(os.environ[RANK]) world_size int(os.environ[WORLD_SIZE]) run_worker(rank, world_size)这个示例是为了展示跨节点调用模型层的基本形态并不包含真实的模型权重也省略了 embedding 和 lm_head 的划分。它的核心思路是每台笔记本运行同样的shard_worker.py但通过环境变量RANK知道自己是第几个节点。每个节点加载模型的一个层切片并暴露一个process方法供远程调用。逻辑上形成一个链worker0 处理完前几层把隐藏状态传给 worker1以此类推。如果你要让这个流程真正跑起来还需要一个主节点脚本负责把输入 embedding 后按顺序rpc.remote调用每个 worker 的process方法并把最后的输出通过 lm_head 转回 token。这一部分代码涉及具体模型细节这里不再展开。更重要的是理解一个工程判断跨机器分片推理的通信量其实不小。假设 hidden_size 为 8192FP16 下每个 token 的一个隐藏状态大约是 16KB。对于 70B 模型每个层之间传输的是整个序列的隐藏状态如果序列长度是 2048那一层就要传约 32MB。几十个节点累积下来一次前向传播要产生数百 MB 的网络传输这在千兆网下就意味着秒级延迟。这还没算 KV Cache 和多请求并发带来的放大效应。6. 网络瓶颈与效果验证很多人第一次听到“几十台笔记本跑 70B 模型”时第一反应是“CPU 怎么可能算得动”但从工程的视角看CPU 算力反而不是先卡住的地方。真正的问题是网络。笔记本之间在局域网中的数据交换主要依赖两种通道Wi-Fi实际有效带宽通常只有 300Mbps 到 600Mbps而且延迟和抖动很大。千兆有线以太网理论 125MB/s实际往往在 110MB/s 左右。好一点的有 2.5G 网卡但老笔记本基本不具备。对比一下数据中心内部NVLink 在高性能集群上的带宽是几百 GB/s 级别即便用普通 PCIe 网卡走 RDMA也能到几十 GB/s。也就是说笔记本通过千兆网络连接时节点间带宽大约是数据中心的几百分之一。这个差异直接决定了方案取舍张量并行在笔记本集群里不可行。因为矩阵乘法每个小步骤都需要 all-reduce通信频率极高网络会成为不可接受的瓶颈。流水线并行虽然可行但当 batch size 变大、序列变长时中间激活值会迅速膨胀通信时间随序列长度线性增长。如果你真的在笔记本集群上尝试很可能看到一个现象每块 CPU 的占用率并不是 100%因为它在大部分时间等待网络数据。这就是典型的“通信墙”。怎么缓解尽量使用千兆有线网络不要用 Wi-Fi。用小 batch size一次只生成少量序列降低激活值传输量。合理使用 KV Cache避免反复传输历史状态。量化权重把每层的输入输出也尽量做量化压缩例如 INT8 传输到目标节点再反量化。如果可能把多台笔记本按三到四台分成一组组内用更强链路连接组间再走千兆骨干。搭建好集群后怎么知道它真的在正确工作有四个层次第一层权重加载成功。每台笔记本的内存占用符合预期没有 OOM。第二层单步前向传播正确。用测试用例跑一遍比较最后一个节点的输出是否接近非分片模型的输出误差应在允许范围内。第三层token 生成正确。让模型回答一个固定问题结果与单机推理一致或近似一致。第四层吞吐率与延迟达到预期。记录从输入到输出第一个 token 的延迟以及每秒生成 token 数。如果这个数字低到不可接受要能定位是网络等待还是 CPU 计算。具体验证的 shell 命令可以这样准备# 节点1启动推理服务的RPC端口 your-rpc-server --port 50051 # 节点2同样启动 your-rpc-server --port 50051 # 主节点先测两台笔记本之间的网络带宽 iperf3 -c 192.168.1.101如果iperf3测出的 TCP 带宽低于 80MB/s应该先排查网线、交换机端口和网卡速率再继续跑推理实验。另外建议在每台笔记本上开启监控# 实时查看内存和CPU使用情况 watch -n 1 free -h mpstat -P ALL 1 1一旦发现某台机器内存接近上限优先检查权重分配是否均匀。如果只有个别节点内存吃紧说明分片策略的均衡性有问题。7. 常见问题与排查思路问题现象可能原因排查方式解决方案主节点连接 worker 超时防火墙未开放端口检查防火墙规则和网络连通性在每台笔记本上开放 RPC 端口确保同一网段某台笔记本内存 OOM分片不均匀个别节点权重过大查看每台机器的权重分配记录和内存监控调整层划分策略把单台负载过高的层再拆细生成速度极慢CPU 利用率低网络延迟导致节点长期等待数据用 iperf3 测带宽观察网络传输队列改用有线千兆、减小 batch size、启用通信压缩推理结果和单机不一致权重分片错位或 KV Cache 同步错误逐层对比中间激活值误差检查模型层编号映射核对 RPC 参数传递顺序部分笔记本掉线导致整个推理中断节点不稳定、电源或散热问题查看 worker 日志确认是否 OOM 或过热关机增加心跳机制必要时做节点冗余或断点续跑多节点协同但实际吞吐低于单 GPU流水线启动/通信开销过大分别测单节点前向时间和两节点间通信时间考虑减少节点数或合并层数只在必须时跨机器这里特别提醒任何涉及分布式系统的实验都要先在测试环境验证后再上真实业务。模型分片听起来是无损的但参数传递顺序、KV Cache 同步、节点故障恢复都会造成不可预期的问题。不要在没有备份和回滚方案的情况下直接改动生产环境。8. 最佳实践与工程建议如果这个实验对你来说不是单纯的好奇而是想要在真实项目中落地有几条建议非常值得记住。第一不要用笔记本集群做生产推理。它的价值在于验证思想而不在于提供稳定的吞吐能力。哪怕你手头已经有几十台笔记本生产上更稳妥的路线仍然是一台带较大显存的 GPU或云上按需租用。笔记本集群适合做原型验证、教学实验、离线批处理不适合给在线用户提供服务。第二在跨节点分发模型和接入数据源时遵循最小权限原则。每台节点只应拿到它完成自己分片工作所必需的模型文件和数据访问权限不要把所有节点的访问凭证统一暴露给任意一台机器。模型权重本身可能包含敏感能力如果你把它暴露在不受信任的网络上要做访问控制和传输加密。第三节省时间的最优路径是先试现成方案。如果你只是想把一个 70B 模型跑起来验证效果优先考虑 llama.cpp 这类成熟引擎配合它的 RPC 能力
返回列表