定义具有其他权重的自定义keras图层时如何使用GRUCell?

时间:2018-12-07 02:17:55

标签: keras keras-layer

我正在定义一个使用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

1 个答案:

答案 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