如何散列类或函数定义



背景

在进行机器学习实验时,我经常通过酸洗/去酸洗的方式重复使用以前训练过的模型。然而,在处理特征提取部分时,不混淆不同的模型是一个挑战。因此,我想添加一个检查,以确保使用与测试数据完全相同的特征提取过程来训练模型。

问题

我的想法如下:在模型的同时,我会在pickle转储中包含一个散列值,该值为特征提取过程提供指纹。

当训练模型或将其用于预测/测试时,会为模型包装器提供符合特定协议的特征提取类。当然,在该类上使用hash()是行不通的,因为它在调用之间并不持久。所以我想我也许可以找到定义类的源文件,并从该文件中获得哈希值。

但是,可能有一种方法可以直接从类的内存内容中获得稳定的哈希值。这将有两个优点:如果找不到源文件,它也会起作用。它可能会忽略对源文件的不相关更改(例如,修复模块docstring中的拼写错误)。类是否有可以在这里使用的代码对象?

您所要查找的只是一个哈希过程,该过程包括类定义的所有重要细节。(基类可以通过递归地包含它们的定义来包含。)为了最大限度地减少错误匹配,基本思想是将宽(加密)哈希应用于类的序列化。因此,从pickle开始:它支持比hash更多的类型,并且当它使用标识时,它使用基于名称的可复制标识。这使得它成为递归策略的基本情况的一个很好的候选者:处理内容很重要的函数和类,并让它处理引用的任何辅助对象。

因此,按事例定义序列化。如果对象属于除最后一种情况之外的任何情况,则调用特殊

  • 对于被视为包含特殊对象的tuple
    1. 角色t
    2. len的系列化
    3. 每个元素的序列化,按顺序
  • 对于被视为包含特殊对象的dict
    1. 角色d
    2. len的系列化
    3. 按排序顺序对每个名称和值进行序列化
  • 对于定义突出的类:
    1. 角色C
    2. __bases__的系列化
    3. vars的系列化
  • 对于定义突出的函数:
    1. 角色f
    2. __defaults__的系列化
    3. __kwdefaults__的序列化(在Python3中)
    4. __closure__的序列化(但使用单元格而不是单元格本身)
    5. vars的系列化
    6. __code__的系列化
  • 对于代码对象(因为pickle根本不支持它们):
    1. 角色c
    2. co_argcountco_nlocalsco_flagsco_codeco_constsco_namesco_freevarsco_cellvars按顺序的序列化;这些都不是特别的
  • 对于静态或类方法对象:
    1. 字符sm
    2. __func__的系列化
  • 对于属性:
    1. 角色p
    2. fgetfsetfdel的序列化,按顺序
  • 对于任何其他对象:pickle.dumps(x,-1)

(实际上,您从来没有存储所有这些:只需在顶级函数中创建一个您选择的hashlib对象,在递归部分update中,它依次与每一个序列化片段一起。)

类型标签是为了避免冲突,特别是为了避免前缀。二进制泡菜已经没有前缀了。您可以基于对容器内容的确定性分析(即使是启发式的)或上下文来决定容器,只要您是一致的。

和往常一样,平衡假阳性和假阴性是一门艺术:对于一个函数,你可以包括__globals__(对已经序列化的对象进行修剪,以避免大的(如果不是无限的)序列化),或者只包括其中的任何__name__。省略co_varnames会忽略重命名局部变量,这很好,除非内省很重要;类似于CCD_ 42和CCD_。

您可能需要支持更多类型:查找pickle不正确的静态属性和默认参数(因为它们包含对特殊类型的引用)或根本不正确。当然,请注意,有些类型(如文件对象)是不可拾取的,因为很难或不可能序列化它们(尽管与pickle不同,一旦完成code对象,您就可以像处理任何其他函数一样处理lambda)。在某些错误匹配的风险下,您可以选择仅序列化此类对象的类型(一如既往,前缀为字符?,以区别于该位置的实际类型)。

相关内容

  • 没有找到相关文章

最新更新