这个问题也是一个问题:https://github.com/fchollet/keras/issues/4266
我正在尝试实现一个卷积LSTM。这是一个循环层,它接受图像作为输入,并使用卷积来计算LSTM中的各种门。所以我试图子类化Recurrent
并改变输入维度。
为了做到这一点,我阅读了关于编写自定义层的文档,并按照建议阅读源代码以了解底层发生的事情。
我读了recursive .py的代码,认为结构很清楚:你继承了Recurrent
,但你不覆盖调用,相反,你提供了一个自定义的step
函数,Recurrent
将负责将步骤应用于序列中的每个条目。
作为起点,我拿了GRU的代码,并试图使其适应我的需要。我想要结合2D卷积和GRU(通常是LSTM,但这并不重要——我决定实现C-GRU)
这个想法是在模型中有一个通常的2D卷积,输出3个特征。这3个特征将被用作GRU中的r、z和h激活。在自定义层中,我只需要跟踪状态。我的层甚至没有可训练的权值,它们包含在卷积中。
原GRU
代码的显著变化如下:
def step(self, x, states):
# the previous state is a 2D vector
h_tm1 = states[0] # previous memory
z=self.inner_activation(x[:,0,:,:])
r=self.inner_activation(x[:,1,:,:])
hh=self.activation(x[:,2,:,:])
h = z * h_tm1 + (1 - z) * hh
return h, [h]
正如你所看到的,我只是重用了卷积中的特征。乘法应该按元素执行。我将调试它以确保它具有预期的行为。
由于状态变为2D,我也改变了initial_state
:
def get_initial_states(self, x):
initial_state=K.zeros_like(x) # (samples, timesteps, input_dim)
# input_dim = (3, x_dim, y_dim)
initial_state=K.sum(initial_state, axis=(1,2)) # (samples, x_dim, y_dim)
return initial_state
output_shape
似乎是为循环网络硬编码的。我重写了它:
def get_output_shape_for(self, input_shape):
#TODO: this is hardcoding for th layout
return (input_shape[0],1,input_shape[2],input_shape[3])
另一个硬编码是input_spec
。在构造函数中,在调用super之后,我用输入维度重写了它:
class CGRU(Recurrent):
def __init__(self,
init='glorot_uniform', inner_init='orthogonal',
activation='tanh', inner_activation='hard_sigmoid', **kwargs):
self.init = initializations.get(init)
self.inner_init = initializations.get(inner_init)
self.activation = activations.get(activation)
self.inner_activation = activations.get(inner_activation)
#removing the regularizers and the dropout
super(CGRU, self).__init__(**kwargs)
# this seems necessary in order to accept 5 input dimensions
# (samples, timesteps, features, x, y)
self.input_spec=[InputSpec(ndim=5)]
还有其他小的变化。您可以在这里找到完整的代码:http://pastebin.com/60ztPis3
运行时,会产生以下错误消息:
theano.tensor.var。AsTensorError: ('Cannot convert [None] to TensorType',)
整个pastebin错误信息:http://pastebin.com/Cdmr20Yn
我在试着调试代码。但这相当困难,它深入到Keras源代码中。有一点:执行从来没有达到我的自定义step
函数。所以很明显构型出了问题。在Recurrent
的call
函数中,input_shape是一个元组,其条目为(None, 40,1,40,40)
这是正确的。我的序列有40个元素。每个都是一个具有1个特征和40x40分辨率的图像。我用的是"th"的布局
这是Recurrent
的call
函数。我的代码达到了对K.rnn
的调用,对我来说设置看起来很好。Input_spec似乎是正确的。但是在K.rnn
期间它崩溃了。没有达到我的阶跃函数
def call(self, x, mask=None):
# input shape: (nb_samples, time (padded with zeros), input_dim)
# note that the .build() method of subclasses MUST define
# self.input_spec with a complete input shape.
input_shape = self.input_spec[0].shape
if self.stateful:
initial_states = self.states
else:
initial_states = self.get_initial_states(x)
constants = self.get_constants(x)
preprocessed_input = self.preprocess_input(x)
last_output, outputs, states = K.rnn(self.step, preprocessed_input,
initial_states,
go_backwards=self.go_backwards,
mask=mask,
constants=constants,
unroll=self.unroll,
input_length=input_shape[1])
在这一点上我迷路了。在我看来,我似乎缺少了配置的某些部分。
更新:
嗯,现在我有一个奇怪的问题:我现在的代码是:
# this is the actual input, fed to the network
inputs = Input((1, 40, 40, 40))
# now reshape to a sequence
reshaped = Reshape((40, 1, 40, 40))(inputs)
conv_inputs = Input((1, 40, 40))
conv1 = Convolution2D(3, 3, 3, activation='relu', border_mode='same')(conv_inputs)
convmodel = Model(input=conv_inputs, output=conv1)
convmodel.summary()
#apply the segmentation to each layer
time_dist=TimeDistributed(convmodel)(reshaped)
from cgru import CGRU
up=CGRU(go_backwards=False, return_sequences=True, name="up")
up=up(time_dist)
output=Reshape([1,40,40,40])(up)
model=Model(input=inputs, output=output)
print(model.summary())
在Theano作为后端的计算机上,这可以工作。模型摘要为:
________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
====================================================================================================
input_1 (InputLayer) (None, 1, 40, 40, 40) 0
____________________________________________________________________________________________________
reshape_1 (Reshape) (None, 40, 1, 40, 40) 0 input_1[0][0]
____________________________________________________________________________________________________
timedistributed_1 (TimeDistribute(None, 40, 3, 40, 40) 30 reshape_1[0][0]
____________________________________________________________________________________________________
up (CGRU) (None, 40, 1, 40, 40) 0 timedistributed_1[0][0]
____________________________________________________________________________________________________
reshape_2 (Reshape) (None, 1, 40, 40, 40) 0 up[0][0]
====================================================================================================
Total params: 30
____________________________________________________________________________________________________
但是在以tensorflow为后端的计算机上,代码失败了。我为convmodel
添加了model.summary()
。在此之前它可以工作:
Layer (type) Output Shape Param # Connected to
====================================================================================================
input_4 (InputLayer) (None, 1, 40, 40) 0
____________________________________________________________________________________________________
convolution2d_2 (Convolution2D) (None, 3, 40, 40) 30 input_4[0][0]
====================================================================================================
Total params: 30
但是随后程序崩溃了:
ValueError: Shapes (?), ?, 40, 40)和(40,?,40)不兼容
似乎Theano和Tensorflow对batch_size有不同(和不兼容)的占位符?请注意,我将Keras配置为在这两种情况下都使用"th"图像布局。
我想问题解决了。initial_states
期望一个列表,而output_dimension
必须被修复。现在它似乎起作用了。底层后端还存在一些其他问题(例如。Theano vs Tensorflow),但这似乎与这个问题无关。
一旦我确定问题已经真正解决,并且图层能够学习,我会用所有必要的步骤更新这个答案。