【问题标题】:EOFError when loading list of dicts with various datatypes加载具有各种数据类型的字典列表时出现 EOFError
【发布时间】:2020-02-29 22:12:20
【问题描述】:

我有字典列表,其中每个字典包含多个不同的项目。它在强化学习的训练过程中用作记忆回放,我需要创建一个备份文件以防万一该过程中断。每个 dict 代表一个环境步骤,因此结构相同,只有值不同。

每个 dict 包含的数据类型有:numpy.array、int、bool、string、list 的 numpy 数组和 float。 Np 数组经过预处理,因此它包含实数浮点数 - 没有 NaN 或 Inf。

我的问题是,我尝试了多种方法,如何存储和加载文件,并且所有文件的行为都相似 - 创建备份没有问题,但有时(甚至根本没有 - 根本没有模式可以在其他地方发现错误) 加载它时,它会引发 EOFError。

列表中的最大数据量现在限制为 100k,创建的文件通常在 128MB 左右。

目前我正在通过 pickle.dumps 进行尝试,但过去我尝试过使用普通的 joblib 转储/加载和 copy.deepcopy 进行转储。

def _save_backup(self, path:str, name:str) -> dict:
    file_path = path+name+'_memory.joblib'
    with open(file_path, "wb") as f:
        serialized_mem = pickle.dumps(self._memory,protocol=pickle.HIGHEST_PROTOCOL)
        dump(serialized_mem,f)    
    return {'memory':file_path}


def _load_backup(self, data:dict):
    if os.path.exists(data['memory']):
        with open(data['memory'], "rb") as f:
            serialized_mem = load(f)
            self._memory = pickle.loads(serialized_mem)

编辑

回复tdelaney评论:

  • 加载和转储来自joblib

from joblib import dump, load import pickle

  • 想法是通过pickle.dumps将对象序列化为字符串,因为它只创建字符串但不保存到文件中,然后我使用joblib.dump来创建这样的文件。

  • 确实在serialized_mem = load(f)中引发了错误

  • 保存过程中不会抑制任何异常

  • dump 后,文件路径作为 dict(以保持基类的继承)传递给主类,并与其他备份文件路径(如神经网络、优化器等)合并。

  • 错误文件大小不是确定性的 - 有时它会在列表达到其全部容量(100k 个样本)之前失败,然后它的 ofc 更小,有时会在例如 500k 步之后发生错误,因此大小是正常的。但是你让我记住了一个非常重要的细节...... self._memory 实际上是 deque 类型(from collections import deque)(它从父级继承并允许像使用列表一样使用它,就是这样为什么我忘了它)。作为双端队列,保存“未满”对象可能会出现问题,这可以解释 EOFError 的含义。我会做一些测试并报告结果。

【问题讨论】:

  • dump()load() 是什么?你是双重腌制吗? load(f) 的错误在哪里?失败时文件的大小是否合理?您是否正在捕获和抑制可能在转储期间发生的异常,从而导致文件缩略?
  • @tdelaney 我已经添加了您要求的详细信息。关于文件大小的问题给了我一个想法,我将对其进行测试。

标签: python pickle joblib


【解决方案1】:

几天没有错误,所以我猜它已修复。最后我使用了一些不同的东西,因为我在保存过程中遇到了MemoryError,同时在 32GB RAM 上运行 64 位 python 版本,其中一半以上可用。

这个实现似乎在加载时解决了MemoryErrorEOFError

def save_backup(self, path:str, name:str) -> dict:
    # save memory
    file_path = path+name+'_memory.joblib'
    with open(file_path, "wb") as f:
        for data in list(self._memory):
            pickle.dump(data,f)  
    # save other files
    d = self._save_backup(path,name) # child class method
    # merge dicts
    d.update({'memory':file_path})
    return d


def load_backup(self, data:dict):
    # load memory
    if os.path.exists(data['memory']):
        with open(data['memory'], "rb") as f:
            self._memory.clear()
            while True:
                try:
                    self._memory.append(pickle.load(f))
                except EOFError:
                    break
    # load others
    self._load_backup(data) # child class method

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-24
    • 1970-01-01
    • 2011-05-24
    • 2014-04-29
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多