多模型加载过程中遇到的问题以及自己的解决方案
目录
问题二:需要加载模型的结构,又不能直接将该网络模型的架构直接铺设一遍,pkl在我的理解范围内没办法去这么做,怎么办?
问题四:模型的入口如果直接是一个函数的形参,怎么办,没有placeholder?
问题五:模型的出口如果使用了一些将tensor进行np操作整合,如何获得出口?
场景:
加载多个TensorFlow已经训练好的神经网络模型,这里以加载两个项目为例(因为加载三个四个五个和加载两个原理上是一样的),此处增加难度,模型A的存储模式是cpkt,模型B的存储模式是pkl。
目标:同时使用这两个神经网络模型,进行一些想要的处理工作。
模型A:
checkpoint
xxx.ckpt.data-00000-of-00001
xxx.ckpt.index
xxx.ckpt.meta
模型B:
xxx.pkl
问题一:如何“理论加载”两个已经训练好的模型?
如果想要使用已经训练好的模型,首先要在TensorFlow中铺设好”管道“(各种tensor和op),然后通过saver.restore(),函数来加载相应的模型参数,然后feed占位符数据。
如果要是在一个.py文件中同时加载两个模型,不能直接restore两次,那样会出现变量冲突,导致加载失败,因为所有的变量都加载到了当前会话所采用的默认图中。所以我们要做的就是,建立不同的图,创建不同的会话。可以采用class来解决这个问题。
class ImportGraph():
def __init__(self, meta_path, ckpt_path, class_model):
self.class_model = class_model
self.graph = tf.Graph()
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.2)
config = tf.ConfigProto(allow_soft_placement=True, gpu_options=gpu_options)
with self.graph.as_default():
# 加载模型的结构,拼接“管道”
self.saver = tf.train.import_meta_graph(meta_path)
# 创建会话并且指定graph
self.sess = tf.Session(graph=self.graph, config=config)
# 加载模型的数据
self.saver.restore(self.sess, ckpt_path)
with self.graph.as_default():
if self.class_model == A:
# 得到模型A的入口
# 这里tensor的名字是随便起的具体怎么用,下文讲解
self.INPUT = self.graph.get_tensor_by_name('Placeholder:0')
self.OUTPUT = self.graph.get_tensor_by_name('Relu:0')
else:
# 得到模型B的入口
def run(self, input):
if class_model == A:
# 返回模型A的结果
result = self.sess.run(self.OUTPUT, feed_dict={self.INPUT: input})
return result
else:
# 返回模型B的结果
# 使用模型
# 加载模型结构、参数
model_A = ImportGraph(A_meta_path, A_ckpt_path, A)
model_B = ImportGraph(B_meta_path, B_ckpt_path, B)
# 使用模型进行相关预测
resA = model_A.run(inputA)
resB = model_B.run(inputB)
问题二:需要加载模型的结构,又不能直接将该网络模型的架构直接铺设一,pkl在我的理解范围内没办法去这么做,怎么办?
在单独只加载pkl模型参数的时候,我是知道如何加载模型,我的解决方案是,使用单模型加载pkl网络,然后用saver.save()保存为cpkt形式(及其脑残)。这样就可以同时得到两个文件的cpkt模型,这样就可以用上面个的类去加载两个不同的神经网络模型了。
问题三:如何获取存储模型中的参数?
这里我们得到模型中的op名字和values是为了解决上述代码中的该部分问题:
self.INPUT = self.graph.get_tensor_by_name('Placeholder:0')
self.OUTPUT = self.graph.get_tensor_by_name('Relu:0')
这里得到的tensor是在run中需要feed真是数据流量的入口,所以一定要找到。然后我们通过run函数得到我们需要的模型预测结果,所以我们一定也要找到模型的出口(这里的出口是指我们需要的那一部分结果,不一定要走完整个模型)。
下面的代码用来返回模型中的所有op操作的名字和values
op_list = self.sess.graph.get_operations()
for op in op_list:
print(op.name)
print(op.values())
大体形式是这样的:(截取一小部分)一行op名字,一行op的内容,只要是在网络架构中用到tf.的都有相应的存储结果。
input_image
(<tf.Tensor 'input_image:0' shape=(?, 368, 368, 3) dtype=float32>,)
center_map
(<tf.Tensor 'center_map:0' shape=(?, 368, 368, 1) dtype=float32>,)
pooled_center_map/center_map/AvgPool
(<tf.Tensor 'pooled_center_map/center_map/AvgPool:0' shape=(?, 46, 46, 1) dtype=float32>,)
sub_stages/sub_conv1/weights/Initializer/random_uniform/shape
(<tf.Tensor 'sub_stages/sub_conv1/weights/Initializer/random_uniform/shape:0' shape=(4,) dtype=int32>,)
sub_stages/sub_conv1/weights/Initializer/random_uniform/min
(<tf.Tensor 'sub_stages/sub_conv1/weights/Initializer/random_uniform/min:0' shape=() dtype=float32>,)
sub_stages/sub_conv1/weights/Initializer/random_uniform/max
(<tf.Tensor 'sub_stages/sub_conv1/weights/Initializer/random_uniform/max:0' shape=() dtype=float32>,)
sub_stages/sub_conv1/weights/Initializer/random_uniform/RandomUniform
(<tf.Tensor 'sub_stages/sub_conv1/weights/Initializer/random_uniform/RandomUniform:0' shape=(3, 3, 3, 64) dtype=float32>,)
sub_stages/sub_conv1/weights/Initializer/random_uniform/sub
(<tf.Tensor 'sub_stages/sub_conv1/weights/Initializer/random_uniform/sub:0' shape=() dtype=float32>,)
sub_stages/sub_conv1/weights/Initializer/random_uniform/mul
(<tf.Tensor 'sub_stages/sub_conv1/weights/Initializer/random_uniform/mul:0' shape=(3, 3, 3, 64) dtype=float32>,)
sub_stages/sub_conv1/weights/Initializer/random_uniform
(<tf.Tensor 'sub_stages/sub_conv1/weights/Initializer/random_uniform:0' shape=(3, 3, 3, 64) dtype=float32>,)
sub_stages/sub_conv1/weights
(<tf.Tensor 'sub_stages/sub_conv1/weights:0' shape=(3, 3, 3, 64) dtype=float32_ref>,)
sub_stages/sub_conv1/weights/Assign
(<tf.Tensor 'sub_stages/sub_conv1/weights/Assign:0' shape=(3, 3, 3, 64) dtype=float32_ref>,)
sub_stages/sub_conv1/weights/read
(<tf.Tensor 'sub_stages/sub_conv1/weights/read:0' shape=(3, 3, 3, 64) dtype=float32>,)
sub_stages/sub_conv1/biases/Initializer/zeros
(<tf.Tensor 'sub_stages/sub_conv1/biases/Initializer/zeros:0' shape=(64,) dtype=float32>,)
sub_stages/sub_conv1/biases
(<tf.Tensor 'sub_stages/sub_conv1/biases:0' shape=(64,) dtype=float32_ref>,)
sub_stages/sub_conv1/biases/Assign
(<tf.Tensor 'sub_stages/sub_conv1/biases/Assign:0' shape=(64,) dtype=float32_ref>,)
sub_stages/sub_conv1/biases/read
(<tf.Tensor 'sub_stages/sub_conv1/biases/read:0' shape=(64,) dtype=float32>,)
sub_stages/sub_conv1/dilation_rate
(<tf.Tensor 'sub_stages/sub_conv1/dilation_rate:0' shape=(2,) dtype=int32>,)
sub_stages/sub_conv1/Conv2D
(<tf.Tensor 'sub_stages/sub_conv1/Conv2D:0' shape=(?, 368, 368, 64) dtype=float32>,)
sub_stages/sub_conv1/BiasAdd
(<tf.Tensor 'sub_stages/sub_conv1/BiasAdd:0' shape=(?, 368, 368, 64) dtype=float32>,)
sub_stages/sub_conv1/Relu
(<tf.Tensor 'sub_stages/sub_conv1/Relu:0' shape=(?, 368, 368, 64) dtype=float32>,)
问题四:模型的入口如果直接是一个函数的形参,怎么办,没有placeholder?
模型被写在了一个函数里面,然后模型的参数保存了函数里面的参数,如果在单独只加载一个模型的情况下,是可以通过定义一占位符,然后将其作为形参,调用模型函数的,但是多模型情况下,没法找到占位符,就没办法去feed数据流,我的方法是,在加载单模型的情况下,再保存一遍模型参数,这样子,我们定义的占位符就也将作为模型参数的一部分。
问题五:模型的出口如果使用了一些将tensor进行np操作整合,如何获得出口?
有的模型是将一些tensor进行整合,例如[tensor1, tensor2, tensor3]---list中,但是这个操作再模型中是不存在的,所以再多模型条件下没有办法直接获得想要的输出,我的解决方法是,将最后需要整合的所有tensor的op名字找出,将这些tensor作为出口,然后将sess.run()的返回值推入到一个空list中,得到想要的结果。