4

背景

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

问题

我的想法如下:与模型一起,我将在 pickle 转储中包含一个哈希值,该哈希值对特征提取过程进行指纹识别。

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

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

4

1 回答 1

3

您正在寻找的只是一个包含类定义的所有重要细节的哈希过程。(可以通过递归包含基类定义来包含基类。)为了最大限度地减少错误匹配,基本思想是将宽(加密)散列应用于您的类的序列化。所以从以下开始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__(在 Python 3 中)
    4. 它的序列化__closure__(但使用单元格而不是单元格本身)
    5. 其序列化vars
    6. 其序列化__code__
  • 对于代码对象(因为pickle根本不支持它们):
    1. 性格c
    2. co_argcount它的, co_nlocals, co_flags, co_code, co_consts, co_names, co_freevars, 和,的序列化co_cellvars顺序;这些都不是特别的
  • 对于静态或类方法对象:
    1. 性格sm
    2. 其序列化__func__
  • 对于属性:
    1. 性格p
    2. fget它的,fset和,的序列化fdel顺序
  • 对于任何其他对象:pickle.dumps(x,-1)

(您实际上永远不会存储所有这些:只需hashlib在顶级函数中创建您选择的对象,并在递归部分update中依次使用每个序列化部分。)

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

与往常一样,平衡误报与误报是一门艺术:对于一个函数,您可以包括__globals__(修剪已经序列化的对象以避免大的序列化,如果不是无限的序列化)或__name__其中找到的任何东西。省略co_varnames忽略重命名局部变量,这很好,除非自省很重要;同样对于co_filenameco_name

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

于 2018-10-07T04:21:17.763 回答