【Bug已解决】PyTorch - RuntimeError: [enforce fail at inline_container.cc:209] . file not found: archive/data.pkl 解决方案问题描述在 PyTorch 中使用torch.jit.save()和torch.jit.load()保存和加载 TorchScript 模型时开发者可能会遇到以下错误RuntimeError: [enforce fail at inline_container.cc:209] . file not found: archive/data.pkl这个错误发生在尝试加载一个 TorchScript 模型文件.pt或.pth时表示模型文件格式不正确或文件已损坏。具体表现为使用torch.jit.load()加载模型时报错模型文件在保存过程中被中断或损坏使用错误的 API 保存模型如用torch.jit.save保存非 TorchScript 模型文件传输过程中损坏如通过 FTP、Git LFS 等PyTorch 版本不兼容导致文件格式变化磁盘空间不足导致保存不完整错误复现场景一用torch.save保存的文件用torch.jit.load加载import torch import torch.nn as nn class SimpleModel(nn.Module): def __init__(self): super().__init__() self.fc nn.Linear(10, 5) def forward(self, x): return self.fc(x) # 使用 torch.save 保存普通模型 model SimpleModel() torch.save(model.state_dict(), model_state_dict.pt) # 错误用 torch.jit.load 加载 loaded torch.jit.load(model_state_dict.pt) # 报错报错RuntimeError: [enforce fail at inline_container.cc:209] . file not found: archive/data.pkl场景二TorchScript 模型保存不完整import torch import torch.nn as nn class SimpleModel(nn.Module): def __init__(self): super().__init__() self.fc nn.Linear(10, 5) def forward(self, x): return self.fc(x) # 创建 TorchScript 模型 model SimpleModel() scripted torch.jit.script(model) # 保存假设磁盘空间不足或被中断 torch.jit.save(scripted, model_scripted.pt) # 文件可能不完整 loaded torch.jit.load(model_scripted.pt) # 可能报错场景三文件传输损坏# 在一台机器上保存 torch.jit.save(scripted, model.pt) # 通过网络传输后如 scp、FTP、Git LFS # 文件可能损坏 # 在另一台机器上加载 loaded torch.jit.load(model.pt) # 报错场景四PyTorch 版本不兼容# 在 PyTorch 2.0 上保存 torch.jit.save(scripted, model_v2.pt) # 在 PyTorch 1.10 上加载 loaded torch.jit.load(model_v2.pt) # 可能报错根因分析1.torch.savevstorch.jit.save的区别这是最常见的混淆点torch.save(obj, path)保存任意 Python 对象包括 state_dict、完整模型等使用 Python 的 pickle 协议。文件内部结构是archive/data.pkl。torch.jit.save(model, path)保存 TorchScript 模型ScriptModule 或 ScriptFunction使用专门的序列化格式。文件内部结构包含archive/data.pkl和archive/code/等。当用torch.jit.load()加载一个由torch.save()保存的文件时文件格式不匹配导致找不到archive/data.pkl。2. TorchScript 模型的文件结构TorchScript 模型保存为 ZIP 格式的归档文件内部结构如下archive/ ├── data.pkl # 序列化的模型数据 ├── data/ # 张量数据 │ ├── 0 │ ├── 1 │ └── ... ├── code/ # 模型代码 │ └── __torch__.py └── constants.json # 常量如果data.pkl文件缺失说明归档不完整或格式错误。3. 文件损坏的原因磁盘空间不足保存过程中磁盘空间耗尽文件被截断网络传输错误通过 FTP、HTTP、scp 等传输时发生错误Git LFS 问题Git LFS 没有正确拉取大文件并发写入冲突多个进程同时写入同一文件文件系统错误存储介质故障4. PyTorch 版本兼容性不同版本的 PyTorch 可能使用不同的 TorchScript 序列化格式。新版本保存的模型可能包含旧版本不支持的特性。5. 混淆.pt和.pth扩展名虽然扩展名不影响文件内容但约定俗成.pt或.pthtorch.save保存的文件state_dict 或模型.pttorch.jit.save保存的 TorchScript 模型混用扩展名可能导致混淆。解决方案方案一使用正确的保存/加载 APIimport torch import torch.nn as nn class SimpleModel(nn.Module): def __init__(self): super().__init__() self.fc nn.Linear(10, 5) def forward(self, x): return self.fc(x) model SimpleModel() # 方式1: 保存/加载 state_dict推荐用于训练 torch.save(model.state_dict(), model_state_dict.pth) # 加载 model_loaded SimpleModel() # 需要先创建模型实例 model_loaded.load_state_dict(torch.load(model_state_dict.pth)) model_loaded.eval() # 方式2: 保存/加载 TorchScript 模型推荐用于部署 scripted torch.jit.script(model) torch.jit.save(scripted, model_scripted.pt) # 加载 loaded torch.jit.load(model_scripted.pt) loaded.eval() # 方式3: 保存/加载完整模型不推荐 torch.save(model, model_complete.pth) # 加载 loaded_model torch.load(model_complete.pth)方案二验证文件完整性import torch import zipfile import os def validate_torchscript_model(filepath): 验证 TorchScript 模型文件的完整性 if not os.path.exists(filepath): print(f[ERROR] 文件不存在: {filepath}) return False file_size os.path.getsize(filepath) print(f文件大小: {file_size} bytes) if file_size 100: print(f[ERROR] 文件太小 ({file_size} bytes)可能已损坏) return False # 检查是否为有效的 ZIP 文件TorchScript 模型是 ZIP 格式 if not zipfile.is_zipfile(filepath): print(f[ERROR] 不是有效的 ZIP 文件) return False # 检查内部结构 try: with zipfile.ZipFile(filepath, r) as zf: names zf.namelist() print(f归档内容: {names}) # 检查必需的文件 required_files [archive/data.pkl] for req in required_files: if req not in names: print(f[ERROR] 缺少必需文件: {req}) return False print([OK] 文件结构验证通过) return True except zipfile.BadZipFile: print(f[ERROR] ZIP 文件损坏) return False # 使用 validate_torchscript_model(model_scripted.pt)方案三安全保存防止文件损坏import torch import os import tempfile def safe_save_torchscript(model, filepath): 安全保存 TorchScript 模型原子写入 # 先写入临时文件 temp_fd, temp_path tempfile.mkstemp(suffix.pt) try: with os.fdopen(temp_fd, wb) as f: torch.jit.save(model, temp_path) # 验证临时文件 try: test_load torch.jit.load(temp_path) del test_load except RuntimeError as e: raise RuntimeError(f保存的文件验证失败: {e}) # 原子性地重命名在 Unix 上是原子操作 os.replace(temp_path, filepath) print(f模型已安全保存到 {filepath}) except Exception: # 清理临时文件 if os.path.exists(temp_path): os.remove(temp_path) raise def safe_save_state_dict(state_dict, filepath): 安全保存 state_dict原子写入 temp_fd, temp_path tempfile.mkstemp(suffix.pth) try: with os.fdopen(temp_fd, wb) as f: torch.save(state_dict, temp_path) os.replace(temp_path, filepath) print(fState dict 已安全保存到 {filepath}) except Exception: if os.path.exists(temp_path): os.remove(temp_path) raise完整修复代码以下是一个完整的模型保存/加载工具处理各种场景 PyTorch 模型保存/加载完整工具 处理 torch.save / torch.jit.save / torch.jit.load 的各种问题 import torch import torch.nn as nn import os import zipfile import tempfile import shutil from typing import Optional, Dict, Any class ModelManager: 模型保存/加载管理器 staticmethod def save_state_dict( model: nn.Module, filepath: str, optimizer: Optional[torch.optim.Optimizer] None, epoch: int 0, metadata: Optional[Dict] None, safe: bool True ): 保存模型 state_dict推荐用于训练检查点 Args: model: PyTorch 模型 filepath: 保存路径 optimizer: 优化器可选 epoch: 当前 epoch metadata: 额外的元数据 safe: 是否使用安全写入原子操作 checkpoint { epoch: epoch, state_dict: model.state_dict(), model_class: model.__class__.__name__, } if optimizer is not None: checkpoint[optimizer] optimizer.state_dict() if metadata: checkpoint[metadata] metadata if safe: ModelManager._safe_save(checkpoint, filepath) else: torch.save(checkpoint, filepath) print(fState dict saved to {filepath}) staticmethod def load_state_dict( model: nn.Module, filepath: str, optimizer: Optional[torch.optim.Optimizer] None, device: str cpu, strict: bool True ) - Dict[str, Any]: 加载模型 state_dict Returns: 包含 epoch 和 metadata 的字典 if not os.path.exists(filepath): raise FileNotFoundError(f文件不存在: {filepath}) checkpoint torch.load(filepath, map_locationdevice) if state_dict in checkpoint: model.load_state_dict(checkpoint[state_dict], strictstrict) if optimizer is not None and optimizer in checkpoint: optimizer.load_state_dict(checkpoint[optimizer]) return { epoch: checkpoint.get(epoch, 0), metadata: checkpoint.get(metadata, {}) } else: # 可能是直接的 state_dict model.load_state_dict(checkpoint, strictstrict) return {} staticmethod def save_torchscript( model: nn.Module, filepath: str, example_input: Optional[torch.Tensor] None, safe: bool True ): 保存 TorchScript 模型推荐用于部署 Args: model: PyTorch 模型 filepath: 保存路径 example_input: 用于 tracing 的示例输入如果使用 trace 方式 safe: 是否使用安全写入 # 转换为 TorchScript try: scripted torch.jit.script(model) print(使用 torch.jit.script 转换成功) except Exception as e: print(ftorch.jit.script 失败: {e}) if example_input is not None: print(尝试使用 torch.jit.trace...) scripted torch.jit.trace(model, example_input) print(torch.jit.trace 转换成功) else: raise RuntimeError( torch.jit.script 失败且未提供 example_input 用于 trace ) if safe: ModelManager._safe_save_torchscript(scripted, filepath) else: torch.jit.save(scripted, filepath) print(fTorchScript model saved to {filepath}) staticmethod def load_torchscript(filepath: str, device: str cpu) - torch.jit.ScriptModule: 加载 TorchScript 模型 Raises: FileNotFoundError: 文件不存在 RuntimeError: 文件格式错误或损坏 if not os.path.exists(filepath): raise FileNotFoundError(f文件不存在: {filepath}) # 验证文件 if not ModelManager.validate_torchscript_file(filepath): raise RuntimeError(f文件验证失败: {filepath}) try: model torch.jit.load(filepath, map_locationdevice) model.eval() print(fTorchScript model loaded from {filepath}) return model except RuntimeError as e: if file not found: archive/data.pkl in str(e): raise RuntimeError( f文件格式错误。请确认:\n f 1. 文件是使用 torch.jit.save() 保存的\n f 2. 文件没有损坏\n f 3. PyTorch 版本兼容\n f原始错误: {e} ) raise staticmethod def validate_torchscript_file(filepath: str) - bool: 验证 TorchScript 文件的完整性 if not os.path.exists(filepath): return False file_size os.path.getsize(filepath) if file_size 50: print(f[WARNING] 文件太小 ({file_size} bytes)) return False if not zipfile.is_zipfile(filepath): print(f[ERROR] 不是有效的 ZIP 格式) return False try: with zipfile.ZipFile(filepath, r) as zf: names zf.namelist() if archive/data.pkl not in names: print(f[ERROR] 缺少 archive/data.pkl) print(f 文件内容: {names[:10]}) return False return True except zipfile.BadZipFile: return False staticmethod def _safe_save(obj, filepath): 安全保存原子写入 temp_path filepath .tmp try: torch.save(obj, temp_path) os.replace(temp_path, filepath) except Exception: if os.path.exists(temp_path): os.remove(temp_path) raise staticmethod def _safe_save_torchscript(model, filepath): 安全保存 TorchScript原子写入 temp_path filepath .tmp try: torch.jit.save(model, temp_path) # 验证 test torch.jit.load(temp_path) del test os.replace(temp_path, filepath) except Exception: if os.path.exists(temp_path): os.remove(temp_path) raise staticmethod def diagnose_file(filepath: str): 诊断模型文件 print( * 60) print(f诊断文件: {filepath}) print( * 60) if not os.path.exists(filepath): print([ERROR] 文件不存在) return file_size os.path.getsize(filepath) print(f文件大小: {file_size:,} bytes ({file_size/1024/1024:.2f} MB)) # 检查是否为 ZIP 文件 is_zip zipfile.is_zipfile(filepath) print(fZIP 格式: {is_zip}) if is_zip: with zipfile.ZipFile(filepath, r) as zf: names zf.namelist() print(f归档文件数: {len(names)}) print(f内容列表:) for name in names[:20]: info zf.getinfo(name) print(f {name} ({info.file_size:,} bytes)) if len(names) 20: print(f ... 和其他 {len(names) - 20} 个文件) # 尝试用不同方式加载 print(f\n尝试加载:) # 尝试 torch.load try: obj torch.load(filepath, map_locationcpu) print(f torch.load 成功: {type(obj)}) if isinstance(obj, dict): print(f 字典键: {list(obj.keys())[:10]}) except Exception as e: print(f torch.load 失败: {str(e)[:100]}) # 尝试 torch.jit.load try: model torch.jit.load(filepath, map_locationcpu) print(f torch.jit.load 成功: {type(model)}) except Exception as e: print(f torch.jit.load 失败: {str(e)[:100]}) # 诊断结论 print(f\n诊断结论:) if is_zip and not any(data.pkl in n for n in names if archive in n): print( 文件可能是 torch.save 保存的不是 TorchScript 模型) print( 使用 torch.load() 加载而不是 torch.jit.load()) elif is_zip: print( 文件是 TorchScript 模型) print( 使用 torch.jit.load() 加载) # 使用示例 class SimpleModel(nn.Module): def __init__(self, input_dim784, hidden_dim256, output_dim10): super().__init__() self.fc1 nn.Linear(input_dim, hidden_dim) self.fc2 nn.Linear(hidden_dim, output_dim) def forward(self, x): x torch.relu(self.fc1(x)) return self.fc2(x) def demo_correct_usage(): 演示正确的保存/加载方式 print( * 60) print(正确的保存/加载方式) print( * 60) model SimpleModel() # 1. 保存 state_dict ModelManager.save_state_dict(model, demo_state_dict.pth, epoch10) # 加载 state_dict new_model SimpleModel() info ModelManager.load_state_dict(new_model, demo_state_dict.pth) print(f恢复的 epoch: {info.get(epoch)}) # 2. 保存 TorchScript ModelManager.save_torchscript(model, demo_torchscript.pt) # 加载 TorchScript loaded ModelManager.load_torchscript(demo_torchscript.pt) # 测试 x torch.randn(1, 784) output1 model(x) output2 loaded(x) print(f输出差异: {(output1 - output2).abs().max().item():.2e}) # 清理 import os os.remove(demo_state_dict.pth) os.remove(demo_torchscript.pt) def demo_error_scenario(): 演示错误场景和修复 print(\n * 60) print(错误场景演示) print( * 60) model SimpleModel() # 错误用 torch.save 保存用 torch.jit.load 加载 torch.save(model.state_dict(), wrong_format.pt) print(用 torch.save 保存了 state_dict) # 诊断 ModelManager.diagnose_file(wrong_format.pt) # 正确加载 print(\n正确加载方式:) checkpoint torch.load(wrong_format.pt) print(f 加载成功: {type(checkpoint)}) import os os.remove(wrong_format.pt) def main(): print(\n * 60) print( PyTorch 模型保存/加载工具) print( * 60) demo_correct_usage() demo_error_scenario() print(\n * 60) print( 演示完成) print( * 60) if __name__ __main__: main()常见陷阱与注意事项1.torch.save和torch.jit.save不能混用最常见的错误是混淆这两个 APItorch.save()保存的是 Python pickle 对象state_dict 或模型torch.jit.save()保存的是 TorchScript 模型用错误的 API 加载会导致file not found: archive/data.pkl错误。2. TorchScript 模型的版本兼容性TorchScript 模型在不同 PyTorch 版本之间可能不兼容。新版本保存的模型可能包含旧版本不支持的算子或特性。建议在相同或兼容的 PyTorch 版本之间共享 TorchScript 模型。3. Git LFS 和大文件如果模型文件通过 Git 管理确保使用 Git LFS 存储大文件。没有 LFSGit 可能会截断或损坏大文件# 安装 Git LFS git lfs install # 跟踪模型文件 git lfs track *.pt git lfs track *.pth # 提交 git add .gitattributes git add model.pt git commit -m Add model4. 磁盘空间检查保存模型前检查磁盘空间import shutil total, used, free shutil.disk_usage(/) print(f可用空间: {free / 1024**3:.2f} GB)5. 文件权限确保对目标目录有写入权限。在 Docker 容器中可能需要以正确的用户身份运行。6. 并发写入不要让多个进程同时写入同一个文件。使用文件锁或原子写入如本文的safe_save方法来避免并发写入问题。7.torch.jit.tracevstorch.jit.scripttorch.jit.trace通过运行模型记录操作不支持控制流if/for/whiletorch.jit.script分析 Python 代码支持控制流如果模型有动态控制流必须使用torch.jit.script。使用trace可能导致不正确的 TorchScript 模型。8. 加载时的map_location加载模型时指定map_location以避免设备不匹配问题# 在 CPU 上加载 GPU 模型 model torch.jit.load(model.pt, map_locationcpu) # 在 GPU 上加载 model torch.jit.load(model.pt, map_locationcuda:0)9. 模型文件的安全验证在生产环境中加载模型文件前始终验证文件的完整性和来源。恶意构造的模型文件可能包含安全风险。10.torch.load的weights_only参数从 PyTorch 2.6 开始torch.load默认使用weights_onlyTrue只加载张量数据而不执行任意代码。如果加载包含复杂 Python 对象的 checkpoint可能需要设置weights_onlyFalse# PyTorch 2.6 checkpoint torch.load(model.pth, weights_onlyFalse)总结RuntimeError: [enforce fail at inline_container.cc:209] . file not found: archive/data.pkl错误的根本原因是文件格式与加载 API 不匹配或文件已损坏。解决步骤如下确认使用正确的 APItorch.save/torch.load用于普通模型torch.jit.save/torch.jit.load用于 TorchScript 模型验证文件完整性使用zipfile检查文件是否为有效的 ZIP 归档使用安全保存通过临时文件 原子重命名避免文件损坏检查 PyTorch 版本兼容性确保保存和加载使用兼容的版本使用诊断工具通过ModelManager.diagnose_file快速定位问题通过本文提供的ModelManager工具类可以系统化地处理模型保存/加载的各种问题确保模型文件的安全性和可加载性。记住在部署环境中始终验证模型文件的完整性并使用正确的 API 进行加载。