为keras模型添加预处理层并设置张量值
Adding a preprocessing layer to keras model and setting tensor values
如何最好地将预处理层(例如,减去均值并除以标准差)添加到 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
然后,在别处设置模型上的 mean/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.variable
或 K.constant
:
在模型构建中设置 mean/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
层是问题所在,但从 this issue 来看,它似乎应该正确序列化。
总的来说,如何在不影响序列化(和导出到 pb)能力的情况下将规范化层附加到 keras 模型?
我确定你可以通过直接下拉到 TF(例如 this thread, or using tf.Transform)来让它工作,但我认为直接在 keras 中是可能的。
更新二:
所以我发现可以通过
避免深层堆栈跟踪
def foo(x):
bar = K.variable(baz, name="baz")
return x - bar
所以在函数内部定义 bar
而不是从外部范围捕获。
然后我发现我可以保存到磁盘但无法从磁盘加载。围绕此有一系列 github 问题。我使用 #5396 中指定的解决方法将所有变量作为参数传递,然后允许我保存和加载。
以为我快到了,我继续使用上面 更新 1 中的方法,将预处理模型堆叠在经过训练的模型前面。
这随后导致 Model is not compiled
错误。解决了这些问题,但最后我从来没有设法让以下方法起作用:
- 构建和训练模型
- 将其保存到磁盘
- 加载它,添加一个预处理模型
- 将堆叠模型作为冻结的 pb 文件导出到磁盘
- 从磁盘加载冻结的 pb
- 将其应用于一些看不见的数据
我做到了没有错误的地步,但无法让标准化张量传播到冻结的 pb。在这上面花了太多时间,然后我放弃并转向不太优雅的方法:
- 从一开始就使用模型中的预处理操作构建模型,但设置为无操作(平均值=0,标准=1)
- 训练模型,构建一个相同的模型,但这次使用 mean/std 的正确值。
- 传递权重
- 将模型导出并冻结到 pb
所有这一切现在完全按预期工作。训练开销很小,但对我来说可以忽略不计。
仍然无法弄清楚如何在 keras 中设置张量变量的值(不引发 assign
异常)但现在可以不用它。
将接受@Daniel 的回答,因为它让我朝着正确的方向前进。
相关问题:
创建变量时,必须给它 "value",而不是形状:
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))
现在,所有这些都是您模型中的额外计算,并且会消耗处理。
最好只在模型之外做所有事情。首先创建预处理数据并存储它,然后创建没有这个预处理层的模型。这样你就可以获得更快的模型。 (如果您的数据或模型太大,这可能很重要)。
如何最好地将预处理层(例如,减去均值并除以标准差)添加到 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
然后,在别处设置模型上的 mean/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.variable
或 K.constant
:
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
层是问题所在,但从 this issue 来看,它似乎应该正确序列化。
总的来说,如何在不影响序列化(和导出到 pb)能力的情况下将规范化层附加到 keras 模型?
我确定你可以通过直接下拉到 TF(例如 this thread, or using tf.Transform)来让它工作,但我认为直接在 keras 中是可能的。
更新二:
所以我发现可以通过
避免深层堆栈跟踪def foo(x):
bar = K.variable(baz, name="baz")
return x - bar
所以在函数内部定义 bar
而不是从外部范围捕获。
然后我发现我可以保存到磁盘但无法从磁盘加载。围绕此有一系列 github 问题。我使用 #5396 中指定的解决方法将所有变量作为参数传递,然后允许我保存和加载。
以为我快到了,我继续使用上面 更新 1 中的方法,将预处理模型堆叠在经过训练的模型前面。
这随后导致 Model is not compiled
错误。解决了这些问题,但最后我从来没有设法让以下方法起作用:
- 构建和训练模型
- 将其保存到磁盘
- 加载它,添加一个预处理模型
- 将堆叠模型作为冻结的 pb 文件导出到磁盘
- 从磁盘加载冻结的 pb
- 将其应用于一些看不见的数据
我做到了没有错误的地步,但无法让标准化张量传播到冻结的 pb。在这上面花了太多时间,然后我放弃并转向不太优雅的方法:
- 从一开始就使用模型中的预处理操作构建模型,但设置为无操作(平均值=0,标准=1)
- 训练模型,构建一个相同的模型,但这次使用 mean/std 的正确值。
- 传递权重
- 将模型导出并冻结到 pb
所有这一切现在完全按预期工作。训练开销很小,但对我来说可以忽略不计。
仍然无法弄清楚如何在 keras 中设置张量变量的值(不引发 assign
异常)但现在可以不用它。
将接受@Daniel 的回答,因为它让我朝着正确的方向前进。
相关问题:
创建变量时,必须给它 "value",而不是形状:
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))
现在,所有这些都是您模型中的额外计算,并且会消耗处理。 最好只在模型之外做所有事情。首先创建预处理数据并存储它,然后创建没有这个预处理层的模型。这样你就可以获得更快的模型。 (如果您的数据或模型太大,这可能很重要)。