为了举例说明,我有一个输入,包含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部分,但是它用开始(提供非零输出)来处理它。详情如下:
1,LSTM输出很好:
[array([[-0.15324114, -0.09620268, -0.01668587, 0.07938149, -0.00757846]], dtype=float32)]
层_out-2和层_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)]
图层-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.]],
请注意,代码似乎正确地使用了一个简单的,如:
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上被问到了,比如这里,这里和这里,但是我似乎找不到任何具体的解决方案。
链接表明,这些问题似乎源于将TimeDistributed应用于BatchNormalization的组合,而Lambda标识层或重塑层的黑客修复则消除了错误,但似乎没有输出正确的模型。
我试图通过以下方式强制基本模型支持掩蔽:
base_model.__setattr__('supports_masking',True)
我还尝试通过以下方式应用身份层:
TimeDistributed(Lambda(lambda x: base_model(x), output_shape=lambda s:s))(final_model)
但这些似乎都不起作用。请注意,我希望最后的模式是可培训的,特别是CNN部分应该保持可培训。
发布于 2018-05-07 04:29:22
不完全确定这是否有效,但基于在此发表的评论,使用较新版本的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的尺寸时,这个掩蔽张量就变得不相容了。
在掩蔽之前,做第一个扁平步骤,将确保掩蔽张量对三维张量正常工作。然后再将图像扩展到原来的大小。
我可能很快就会尝试安装新版本来亲自测试它,但是这些安装过程已经造成了太多的麻烦,我在这里遇到了一些重要的问题。
在我的机器上,这段代码会编译,但是这个奇怪的错误会出现在预测时间(参见这个答案第一行的链接)。
建立预测中间层的模型
根据我看到的代码,我不确定掩蔽函数是否在内部保持在张量中。我不知道它到底是如何工作的,但它似乎与层内函数的构建是分开管理的。
因此,尝试使用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])
发布于 2018-05-09 11:42:56
它似乎是按预期运作的。Keras中的掩蔽不会像您预期的那样产生零,而是跳过上游层(如LSTM和损失计算)中屏蔽的时间步骤。在RNN的情况下,Keras (至少是tensorflow)被实现,从而将前一步的状态传递给backend.py。这在一定程度上是为了在动态输入时保持张量的形状。
如果您真的想要零,那么您必须使用与掩蔽和返回零类似的逻辑来实现自己的层。为了解决您的问题,您需要在使用final_input
的最终LSTM层之前使用一个掩码。
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
,并检查两个图像的输出是否相同。
发布于 2018-08-21 11:37:51
我正在尝试实现同样的东西,我希望我的LSTM序列有可变的大小。然而,我甚至不能实现你的原始模型。我得到以下错误: TypeError: input_1不支持掩蔽,但是传递了一个input_mask:张量(“time_distributed_1/repe1: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
https://stackoverflow.com/questions/50094633
复制相似问题