Python 使用numpy实现MXNet参数序列化
我想在s390x架构上使用预先训练过的MXNet模型,但它似乎不起作用。这是因为预先训练的模型是小端的,而s390x是大端的。所以,我试着使用对小端和大端都有效的方法 解决这个问题的一种方法是在x86机器上加载模型参数,调用asnumpy,通过numpy保存,然后使用numpy在s390x机器上加载参数,并将其转换为MXNet。但我真的不知道如何编码。谁能帮我一下吗 更新 这个问题似乎还不清楚。所以,我添加了一个例子,更好地解释了我想在3个步骤中做什么-Python 使用numpy实现MXNet参数序列化,python,numpy,mxnet,s390x,Python,Numpy,Mxnet,S390x,我想在s390x架构上使用预先训练过的MXNet模型,但它似乎不起作用。这是因为预先训练的模型是小端的,而s390x是大端的。所以,我试着使用对小端和大端都有效的方法 解决这个问题的一种方法是在x86机器上加载模型参数,调用asnumpy,通过numpy保存,然后使用numpy在s390x机器上加载参数,并将其转换为MXNet。但我真的不知道如何编码。谁能帮我一下吗 更新 这个问题似乎还不清楚。所以,我添加了一个例子,更好地解释了我想在3个步骤中做什么- 从MXNet加载一个预先存在的模型,类似
我希望使用numpy来加载我们在步骤2中创建的.npy文件,而不是使用MXNETAPI进行加载。加载.npy文件后,需要将其转换为MXNet。因此,我最终可以在MXNet中使用该模型。从另一个问题中发布的代码片段开始: mxnet似乎有一个选项,可以在内部将数据存储为numpy阵列:
mx.npx.set_np(True, True)
不幸的是,这个选项没有达到我希望的效果(我的IPython会话崩溃)
参数是mxnet.glion.parameter.parameter
实例的dict
,每个实例都包含其他特殊数据类型的属性。将其分离,以便将其存储为大量纯numpy数组(或将它们的集合存储在.npz
文件中),这是一项毫无希望的任务
幸运的是,python有pickle
将复杂的数据结构转换成或多或少可移植的东西:
# (mxnet/resnet setup skipped)
parameters = resnet.collect_params()
import pickle
with open('foo.pkl', 'wb') as f:
pickle.dump(parameters, f)
要恢复参数,请执行以下操作:
with open('foo.pkl', 'rb') as f:
parameters_loaded = pickle.load(f)
本质上,它看起来像mxnet/glion/block.py
中定义的resnet.save_parameters()
获取参数(使用\u collect_parameters\u和前缀()
)并使用自定义写入函数将其写入文件,该函数似乎是从C编译的(我没有检查详细信息)
您可以改为使用pickle
保存参数
对于加载,load\u参数(也在util.py
中)包含以下代码(删除了健全性检查):
这里,load
是从文件加载的dict。通过检查代码,我没有完全掌握加载的内容-params
似乎是函数中不再使用的局部变量。但从这里开始值得一试,为load\u parameters
函数编写一个替换函数。通过在类外定义函数,可以将函数“猴子补丁”到现有类中,如下所示:
def my_load_parameters(self, ...):
... (put your modified implementation here)
mx.gluon.Block.load_parameters = my_load_parameters
免责声明/警告:
- 即使通过
pickle
获得save/load以在单个big-endian系统上工作,也不能保证在不同的endian系统之间工作。pickle协议本身是endian中立的,但是如果是浮点值(在mxnet.glion.parameter.parameter的深处,parameter
被存储为机器endian约定的原始数据缓冲区,那么pickle不会神奇地猜测缓冲区中的8字节组需要反转。我认为当pickle时,numpy数组是endian安全的
- 如果基础类定义在Pickle和unpickle之间发生变化,Pickle就不是很健壮
- 永远不要取消勾选不受信任的数据
您能提供一个生成/保存/加载模型(不处理endian ness)的最小示例吗?@Han KwangNienhuys我想在mxnet中加载一个先前存在的胶子模型。假设我们将该模型设为resnet-50。因此,我想编写将resnet-50参数保存为numpy文件(.npy)的代码。然后我想导入这个.npy文件以在另一台计算机上使用resnet-50模型。我不确定该如何用Python编写此文件。您能提供帮助吗?使用.npy扩展名将自动解决任何endian问题。因此,只生成/保存/加载模型的代码将在不考虑endianness的情况下工作。我无法编码这个approach@Han-KwangNienhuys我添加了一个示例。如果您需要更多信息,请让我知道。可复制的示例可以复制粘贴用于实验,并且可以工作或演示问题,而无需依赖您的私人文件。仍然不工作,得到以下-回溯:loaded_dict=pickle.load(f)File“/root/mxnet-1.5.0/mxnet/python/mxnet/ndarray/ndarray.py”,第386行,在设置状态检查调用中(_LIB.mxndarrayoladfromrawbytes(ptr,length,ctypes.byref(handle))/root/mxnet-1.5.0/mxnet/python/mxnet/base.py),第253行,在检查调用中引发MXNetError(py_str(_-str.mxnet.getlasterror())错误:[15:34:40]include/mxnet//tuple.h:354:检查失败:ndim>=-1(-906324999 vs-1):ndim不能小于-1,收到-906324999
,感谢您的努力。我看到您正在使用mxnet 1.5.0。我安装了mxnet 1.6(通过pip,在Anaconda 64位Linux中使用Python 3.7.6/numpy 1.18.1)还有我发布的pickle转储/加载循环。您是否将参数pickle文件保存在一个小的endian机器上,然后加载到big endian机器上?对不起,没有,我在同一台机器上进行了转储/加载往返。恐怕您必须修补C代码。
# (mxnet/resnet setup skipped)
parameters = resnet.collect_params()
import pickle
with open('foo.pkl', 'wb') as f:
pickle.dump(parameters, f)
with open('foo.pkl', 'rb') as f:
parameters_loaded = pickle.load(f)
for name in loaded:
params[name]._load_init(loaded[name], ctx, cast_dtype=cast_dtype, dtype_source=dtype_source)
def my_load_parameters(self, ...):
... (put your modified implementation here)
mx.gluon.Block.load_parameters = my_load_parameters