我正在定义一个使用cnn和rnn的自定义keras单词嵌入层,我修改了GRU的代码以使用GRUCell和一些自定义的权重来CNN过滤器。重要的是,过滤器权重是在该层的构建方法中定义的,而GRUCell权重是通过调用单元格构建方法来定义的。由于某种原因,这取决于我是从RNN还是从Layer继承而导致仅注册其中之一的权重。如果我从RNN继承,则仅注册RNN的权重;如果我从层继承,则仅注册CNN的权重。寄存器是指当我使用该层并检查参数数量时,它们分别仅对应于CNN和RNN的参数。我假设其他权重没有得到训练,因为训练不起作用。我想了解我的代码有什么问题,是否需要在该层的build方法内定义RNN的权重?
from keras.layers.recurrent import RNN, GRUCell
import keras.backend as K
class cnn_lstm_layer(RNN):
def __init__(self, num_vocab, embed_dim, units=10):
self.num_vocab = num_vocab
self.embed_dim = embed_dim
self.units = units
cell = GRUCell(units)
super().__init__(cell)
def build(self, input_shape):
f2_count = 3
f3_count = 4
f4_count = 5
self.embeddings = self.add_weight(
shape=[self.num_vocab, self.embed_dim],
initializer='glorot_uniform',
name='char_embeddings')
self.kernel2 = self.add_weight(
shape=[2, self.embed_dim, 1, f2_count],
initializer='glorot_uniform',
name='f2')
self.kernel3 = self.add_weight(
shape=[3, self.embed_dim, 1, f3_count],
initializer='glorot_uniform',
name='f3')
self.kernel4 = self.add_weight(
shape=[4, self.embed_dim, 1, f4_count],
initializer='glorot_uniform',
name='f4')
self.conv_output_len = f2_count + f3_count + f4_count
step_input_shape = [None, self.conv_output_len]
self.cell.build(step_input_shape)
self.built = True
def call(self, inputs):
'''
uses the following reshaping trick
https://stackoverflow.com/questions/51091544/time-distributed-convolutional-layers-in-tensorflow
'''
inputs = K.cast(inputs, tf.int32)
encoded = K.gather(self.embeddings, inputs)
# encoded = K.print_tensor(encoded, message = 'encoded: ')
# encoded = K.tf.Print(encoded, data = [encoded],message='encoded: ',summarize = 1000)
encoded = K.expand_dims(encoded, -1)
input_shape = K.int_shape(encoded)
_, s_words, s_char, s_embed_dim, _ = input_shape
encoded = K.reshape(encoded, (-1, s_char, s_embed_dim, 1))
paddings2 = [[0, 0], [1, 0], [0, 0], [0, 0]]
paddings3 = [[0, 0], [2, 0], [0, 0], [0, 0]]
paddings4 = [[0, 0], [3, 0], [0, 0], [0, 0]]
c2 = K.conv2d(tf.pad(encoded, paddings2),
self.kernel2,
data_format='channels_last',
padding='valid') # shape = (?,19,1,3)
c3 = K.conv2d(tf.pad(encoded, paddings3),
self.kernel3,
data_format='channels_last',
padding='valid')
c4 = K.conv2d(tf.pad(encoded, paddings4),
self.kernel4,
data_format='channels_last',
padding='valid')
c = K.concatenate([c2, c3, c4], axis=3) # shape = (?,19,1,12)
c = K.squeeze(c, 2) # shape = (?,19,12)
initial_state = self.get_initial_state(c)
last_output, outputs, states = K.rnn(self.cell.call,
c,
initial_state)
output = last_output
output = K.reshape(output,(-1,s_words,self.units))
return output
def get_initial_state(self, inputs):
# build an all-zero tensor of shape (samples, output_dim)
initial_state = K.zeros_like(inputs) # (samples, timesteps, input_dim)
initial_state = K.sum(initial_state, axis=(1, 2)) # (samples,)
initial_state = K.expand_dims(initial_state) # (samples, 1)
if hasattr(self.cell.state_size, '__len__'):
return [K.tile(initial_state, [1, dim])
for dim in self.cell.state_size]
else:
return [K.tile(initial_state, [1, self.cell.state_size])]
def compute_output_shape(self, input_shape):
batch_size, s_words, s_char = input_shape
output_shape = (batch_size, s_words,self.units)
return output_shape
答案 0 :(得分:0)
问题的核心在于trainable_weights方法。在RNN类中,它定义为
def trainable_weights(self):
if not self.trainable:
return []
if isinstance(self.cell, Layer):
return self.cell.trainable_weights
return []
在Layer类中,它定义为
def trainable_weights(self):
trainable = getattr(self, 'trainable', True)
if trainable:
return self._trainable_weights
else:
return []
这就是为什么从RNN和Layer继承时仅赋予RNN或CNN权重的原因。然后解决方案是重写trainable_weights,以同时考虑外部build方法中定义的权重和单元格build方法中定义的权重。即
@property
def trainable_weights(self):
return self._trainable_weights + self.cell.trainable_weights