Python高效存储指南:tensor/list/numpy array/dict本地化实战

Python高效存储指南:tensor/list/numpy array/dict本地化实战 1. 为什么需要本地化存储Python数据结构在日常的Python开发中我们经常需要处理各种数据结构比如张量(tensor)、列表(list)、numpy数组(numpy array)和字典(dict)。这些数据可能来自机器学习模型的输出、数据处理的结果或者是程序运行时的中间状态。如果每次运行程序都要重新生成这些数据不仅浪费时间还可能因为数据源的变化导致结果不一致。举个例子我最近在做一个图像分类项目训练好的模型每次推理都会产生大量中间张量。如果每次都重新训练模型获取这些张量光等待时间就要好几个小时。后来我把这些张量保存到本地后续分析时直接加载效率提升了十几倍。本地化存储还有以下好处数据持久化程序关闭后数据不会丢失数据共享团队成员可以复用相同的数据调试方便可以保存特定状态用于问题排查性能优化避免重复计算消耗资源2. 张量(tensor)的存储与读取张量是深度学习中最常用的数据结构PyTorch提供了非常方便的存储方法。先来看一个基础示例import torch # 创建一个随机张量 x torch.rand(4, 5) # 保存到本地 torch.save(x, myTensor.pth) # 从本地加载 y torch.load(myTensor.pth) print(y)这里有几个实用技巧值得注意设备转换如果张量在GPU上保存前最好转到CPUtorch.save(x.to(torch.device(cpu)), gpu_tensor.pth)压缩存储大张量可以启用压缩torch.save(x, compressed.pth, _use_new_zipfile_serializationTrue)多张量存储可以保存多个张量到一个文件torch.save({tensor1: x, tensor2: y}, multi_tensors.pth)实际项目中我遇到过保存模型中间层输出的需求。这时候可以这样操作# 获取模型某层的输出 activation {} def get_activation(name): def hook(model, input, output): activation[name] output.detach() return hook model.layer1.register_forward_hook(get_activation(layer1)) # 前向传播后保存激活值 output model(input_data) torch.save(activation, activations.pth)3. 列表(list)的多种存储方式列表是Python中最灵活的数据结构之一根据使用场景不同我们有多种存储选择。3.1 保存为.npy格式numpy的.npy格式特别适合存储数值型列表import numpy as np a [(9000023330249, 1), (13142928, 1), (9000084906496, 1)] # 保存 np.save(a.npy, a) # 读取 b np.load(a.npy, allow_pickleTrue) b b.tolist() # 转换回列表这种方法的特点是二进制格式读写速度快支持高维列表存储文件体积相对较小3.2 保存为.txt文本文件对于需要人工查看的列表文本格式更合适list_data [1, 2, 3, a, b, c] # 写入 with open(list.txt, w) as f: f.write(str(list_data)) # 读取 with open(list.txt, r) as f: loaded_list eval(f.read())注意使用eval有安全风险如果数据来源不可信建议改用ast.literal_evalimport ast loaded_list ast.literal_eval(f.read())3.3 保存为JSON格式当列表包含复杂结构时JSON是个好选择import json complex_list [ {name: Alice, scores: [88, 92, 95]}, {name: Bob, scores: [78, 85, 90]} ] # 保存 with open(data.json, w) as f: json.dump(complex_list, f) # 读取 with open(data.json, r) as f: loaded_list json.load(f)JSON的优点是可读性好跨语言支持但要注意只能存储基本数据类型str, int, float, bool, None自定义对象需要额外处理文件体积通常比二进制格式大4. numpy数组的高效存储方案numpy数组是科学计算的核心数据结构numpy提供了多种存储方式。4.1 基础.npy格式import numpy as np arr np.random.rand(1000, 1000) # 保存单个数组 np.save(array.npy, arr) # 读取 loaded_arr np.load(array.npy)对于大型数组可以指定mmap_mode实现内存映射large_arr np.load(big_array.npy, mmap_moder)4.2 多个数组存储.npzarr1 np.arange(10) arr2 np.random.rand(5,5) # 保存多个数组 np.savez(arrays.npz, arr1arr1, arr2arr2) # 读取 data np.load(arrays.npz) print(data[arr1]) print(data[arr2]).npz文件实际上是压缩包可以用compressed参数进一步压缩np.savez_compressed(compressed.npz, big_arrayhuge_arr)4.3 文本格式存储虽然性能不如二进制格式但文本格式便于与其他程序交互# 保存为文本 np.savetxt(array.txt, arr, delimiter,) # 读取 arr_from_txt np.loadtxt(array.txt, delimiter,)对于高维数组可以先用reshape平铺# 保存3D数组 np.savetxt(3darray.txt, arr_3d.reshape(-1, arr_3d.shape[-1])) # 读取后恢复形状 loaded np.loadtxt(3darray.txt).reshape(original_shape)5. 字典(dict)的持久化方案字典是Python中极其重要的数据结构存储时需要考虑值的类型。5.1 JSON格式存储import json user_data { name: 张三, age: 30, skills: [Python, 机器学习] } # 保存 with open(user.json, w, encodingutf-8) as f: json.dump(user_data, f, ensure_asciiFalse, indent2) # 读取 with open(user.json, r, encodingutf-8) as f: loaded_dict json.load(f)当字典中包含numpy数组时需要转换data {vector: np.random.rand(10)} # 保存前转换 json_ready {k: v.tolist() if isinstance(v, np.ndarray) else v for k, v in data.items()} with open(data.json, w) as f: json.dump(json_ready, f)5.2 使用pickle存储复杂字典对于包含自定义对象的字典可以使用pickleimport pickle complex_dict { model: your_model_instance, config: {lr: 0.01, epochs: 100} } # 保存 with open(model_data.pkl, wb) as f: pickle.dump(complex_dict, f) # 读取 with open(model_data.pkl, rb) as f: loaded pickle.load(f)注意pickle有安全风险不要加载不可信的pickle文件。5.3 使用PyTorch保存字典PyTorch也可以用来保存字典特别是当值包含张量时data { weights: torch.randn(10,10), metadata: {created: 2023-01-01} } torch.save(data, data.pth) loaded torch.load(data.pth)6. 性能对比与最佳实践不同的存储格式在速度和文件大小上差异很大。我做过一个实测对比数据类型存储格式文件大小写入时间读取时间浮点张量(1MB).pth1.0MB15ms10ms同上的张量.npy1.0MB12ms8ms同上张量.txt2.3MB210ms180ms字典(含数组).json1.5MB25ms20ms同上字典.pkl0.9MB18ms12ms根据实测结果我总结了一些最佳实践纯数值数据优先使用.npy或.pth格式配置类数据用JSON更易维护临时存储可以用pickle但生产环境慎用超大文件考虑分块存储或使用内存映射对于特别大的数据集建议使用HDF5格式import h5py with h5py.File(big_data.h5, w) as f: f.create_dataset(dataset1, databig_array) f.attrs[metadata] json.dumps(meta_info)最后分享一个实用技巧存储时可以添加版本信息方便后续兼容处理data { __version__: 1.0, created: 2023-08-20, payload: real_data }