【问题标题】:How to add a tensor of trainable weights to a Keras tensor?如何将可训练权重的张量添加到 Keras 张量?
【发布时间】:2018-10-18 20:41:28
【问题描述】:

按照https://arxiv.org/pdf/1503.08895.pdf 第 5 页上的“时间编码”部分(顺便说一句,一篇很棒的论文),我说了 N 个维度为 M 的嵌入向量。所以我的 Keras 张量是(批量大小,N,M)我想为每个批量大小的样本添加一个 N x M 的权重矩阵。为此,我创建了自己的 Keras 层:

from constants import BATCH_SIZE
class Added_Weights(Layer):

  def __init__(self, input_dim, output_dim, **kwargs):
      self.output_dim = output_dim
      self.input_dim = input_dim

      super(Added_Weights, self).__init__(**kwargs)

  def build(self, input_shape):
      # Create a trainable weight variable for this layer.
      self.kernel = self.add_weight(name='kernel',
                                  shape=(BATCH_SIZE, self.input_dim[0], self.input_dim[1]),
                                  initializer=RandomNormal(mean=0., stddev=0.05, seed=None),
                                  trainable=True)

      print("kernel has shape "+self.kernel.shape + " or "+K.int_shape(self.kernel))
      super(Added_Weights, self).build(input_shape)  

  def call(self, x, **kwargs):
      return Add()([x, self.kernel])

  def compute_output_shape(self, input_shape):
      return (BATCH_SIZE, self.input_dim[0], self.input_dim[1])

这是可行的,但问题是 BATCH_SIZE 许多矩阵中的每一个都有不同的权重。我需要为批次中的每个样本添加相同的权重。

所以我尝试了几件事。 Keras 有一个内置的 RepeatVector 层,所以我尝试给出内核形状 (N, M) 并执行 RepeatVector (BATCH_SIZE)(kernel),但由于某种原因最终得到了形状 (N, BATCH_SIZE, M)。我想在那里使用 Reshape,但 Reshape() 将第一个维度视为 batch_size 并且不允许我修改它。 Permute() 也有同样的问题。

另一个想法是按照代码中的样子制作初始形状,然后遍历张量以将切片 1 到 BATCH_SIZE-1 设置为等于切片 0,因此它们都具有相同的权重,但我m 不允许以这种方式为 Keras 张量赋值。

我唯一的另一个想法是尝试使用形状 (N, M) 并希望 Keras 足够聪明,可以将其添加到输入的每个切片中,但是在将 Add() 应用于我的 (?, N, M) 和 (N, M) 内核,不知何故我最终得到了一个 (N, N, M) 张量,此时我们已经死了。

【问题讨论】:

    标签: keras add shapes


    【解决方案1】:

    我认为你把事情复杂化了。只需将权重定义为 build 中的 N x M 张量,并与 call 中的输入张量进行求和。我将您的代码调整如下:

    from keras.engine.topology import Layer
    from keras.models import Model
    from keras.layers import Input
    import numpy as np
    
    N = 3
    M = 4
    BATCH_SIZE = 1
    
    class Added_Weights(Layer):
        def __init__(self, **kwargs):
            super(Added_Weights, self).__init__(**kwargs)
    
        def build(self, input_shape):
            # Create a trainable weight variable for this layer.
            self.kernel = self.add_weight(name='kernel',
                                          shape=(input_shape[1], input_shape[2]),
                                          initializer='ones',  # TODO: Choose your initializer
                                          trainable=True)
            super(Added_Weights, self).build(input_shape)
    
        def call(self, x, **kwargs):
            # Implicit broadcasting occurs here.
            # Shape x: (BATCH_SIZE, N, M)
            # Shape kernel: (N, M)
            # Shape output: (BATCH_SIZE, N, M)
            return x + self.kernel
    
        def compute_output_shape(self, input_shape):
            return input_shape
    
    
    a = Input(shape=(N, M))
    layer = Added_Weights()(a)
    model = Model(inputs=a,
                  outputs=layer)
    
    a = np.zeros(shape=(BATCH_SIZE, N, M))
    pred = model.predict(a)
    print(pred)
    

    请注意,self.kernelcall 中被隐式广播以匹配 x 的形状,因此将相同的权重添加到批次中的每个样本。

    【讨论】:

    • 我认为像“x + self.kernel”这样的直接操作丢失了keras张量的“历史”属性,我记得有过这种问题,但我会尝试一下。
    • 我已经以类似的方式实现了几个自定义层,我没有遇到任何问题。但是请让我知道这是否会产生错误(如果可能,请提出另一个问题):)
    • 一定是别的东西,这看起来像是在工作。非常感谢,我将其标记为已接受:D
    • 自从我添加了这个组件后,我的模型的收敛性就完全被破坏了。如果您有时间提供帮助,这里是完整的问题stackoverflow.com/questions/50400481/…
    猜你喜欢
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-17
    • 2019-01-03
    • 1970-01-01
    • 2019-09-30
    相关资源
    最近更新 更多