ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

多卡跑不动?InternVL3-38B-Instruct split_model 多 GPU 切分原理与实战指南

多卡跑不动?InternVL3-38B-Instruct split_model 多 GPU 切分原理与实战指南

【免费下载链接】InternVL3-38B-Instruct

项目地址:https://ai.gitcode.com/OpenGVLab/InternVL3-38B-Instruct
点击查看免费下载

InternVL3-38B-Instruct 是 OpenGVLab 开源的 38B 参数多模态大模型(MLLM),由 6B 视觉编码器 InternViT-6B + Qwen2.5-32B 语言模型组成。单张消费级甚至 48G 专业卡都装不下它,官方在 README.md 中给出了split_model多 GPU 切分方案。本文带你从零理解这套多卡部署原理:device_map是怎么算出来的、为什么第一张卡按"半张卡"对待、首尾层为什么必须同卡,最后给出一套可直接上手的加载与推理流程,帮助你用多张 80GB GPU 稳定跑起这个模型。

为什么单卡跑不动 InternVL3-38B-Instruct?

先算一笔显存账:

组成部分参数量bf16 权重占用
LLM 语言模型(Qwen2.5-32B)~32B~64 GB
ViT 视觉编码器(InternViT-6B)~6B~12 GB
MLP 投影层 + 推理 KV Cache—数 GB
  • bf16 加载总计约 80 GB 以上,单张 80GB 显卡连权重都放不下,更别提激活值与 KV Cache;
  • 38B 模型总参数(model-00001-of-00016.safetensors 等 16 个分片,可用 model.safetensors.index.json 查看各参数分布)远超单卡容量。

因此必须用张量级/层级的多卡切分(model parallel):把 LLM 的 64 个 Transformer 层(config.json 中num_hidden_layers: 64)拆到多张 GPU 上。

split_model 切分原理拆解

官方split_model函数的完整代码见 README.md。它的核心逻辑可以拆成 4 步:

第 1 步:读取 LLM 层数,确定每卡层数

world_size = torch.cuda.device_count() # 可见 GPU 数 num_layers = config.llm_config.num_hidden_layers # 38B 为 64 num_layers_per_gpu = math.ceil(num_layers / (world_size - 0.5))

注意分母是world_size - 0.5——第一张卡被当成"半张卡"。因为 ViT 视觉编码器(~12GB)固定放在 GPU 0 上,挤占了大量显存,所以 GPU 0 只能承担约一半的 LLM 层数:

num_layers_per_gpu[0] = math.ceil(num_layers_per_gpu[0] * 0.5)

以 3 张 80GB 卡为例:每卡约 64 / 2.5 ≈ 26 层,GPU 0 承担 13 层 + ViT + 首尾层,GPU 1、GPU 2 各承担 26 层,负载大致均衡。

第 2 步:逐层写入 device_map

for i, num_layer in enumerate(num_layers_per_gpu): for j in range(num_layer): device_map[f'language_model.model.layers.{layer_cnt}'] = i layer_cnt += 1

生成的是一张"层号 → 卡号"的映射表,例如 3 卡时:

GPU承载模块
GPU 0ViT、mlp1、embed_tokens、lm_head、layers 0~12、最后一层 63
GPU 1layers 13~38
GPU 2layers 39~62

第 3 步:ViT 与投影层固定到 GPU 0

device_map['vision_model'] = 0 device_map['mlp1'] = 0

图片先进入 GPU 0 的 ViT 做特征提取,再经 MLP 投影送入 LLM 第一层(也在 GPU 0),数据流向最顺,跨卡传输量最小。

第 4 步(关键):首层与尾层同卡

device_map['language_model.model.embed_tokens'] = 0 device_map['language_model.lm_head'] = 0 device_map[f'language_model.model.layers.{num_layers - 1}'] = 0

README 对此有明确解释(README.md):这样做是为了避免多 GPU 推理时张量不在同一设备导致的报错——嵌入层产出、最后一层与 lm_head 消耗的张量必须同设备,把首尾层"闭环"到同一张卡上,就不会出现Expected all tensors to be on the same device这类错误。

硬件配置速查:几张卡才够?

README 在 Inference 章节 给出了官方建议:

加载方式最低硬件要求
load_in_8bit=True(BNB 8-bit 量化)2 × 80GB GPU
load_in_8bit=False(bf16 全精度)3 × 80GB GPU

💡 提示:8-bit 量化会把权重压到约 38GB,两张 80GB 卡分压后仍有充裕空间放 KV Cache;bf16 则三卡起步。

实战:加载模型并跑通推理

以下流程基于 transformers(官方要求transformers>=4.37.2),完整可运行代码在 README.md。

1. 用 split_model 生成 device_map 并加载模型

device_map = split_model('InternVL3-38B') model = AutoModel.from_pretrained( path, torch_dtype=torch.bfloat16, low_cpu_mem_usage=True, use_flash_attn=True, trust_remote_code=True, device_map=device_map).eval()

2. 加载图片与对话

pixel_values = load_image('./examples/image1.jpg', max_num=12).to(torch.bfloat16).cuda() generation_config = dict(max_new_tokens=1024, do_sample=True) question = '<image>\nPlease describe the image shortly.' response = model.chat(tokenizer, pixel_values, question, generation_config)

仓库自带的 examples/ 目录包含示例图片与视频(如 examples/red-panda.mp4),可直接验证单图对话、多图对比、视频理解等场景。

常见坑与排查清单

问题原因与对策
tensors not on the same device报错首尾层未同卡。确认device_map中embed_tokens、lm_head与最后一层都指向 GPU 0
GPU 0 显存爆、其他卡空闲忘了 ViT 占 12GB,第一张卡要按"半卡"计算层数,即world_size - 0.5的算法
卡数不够导致加载失败bf16 至少 3 × 80GB;可改用load_in_8bit=True降为 2 × 80GB
trust_remote_code缺失InternVL3 使用自定义建模代码 modeling_internvl_chat.py(配置入口 configuration_internvl_chat.py),加载时必须传trust_remote_code=True
想显式指定某些卡用CUDA_VISIBLE_DEVICES=0,1,2控制可见 GPU,split_model会自动按可见卡数计算

进阶:生产部署可换用 LMDeploy

split_model方案适合验证与小规模推理。如果要做高并发服务,官方推荐使用 LMDeploy(见 README.md):

lmdeploy serve api_server OpenGVLab/InternVL3-38B \ --chat-template internvl2_5 --server-port 23333 --tp 2

它通过 Tensor Parallelism(--tp 2)实现张量并行,配合 OpenAI 兼容接口,吞吐和稳定性都优于手动切层方案。

总结

InternVL3-38B-Instruct 的多卡部署核心就是三句话:

  1. 64 层 LLM 逐层切分:split_model按world_size - 0.5均分层数,生成device_map;
  2. 第一张卡按半卡算:ViT + mlp1 固定在 GPU 0,所以它少分一半 LLM 层;
  3. 首尾层必须同卡:embed_tokens、lm_head、最后一层都放 GPU 0,规避跨设备张量报错。

记住这套原理后,无论是 2 卡量化还是 3 卡全精度,你都能自行调整分配策略,把这个 38B 多模态模型稳稳跑起来。

【免费下载链接】InternVL3-38B-Instruct

项目地址:https://ai.gitcode.com/OpenGVLab/InternVL3-38B-Instruct
点击查看免费下载
上一篇:深蓝词库转换:一站式输入法词库跨平台迁移工具
下一篇:7个技巧让Fiji成为你的科研效率倍增器:从图像分析到成果发表的7天蜕变指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

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