代码之家  ›  专栏  ›  技术社区  ›  danben

使用Pickle/cPickle达到最大递归深度

  •  46
  • danben  · 技术社区  · 15 年前

    背景:我正在使用最小构造算法构建一个表示字典的trie。输入列表为4.3M utf-8字符串,按字典顺序排序。生成的图是非循环的,最大深度为638个节点。我的脚本的第一行将递归限制设置为1100 sys.setrecursionlimit()

    问题是:我希望能够将trie序列化到磁盘,这样就可以将其加载到内存中,而不必从头开始重建(大约22分钟)。我两者都试过了 pickle.dump() cPickle.dump()

      File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 649, in save_dict
        self._batch_setitems(obj.iteritems())
      File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 663, in _batch_setitems
        save(v)
      File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 725, in save_inst
        save(stuff)
      File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 648, in save_dict
        self.memoize(obj)
    RuntimeError: maximum recursion depth exceeded
    

    trie 包含对开始状态的引用,并定义一些方法。 dfa_state 包含布尔字段、字符串字段和从标签到状态的字典映射。

    我不太熟悉计算机的内部工作原理 pickle -对于某些n,我的最大递归深度是否需要大于/等于trie深度的n倍?或者这是我不知道的其他原因造成的?

    将递归深度设置为3000没有帮助,因此这条途径看起来不太有希望。

    你们是对的;由于默认的递归限制,我认为pickle将使用较小的嵌套深度是短视的。1万人成功了。

    5 回复  |  直到 7 年前
        1
  •  41
  •   Jason Coon    15 年前

    从…起 the docs :

    sys.setrecursionlimit() .

    我的建议是继续提高递归限制,看看您正在处理的数据和正在使用的trie实现是否有上限。

    附加实现 内置了数据持久性(使用pickle和 shelves

        2
  •  11
  •   John La Rooy    15 年前

    Pickle确实需要递归地遍历您的trie。如果Pickle只使用5个级别的函数调用来完成这项工作,那么深度638的trie需要将级别设置为3000以上。

    尝试一个更大的数字,递归限制实际上只是为了保护用户在递归陷入无限深渊时不必等待太长时间。

    Pickle可以处理循环,所以即使您的trie有一个循环也没关系

        3
  •  7
  •   Community CDub    8 年前

    堆栈大小也必须随时间增加 resource.setrlimit

    如果你只使用 sys.setrecursionlimit ,如果达到Linux内核允许的最大堆栈大小,仍然可以执行segfault。

    Setting stacksize in a python script

    import pickle
    import resource
    import sys
    
    print resource.getrlimit(resource.RLIMIT_STACK)
    print sys.getrecursionlimit()
    
    max_rec = 0x100000
    
    # May segfault without this line. 0x100 is a guess at the size of each stack frame.
    resource.setrlimit(resource.RLIMIT_STACK, [0x100 * max_rec, resource.RLIM_INFINITY])
    sys.setrecursionlimit(max_rec)
    
    a = []
    # 0x10 is to account for subfunctions called inside `pickle`.
    for i in xrange(max_rec / 0x10):
        a = [a]
    print pickle.dumps(a, -1)
    

    What is the maximum recursion depth in Python, and how to increase it?

    me的默认最大值为8Mb。

    在Ubuntu 16.10、Python 2.7.12上测试。

        4
  •  4
  •   Cerin    15 年前

    你可以试着进一步提高限制。有一个硬性的最大值取决于平台,但尝试50000将是合理的。

        5
  •  0
  •   Alexandre    7 年前

    我的需求有点紧迫,所以我通过以.txt格式保存字典来解决这个问题。唯一的问题是,当您再次加载文件时,您必须将其转换回字典。

    import json
    
    # Saving the dictionary
    with open('filename.txt', 'w') as file_handle:
        file_handle.write(str(dictionary))
    
    # Importing the .txt file
    with open('filename.txt', 'r') as file_handle:
        f = '"' + file_handle.read() + '"'
    
    # From .txt file to dictionary
    dictionary = eval(json.loads(f))
    

    如果这不起作用,您可以尝试使用json格式导出字典。

        6
  •  0
  •   ofekp    4 年前

    全部的 使用 importlib.reload 我做的 甚至需要增加限额 setrecursionlimit

    如果你想知道我是怎么找到它的,继续读下去。

    在找到解决方案之前,我发现如果我先将模型移动到CPU,实际上可以保存该模型,但在评估过程中出现错误(XXX是类名,不重要):

    PicklingError: Can't pickle <class 'XXX'>: it's not the same object as XXX

    https://stackoverflow.com/a/1964942/4295037

    但在删除了所有的 我能够保存模型,而无需首先将其移动到CPU设备。