为了举例,我有一个由2个图像组成的输入,总形状(2,299,299,3)。我尝试在每个图像上应用inceptionv3,然后使用LSTM处理输出。我正在使用遮罩层来排除处理空白图像(在下面指定)。
代码是:
import numpy as np
from keras import backend as K
from keras.models import Sequential,Model
from keras.layers import Convolution2D, MaxPooling2D, ZeroPadding2D, BatchNormalization, \
Input, GlobalAveragePooling2D, Masking,TimeDistributed, LSTM,Dense,Flatten,Reshape,Lambda, Concatenate
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.applications import inception_v3
IMG_SIZE=(299,299,3)
def create_base():
base_model = inception_v3.InceptionV3(weights='imagenet', include_top=False)
x = GlobalAveragePooling2D()(base_model.output)
base_model=Model(base_model.input,x)
return base_model
base_model=create_base()
#Image mask to ignore images with pixel values of -1
IMAGE_MASK = -2*np.expand_dims(np.ones(IMG_SIZE),0)
final_input=Input((2,IMG_SIZE[0],IMG_SIZE[1],IMG_SIZE[2]))
final_model = Masking(mask_value = -2.)(final_input)
final_model = TimeDistributed(base_model)(final_model)
final_model = Lambda(lambda x: x, output_shape=lambda s:s)(final_model)
#final_model = Reshape(target_shape=(2, 2048))(final_model)
#final_model = Masking(mask_value = 0.)(final_model)
final_model = LSTM(5,return_sequences=False)(final_model)
final_model = Model(final_input,final_model)
#Create a sample test image
TEST_IMAGE = np.ones(IMG_SIZE)
#Create a test sample input, consisting of a normal image and a masked image
TEST_SAMPLE = np.concatenate((np.expand_dims(TEST_IMAGE,axis=0),IMAGE_MASK))
inp = final_model.input # input placeholder
outputs = [layer.output for layer in final_model.layers] # all layer outputs
functors = [K.function([inp]+ [K.learning_phase()], [out]) for out in outputs]
layer_outs = [func([np.expand_dims(TEST_SAMPLE,0), 1.]) for func in functors]
这不能正常工作。具体来说,模型应该屏蔽输入的IMAGE_MASK部分,但它会在初始时处理它(给出非零输出)。以下是详细信息:
layer_out [-1],LSTM输出正常:
[array([[-0.15324114, -0.09620268, -0.01668587, 0.07938149, -0.00757846]], dtype=float32)]
layer_out [-2]和layer_out [-3],LSTM输入错误,它应该在第二个数组中全部为零:
[array([[[ 0.37713543, 0.36381325, 0.36197218, ..., 0.23298527,
0.43247852, 0.34844452],
[ 0.24972123, 0.2378867 , 0.11810347, ..., 0.51930511,
0.33289322, 0.33403745]]], dtype=float32)]
layer_out [-4],CNN的输入被正确屏蔽:
[[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.],
...,
[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]]],
[[[-0., -0., -0.],
[-0., -0., -0.],
[-0., -0., -0.],
...,
[-0., -0., -0.],
[-0., -0., -0.],
[-0., -0., -0.]],
请注意,代码似乎使用更简单的base_model 正确,例如:
def create_base():
input_layer=Input(IMG_SIZE)
base_model=Flatten()(input_layer)
base_model=Dense(2048)(base_model)
base_model=Model(input_layer,base_model)
return base_model
我已经耗尽了大部分在线资源。已经在Keras的github上询问了这个问题的排列,例如here,here和here,但我似乎无法找到任何具体的解决方案。< / p>
这些链接表明这些问题似乎源于TimeDistributed应用于BatchNormalization的组合,以及Lambda身份层的hacky修复,或者Reshape图层删除错误但似乎没有输出正确的模型。
我试图通过以下方式强制基本模型支持屏蔽:
base_model.__setattr__('supports_masking',True)
我还尝试通过以下方式应用身份图层:
TimeDistributed(Lambda(lambda x: base_model(x), output_shape=lambda s:s))(final_model)
但这些似乎都不起作用。请注意,我希望最终模型可以训练,特别是它的CNN部分应该是可训练的。
答案 0 :(得分:2)
不完全确定这会有效,但基于comment made here,使用较新版本的tensorflow + keras它应该有效:
final_model = TimeDistributed(Flatten())(final_input)
final_model = Masking(mask_value = -2.)(final_model)
final_model = TimeDistributed(Reshape(IMG_SIZE))(final_model)
final_model = TimeDistributed(base_model)(final_model)
final_model = Model(final_input,final_model)
我看了一下掩码的源代码,我注意到Keras创建了一个只减少最后一个轴的掩码张量。只要您处理5D张量,它就不会产生任何问题,但是当您减小LSTM的尺寸时,此蒙版张量就会变得不相容。
在屏蔽之前执行第一个展平步骤,将确保屏蔽张量适用于3D张量。然后再次将图像展开为原始大小。
我可能会尽快安装新版本来自行测试,但是这些安装程序造成了太多麻烦,而且我正处于重要的事情中。
在我的机器上,此代码编译,但在预测时间内出现奇怪的错误(请参阅本答复第一行的链接)。
我不确定,通过我看到的代码,屏蔽功能在内部保留在张量中。我不确切知道它是如何工作的,但它似乎与层内功能的构建分开管理。
因此,尝试使用keras标准模型进行预测:
inp = final_model.input # input placeholder
outputs = [layer.output for layer in final_model.layers] # all layer outputs
fullModel = Model(inp,outputs)
layerPredictions = fullModel.predict(np.expand_dims(TEST_SAMPLE,0))
print(layerPredictions[-2])
答案 1 :(得分:1)
它似乎按预期工作。在Keras中屏蔽不会像您期望的那样产生零,而是跳过在上游层中屏蔽的时间步长,例如LSTM和损耗计算。在RNN的情况下,实现Keras(至少张量流),使得前一步骤的状态被转移tensorflow_backend.py。这样做的部分原因是为了在给出动态输入时保留张量的形状。
如果您真的想要零,则必须使用与Masking类似的逻辑实现您自己的图层并明确返回零。要解决您的问题,您需要使用final_input
:
class MyMask(Masking):
"""Layer that adds a mask based on initial input."""
def compute_mask(self, inputs, mask=None):
# Might need to adjust shapes
return K.any(K.not_equal(inputs[0], self.mask_value), axis=-1)
def call(self, inputs):
# We just return input back
return inputs[1]
def compute_output_shape(self, input_shape):
return input_shape[1]
final_model = MyMask(mask_value=-2.)([final_input, final_model])
你可能可以用更简单的方式附加掩码,但是这个自定义类基本上会根据你的初始输入添加一个掩码,并输出一个现在有掩码的Keras张量。
您的LSTM将在您的示例中忽略第二张图片。要确认您可以return_sequences=True
并检查2张图像的输出是否相同。
答案 2 :(得分:1)
我正在尝试实现同一件事,我希望我的LSTM序列具有可变大小。但是,我什至无法实现您的原始模型。我收到以下错误:TypeError:图层input_1不支持遮罩,但传递了input_mask:Tensor(“ time_distributed_1 / Reshape_1:0”,shape =(?, 100,100),dtype = bool)我正在使用tensorflow 1.10和keras 2.2.2
我通过添加第二个输入(掩码)来指定LSTM考虑哪些时间步长,从而解决了该问题。这样,图像序列始终具有相同数量的时间步长,CNN始终生成输出,但对于LSTM输入,其中一些会被忽略。但是,需要仔细选择丢失的图像,以免影响批量归一化。
def LSTM_CNN(params):
resnet = ResNet50(include_top=False, weights='imagenet', pooling = 'avg')
input_layer = Input(shape=(params.numFrames, params.height, params.width, 3))
input_mask = Input(shape=(params.numFrames,1))
curr_layer = TimeDistributed(resnet)(input_layer)
resnetOutput = Dropout(0.5)(curr_layer)
curr_layer = multiply([resnetOutput,input_mask])
cnn_output = curr_layer
curr_layer = Masking(mask_value=0.0)(curr_layer)
lstm_out = LSTM(256, dropout=0.5)(curr_layer)
output = Dense(output_dim=params.numClasses, activation='sigmoid')(lstm_out)
model = Model([input_layer, input_mask], output)
return model