【问题标题】:element-wise multiplication with broadcasting in keras custom layer在 keras 自定义层中使用广播进行元素乘法
【发布时间】:2017-11-14 15:10:36
【问题描述】:

我正在创建一个自定义层,其权重需要在激活之前按元素相乘。当输出和输入的形状相同时,我可以让它工作。当我将一阶数组作为输入,将二阶数组作为输出时,就会出现问题。 tensorflow.multiply 支持广播,但是当我尝试在 Layer.call(x, self.kernel) 中使用它时 将 x 乘以 self.kernel 变量,它抱怨说它们是不同的形状:

ValueError: Dimensions must be equal, but are 4 and 3 for 'my_layer_1/Mul' (op: 'Mul') with input shapes: [?,4], [4,3].

这是我的代码:

from keras import backend as K
from keras.engine.topology import Layer
import tensorflow as tf
from keras.models import Sequential
import numpy as np

class MyLayer(Layer):

    def __init__(self, output_dims, **kwargs):
        self.output_dims = output_dims

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

    def build(self, input_shape):
        # Create a trainable weight variable for this layer.
        self.kernel = self.add_weight(name='kernel',
                                      shape=self.output_dims,
                                      initializer='ones',
                                      trainable=True)


        super(MyLayer, self).build(input_shape)  # Be sure to call this somewhere!

    def call(self, x):
        #multiply wont work here?
        return K.tf.multiply(x, self.kernel)

    def compute_output_shape(self, input_shape):
        return (self.output_dims)

mInput = np.array([[1,2,3,4]])
inShape = (4,)
net = Sequential()
outShape = (4,3)
l1 = MyLayer(outShape, input_shape= inShape)
net.add(l1)
net.compile(loss='mean_absolute_error', optimizer='adam', metrics=['accuracy'])
p = net.predict(x=mInput, batch_size=1)
print(p)

编辑: 给定输入形状 (4,) 和输出形状 (4,3),权重矩阵应该与输出形状相同,并用 1 初始化。所以在上面的代码中输入是[1,2,3,4],权重矩阵应该是[[1,1,1,1],[1,1,1,1],[1,1,1 ,1]] 和输出应该看起来像 [[1,2,3,4],[1,2,3,4],[1,2,3,4]]

【问题讨论】:

  • 考虑tf.reshape方法
  • 如果要逐元素乘法,为什么要更改输出形状?
  • 如果我使用 tf.reshape,我想我必须手动复制每一行的值。我觉得 tf.multiply 应该可以工作,但也许我没有以正确的形式传递参数。
  • 我希望输入在输出中的多行广播,如下所示:输入:[1,2,3] 输出:[[1,2,3],[1,2,3 ],[1,2,3]]
  • 元素乘法如何参与其中?

标签: python tensorflow neural-network keras keras-layer


【解决方案1】:

在相乘之前,您需要重复元素以增加形状。 您可以为此使用K.repeat_elements。 (import keras.backend as K)

class MyLayer(Layer):

    #there are some difficulties for different types of shapes   
    #let's use a 'repeat_count' instead, increasing only one dimension
    def __init__(self, repeat_count,**kwargs):
        self.repeat_count = repeat_count
        super(MyLayer, self).__init__(**kwargs)

    def build(self, input_shape):

        #first, let's get the output_shape
        output_shape = self.compute_output_shape(input_shape)
        weight_shape = (1,) + output_shape[1:] #replace the batch size by 1


        self.kernel = self.add_weight(name='kernel',
                                      shape=weight_shape,
                                      initializer='ones',
                                      trainable=True)


        super(MyLayer, self).build(input_shape)  # Be sure to call this somewhere!

    #here, we need to repeat the elements before multiplying
    def call(self, x):

        if self.repeat_count > 1:

             #we add the extra dimension:
             x = K.expand_dims(x, axis=1)

             #we replicate the elements
             x = K.repeat_elements(x, rep=self.repeat_count, axis=1)


        #multiply
        return x * self.kernel


    #make sure we comput the ouptut shape according to what we did in "call"
    def compute_output_shape(self, input_shape):

        if self.repeat_count > 1:
            return (input_shape[0],self.repeat_count) + input_shape[1:]
        else:
            return input_shape

【讨论】:

  • 这很好用,谢谢。我现在只是好奇为什么 tf.multiply 不会自动扩展维度并在我使用它时重复元素。
  • 嗯......也许你忘了保留input_shape[0]?有一个隐藏的批量大小维度。当你说输入形状是(4,)时,它实际上是(None,4),输出形状是(None, 4, 3)。如果您正确添加所有维度,则乘法可能会起作用。
  • 要使这样的乘法起作用,您必须使权重和输入具有相同的维数,例如x.shape=(batch,4,1)weights.shape=(1,4,3)。如果只有一个维度不同,这可行,但我从未尝试过同时使用两个不同的维度。
【解决方案2】:

这是基于 Daniel Möller 回答的另一种解决方案,但像原始代码一样使用 tf.multiply。

class MyLayer(Layer):

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

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

    def build(self, input_shape):
        # Create a trainable weight variable for this layer.
        output_shape = self.compute_output_shape(input_shape)
        self.kernel = self.add_weight(name='kernel',
                                      shape=(1,) + output_shape[1:],
                                      initializer='ones',
                                      trainable=True)


        super(MyLayer, self).build(input_shape)  # Be sure to call this somewhere!

    def call(self, x):

        return K.tf.multiply(x, self.kernel)

    def compute_output_shape(self, input_shape):
        return (input_shape[0],self.output_dim)+input_shape[1:]

【讨论】:

  • 您好,我正在尝试使用此代码,但似乎我需要定义 inShape = '4' 和 outShape = '4' 即内部有 1 个数字的字符串 .. 因为否则我会收到错误消息: ValueError:将形状转换为 TensorShape 时出错:int() 以 10 为基数的无效文字:'('。(如果使用 '(4,)' 和另一个错误,如果我使用元组 (4,)...你觉得有道理吗?为什么会这样?你能解释一下吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-07
  • 1970-01-01
  • 1970-01-01
  • 2020-06-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多