在尝试机器学习时,我经常重复使用之前训练的模型,通过酸洗/去除。 但是,在处理特征提取部分时,不要混淆不同的模型是一项挑战。 因此,我想添加一个检查,确保使用与测试数据完全相同的特征提取过程来训练模型。
我的想法如下: 与模型一起,我在pickle转储中包含一个散列值,用于指纹特征提取过程。
当训练模型或将其用于预测/测试时,模型包装器被赋予符合特定协议的特征提取类。
当然,在该课程上使用hash()
不会起作用,因为它不会在通话中保持不变。
所以我想我可以找到定义类的源文件,并从该文件中获取哈希值。
但是,可能有一种方法可以直接从类的内存中获取稳定的哈希值。 这有两个好处: 如果找不到源文件,它也可以工作。 它可能会忽略源文件的无关更改(例如,修复模块docstring中的拼写错误)。 类有一个可以在这里使用的代码对象吗?
答案 0 :(得分:3)
您所需要的只是一个散列过程,其中包括该类定义的所有重要细节。 (可以通过递归地包含其定义来包含基类。)为了最大程度地减少错误匹配,基本思想是对类的序列化应用宽(加密)哈希。因此,从pickle
开始:它比hash
支持更多类型,并且当使用身份时,它会基于名称使用可再现身份。这使得它很适合作为递归策略的基础案例:处理其内容很重要的函数和类,并使其处理所引用的任何辅助对象。
因此,要根据情况定义序列化。如果对象 special 属于以下情况,则调用对象 。
tuple
:
t
len
的序列化dict
:
d
len
的序列化C
__bases__
的序列化vars
的序列化f
__defaults__
的序列化__kwdefaults__
的序列化(在Python 3中)__closure__
的序列化(但使用单元格 values 而不是单元格本身)vars
的序列化__code__
的序列化pickle
根本不支持它们):
c
co_argcount
,co_nlocals
,co_flags
,co_code
,co_consts
,co_names
,co_freevars
和co_cellvars
,按此顺序;这些都不是很特别s
或m
__func__
的序列化p
fget
,fset
和fdel
的序列化顺序pickle.dumps(x,-1)
(您实际上从来没有存储这一切:只需在顶级函数中创建一个您选择的hashlib
对象,然后在递归部分update
中创建每个对象依次进行序列化。)
类型标签应避免冲突,尤其是无前缀。二元泡菜已经不含前缀。您可以根据对容器的内容(即使是启发式)进行确定性分析或根据上下文来进行容器决定,只要您保持一致即可。
一如既往,在误报与误报之间取得平衡是一种技巧:对于一个函数,您可以包括__globals__
(对已经序列化的对象进行修剪,以避免进行大范围的序列化)其中找到了__name__
。省略co_varnames
会忽略重命名局部变量,除非自省很重要,否则这样做很好。对于co_filename
和co_name
同样。
您可能需要支持更多类型:寻找不能正确pickle
(因为它们包含对特殊类型的引用)或完全不正确的静态属性和默认参数。当然请注意,某些类型(如文件对象)
是不可拾取的,因为很难或不可能对其进行序列化(尽管与pickle
不同,您可以像处理任何其他函数一样处理lambda。 code
个对象)。冒着错误匹配的风险,您可以选择仅序列化此类对象的 type (一如既往,以字符?
为前缀,以区别于该位置的实际类型)。