pickle/joblib加载序列化文件内存占用高于原始读取问题咨询
现有一套规模较大的数据集,由分布在不同目录下的数千个文件组成,这些文件格式不统一,来自不同采样率的各类传感器,数据整体零散混乱、规整难度较高。自行开发的Python处理模块可自动遍历对应目录完成数据解析、格式转换,最终将数据整合为pandas DataFrame结构,支持高效便捷的重采样等操作,降低数据使用门槛。
规整得到的DataFrame体积较大,内存占用高,同时加载多个此类数据集时,剩余内存无法支撑机器学习模型训练;且直接读取分散的原始文件速度极慢,严重影响使用效率。
采用两阶段方案处理该问题:
- 第一阶段:先将全量数据集读取为一个大变量,结构为嵌套
pandasDataFrame的dict,从中提取模型训练所需的核心字段,计算得到精简的派生DataFrame后,从内存中删除存储全量数据的dict变量。该方案虽不完美,但可满足基本使用需求,不过存在明显缺陷:后续计算若需用到全量数据,必须重新读取原始散文件,耗时极长。 - 第二阶段:参考
sklearn官方推荐,在删除全量dict变量前,使用joblib将其序列化为本地文件。后续读取该单序列化文件的速度比读取跨目录散文件快约90%,效率提升显著,核心原因是直接加载单个大文件无需遍历数千个散文件、也无需重复执行格式转换逻辑。
使用中发现内存占用存在异常:相同处理逻辑下,从分散原始文件读取生成全量数据结构时,最终进程内存占用比加载joblib序列化的同内容文件低约70%——即序列化加载方式虽然读取速度更快,但内存开销大幅升高,希望确认是否有其他开发者遇到过同类问题。
受数据存储位置(存放在有特殊访问限制的网络驱动器)以及面向普通用户的易用性要求,采用Jupyter Notebook作为交付载体:IT部门提供的托管Web版Jupyter平台预置了所有依赖,可直接访问网络驱动器,用户仅需登录即可使用,无需掌握Linux系统运维知识;若采用虚拟机(VM)方案,则需要用户手动配置网络盘挂载,易用性极差。当前运行环境为Python 3.9.6。
目前正在尝试编写可复现该问题的最小可复现示例(MWE),但已写出的测试用例呈现出相反的结果:加载序列化数据集的内存占用比直接读取生成数据更低,推测该差异可能与真实数据中嵌套DataFrame的dict的特殊结构有关。
附最小测试示例(警告:运行该代码会在硬盘生成约4GB大小的文件):
import numpy as np import psutil from os.path import exists from os import getpid from joblib import dump, load ## WARNING. THIS CODE SAVES A LARGE FILE INTO YOUR HARD DRIVE def read_compute(): if exists('df.joblib'): df = load('df.joblib') print('==== df loaded from .joblib') else: df = np.random.rand(1000000,500) dump(df, 'df.joblib') print('=== df created and dumped') tab = df[:100, :10] del df return tab table = read_compute() print(f'{psutil.Process(getpid()).memory_info().rss / 1024 ** 2} MB')
当工作目录下不存在df.joblib文件时,运行输出为:
=== df created and dumped 3899.62890625 MB
待文件生成后重启内核,再次运行相同代码,输出为:
==== df loaded from .joblib 1588.5234375 MB
该测试结果与真实业务场景中的表现完全相反:真实场景下加载joblib序列化文件的内存占用远高于读取原始散文件的内存占用。
内容的提问来源于stack exchange,提问作者phollox

