如何散列类或函数定义?

时间:2017-10-16 10:31:22

标签: python fingerprinting function-definition

背景

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

问题

我的想法如下: 与模型一起,我在pickle转储中包含一个散列值,用于指纹特征提取过程。

当训练模型或将其用于预测/测试时,模型包装器被赋予符合特定协议的特征提取类。 当然,在该课程上使用hash()不会起作用,因为它不会在通话中保持不变。 所以我想我可以找到定义类的源文件,并从该文件中获取哈希值。

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

1 个答案:

答案 0 :(得分:3)

您所需要的只是一个散列过程,其中包括该类定义的所有重要细节。 (可以通过递归地包含定义来包含基类。)为了最大程度地减少错误匹配,基本思想是对类的序列化应用宽(加密)哈希。因此,从pickle开始:它比hash支持更多类型,并且当使用身份时,它会基于名称使用可再现身份。这使得它很适合作为递归策略的基础案例:处理其内容很重要的函数和类,并使其处理所引用的任何辅助对象。

因此,要根据情况定义序列化。如果对象 special 属于以下情况,则调用对象

  • 对于被认为包含特殊对象的tuple
    1. 字符t
    2. len的序列化
    3. 每个元素的序列化顺序
  • 对于被认为包含特殊对象的dict
    1. 字符d
    2. len的序列化
    3. 每个名称和值的序列化,按排序顺序
  • 对于定义明显的类:
    1. 字符C
    2. __bases__的序列化
    3. vars的序列化
  • 对于定义显着的函数:
    1. 字符f
    2. __defaults__的序列化
    3. __kwdefaults__的序列化(在Python 3中)
    4. __closure__的序列化(但使用单元格 values 而不是单元格本身)
    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会忽略重命名局部变量,除非自省很重要,否则这样做很好。对于co_filenameco_name同样。

您可能需要支持更多类型:寻找不能正确pickle(因为它们包含对特殊类型的引用)或完全不正确的静态属性和默认参数。当然请注意,某些类型(如文件对象)

是不可拾取的,因为很难或不可能对其进行序列化(尽管与pickle不同,您可以像处理任何其他函数一样处理lambda。 code个对象)。冒着错误匹配的风险,您可以选择仅序列化此类对象的 type (一如既往,以字符?为前缀,以区别于该位置的实际类型)。