将预处理层添加到KERAS模型并设置张量值



如何最好地添加一个预处理层(例如,将平均值和按STD划分(到Keras(v2.0.5(模型中,以使模型完全自我包含以进行部署(可能是在C 环境中(。我尝试了:

    def getmodel():
       model = Sequential()
       mean_tensor = K.placeholder(shape=(1,1,3), name="mean_tensor")
       std_tensor = K.placeholder(shape=(1,1,3), name="std_tensor")
       preproc_layer = Lambda(lambda x: (x - mean_tensor) / (std_tensor + K.epsilon()),
                              input_shape=im_shape)
       model.add(preproc_layer)
       # Build the remaining model, perhaps set weights,
       ...
       return model

然后,其他地方设置了模型上的均值/std。我找到了SET_VALUE函数,因此尝试了以下内容:

m = getmodel()
mean, std = get_mean_std(..)
graph = K.get_session().graph
mean_tensor = graph.get_tensor_by_name("mean_tensor:0")
std_tensor = graph.get_tensor_by_name("std_tensor:0")
K.set_value(mean_tensor, mean)
K.set_value(std_tensor, std)

但是,set_value失败了

AttributeError: 'Tensor' object has no attribute 'assign'

因此,set_value无法(有限(文档所建议的那样工作。这样做的正确方法是什么?获取TF会话,将所有培训代码包装在with (session)中并使用feed_dict?我本来以为会有一种本地keras设置张量值的方法。

而不是使用占位持有人,我尝试使用K.variableK.constant在模型构建上设置平均/STD:

mean_tensor = K.variable(mean, name="mean_tensor")
std_tensor = K.variable(std, name="std_tensor")

这避免了任何set_value问题。尽管我注意到,如果我尝试训练该模型(我知道这不是特别有效,因为您正在重新完成每个图像的归一化(,但在第一个时期的结束时,ModelCheckpoint处理程序失败了,并且很深跟踪:

...
File "/Users/dgorissen/Library/Python/2.7/lib/python/site-packages/keras/models.py", line 102, in save_model
  'config': model.get_config()
File "/Users/dgorissen/Library/Python/2.7/lib/python/site-packages/keras/models.py", line 1193, in get_config
  return copy.deepcopy(config)
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 163, in deepcopy
  y = copier(x, memo)
...
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 190, in deepcopy
  y = _reconstruct(x, rv, 1, memo)
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 343, in _reconstruct
  y.__dict__.update(state)
AttributeError: 'NoneType' object has no attribute 'update'

更新1:

我也尝试了另一种方法。训练模型是正常的,然后仅预先预测预处理的第二个模型:

# Regular model, trained as usual
model = ...
# Preprocessing model
preproc_model = Sequential()
mean_tensor = K.constant(mean, name="mean_tensor")
std_tensor = K.constant(std, name="std_tensor")
preproc_layer = Lambda(lambda x: (x - mean_tensor) / (std_tensor + K.epsilon()),
                       input_shape=im_shape, name="normalisation")
preproc_model.add(preproc_layer)
# Prepend the preprocessing model to the regular model    
full_model = Model(inputs=[preproc_model.input],
              outputs=[model(preproc_model.output)])
# Save the complete model to disk
full_model.save('full_model.hdf5')

这似乎有效,直到save()调用,它以与上面相同的深堆栈跟踪失败。也许Lambda层是问题所在,但是从这个问题来判断它似乎应该正确序列化。

因此,总的来说,我如何将归一化层附加到KERAS模型,而不会损害序列化的能力(并导出到PB(?

我确定您可以通过直接下降到TF(例如此线程,或使用TF.Transform(来使其有效,但会认为直接在Keras中可以。

更新2:

所以我发现可以通过执行

来避免深层堆栈跟踪
def foo(x):
    bar = K.variable(baz, name="baz")
    return x - bar

因此,在功能内定义bar而不是从外部范围捕获。

我发现我可以保存到磁盘,但无法从磁盘上加载。周围有一套github问题。我使用#5396中指定的解决方案将所有变量传递为参数,然后使我能够保存和加载。

以为我几乎在那儿,我继续从上面的 Update 1 中继续使用我的方法,该方法将预处理模型堆叠在训练有素的模型前。然后导致Model is not compiled错误。围绕这些工作,但最终我从未设法使以下内容工作:

  • 建立和训练模型
  • 将其保存到磁盘
  • 加载它,预处理模型
  • 将堆叠模型导出到磁盘作为冷冻PB文件
  • 从磁盘加载冷冻的PB
  • 将其应用于一些看不见的数据

我把它得到了没有错误的地步,但是无法使归一化张量传播到冷冻的PB。花了太多时间,然后我放弃了,转到了一些不那么优雅的方法:

  • 从一开始就在模型中使用预处理操作构建模型,但设置为no-op(平均= 0,std = 1(
  • 训练模型,建立一个相同的模型,但这次以均值/std的正确值。
  • 转移权重
  • 导出并将模型冻结至PB

所有这些现在都按预期完全工作。训练的小开销,但对我来说可以忽略不计。

仍然无法弄清楚如何设置keras中张量变量的值(而不提高assign例外(,但现在可以没有它。

将接受 @丹尼尔的回答,因为它使我朝着正确的方向前进。

相关问题:

  • 将TensorFlow预处理添加到现有的Keras模型(用于张量式服务(

创建变量时,必须给它"值",而不是形状:

mean_tensor = K.variable(mean, name="mean_tensor")
std_tensor = K.variable(std, name="std_tensor")

现在,在Keras,您不必处理会话,图表等。您只使用层,在lambda层(或损失功能(内部工作,您可以与张量一起使用。

对于我们的lambda层,我们需要一个更复杂的功能,因为在进行计算之前,形状必须匹配。由于我不知道im_shape,所以我认为它有3个维度:

def myFunc(x):
    #reshape x in a way it's compatible with the tensors mean and std:
    x = K.reshape(x,(-1,1,1,3)) 
        #-1 is like a wildcard, it will be the value that matches the rest of the given shape.     
        #I chose (1,1,3) because it's the same shape of mean_tensor and std_tensor
    result = (x - mean_tensor) / (std_tensor + K.epsilon())
    #now shape it back to the same shape it was before (which I don't know)    
    return K.reshape(result,(-1,im_shape[0], im_shape[1], im_shape[2]))
        #-1 is still necessary, it's the batch size

现在我们创建了lambda层,考虑到它也需要一个输出形状(由于您的自定义操作,系统不一定知道输出形状(

model.add(Lambda(myFunc,input_shape=im_shape, output_shape=im_shape))

之后,只需编译模型并训练它即可。(通常使用model.compile(...)model.fit(...)(


如果要包含所有内容,包括功能中的预处理,也可以:

def myFunc(x):
    mean_tensor = K.mean(x,axis=[0,1,2]) #considering shapes of (size,width, heigth,channels)    
    std_tensor = K.std(x,axis=[0,1,2])
    x = K.reshape(x, (-1,3)) #shapes of mean and std are (3,) here.    
    result = (x - mean_tensor) / (std_tensor + K.epsilon())
    return K.reshape(result,(-1,width,height,3))

现在,所有这些都是模型中的额外计算,并且将消耗处理。最好只做模型之外的所有事情。首先创建预处理数据并存储它,然后创建模型而无需此预处理层。这样,您可以获得更快的型号。(如果您的数据或模型太大,可能很重要(。

最新更新