加载模型后 .to() 报 Cannot copy out of meta tensor
meta 张量没有权重数据;区分自建骨架与框架 offload,避免用 to_empty 把未初始化权重当成修复。
Cannot copy out of meta tensor; no data! 表示准备复制的张量只保存形状、dtype 等元数据,没有实际数值。
在 Transformers 4.46.3、Accelerate 1.0.1、PyTorch 2.4.1+cu118 的 Llama 3.1/LoRA 加载链中,这个异常可以出现在 PEFT 替换模块的 .to() 处。LoRA 张量未初始化时,需要继续定位具体模块和加载条件;这条加载链的具体修复版本尚未确认,不能把下面的自管骨架示例直接当成 LoRA 或量化加载的修复。
PyTorch 2.5.1 与 2.14.1 的 meta 设备语义都不允许直接把没有数据的 meta_tensor.to("cpu") 当成权重搬运;to_empty 后参数仍未初始化。.to() 不会凭形状恢复数值。不同加载链的原因可能不同,不能把所有 meta 错误归为同一份权重缺失。
先区分是谁建立了 meta 参数
for name, parameter in model.named_parameters():
if parameter.is_meta:
print("parameter", name, tuple(parameter.shape))
for name, buffer in model.named_buffers():
if buffer.is_meta:
print("buffer", name, tuple(buffer.shape))
print(getattr(model, "hf_device_map", None))
若模型由 Accelerate 的分派或 offload 管理,部分 meta 占位可能属于框架的加载机制。此时不要对整模型再执行 .to("cuda"),也不要直接对它调用 to_empty;应保留原分派方式,通过对应框架的加载入口补齐 checkpoint 和模块。异常出现在 forward 时,要沿 traceback 找到具体子模块,再核对该子模块的权重、adapter 和配置是否属于同一模型。
若是自己用 meta 构建骨架,PyTorch 提供的 to_empty 只负责分配空间。下面的 CPU 示例还必须加载数值:
import torch
with torch.device("meta"):
model = torch.nn.Linear(3, 2)
state = {"weight": torch.zeros(2, 3), "bias": torch.ones(2)}
model.to_empty(device="cpu")
model.load_state_dict(state, strict=True)
output = model(torch.zeros(1, 3))
assert torch.equal(output, torch.ones(1, 2))
示例里的零权重与一偏置仅用于检查加载流程。真实模型必须使用自己的完整可信 checkpoint,不能照抄这些值。to_empty 后的参数未初始化;没有报错不代表权重正确。strict=True 能发现键名缺失或多余,仍需检查权重格式、形状和对应模型版本。
对于自管骨架,验收应包括没有残留 meta 参数/缓冲区、checkpoint 键名正确,以及已知输入的输出符合预期。对于框架 offload,别用“所有参数都不在 meta”作为统一标准,应检查分派状态、真实 forward 和目标模块的加载日志。两种加载方式的检查标准混用,容易把正常占位误删,或把未加载权重漏过去。


