Estoy creando una capa personalizada con pesos que deben multiplicarse por elementos antes de la activación. Puedo hacer que funcione cuando la salida y la entrada tienen la misma forma. El problema ocurre cuando tengo una matriz de primer orden como entrada con una matriz de segundo orden como salida. tensorflow.multiply admite la transmisión, pero cuando trato de usarlo en Layer.call (x, self.kernel) para multiplicar x por la variable self.kernel, se queja de que son formas diferentes que dicen:

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

Aquí está mi código:

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)

Editar: Dada la forma de entrada (4,) y la forma de salida (4,3), la matriz de peso debe tener la misma forma que la salida e inicializada con unos. Entonces, en el código anterior, la entrada es [1,2,3,4], la matriz de peso debe ser [[1,1,1,1], [1,1,1,1], [1,1,1 , 1]] y el resultado debería verse como [[1,2,3,4], [1,2,3,4], [1,2,3,4]]

6
buckithed 14 nov. 2017 a las 18:10

2 respuestas

La mejor respuesta

Antes de multiplicar, debes repetir los elementos para aumentar la forma. Puedes usar K.repeat_elements para eso. (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
4
Daniel Möller 14 nov. 2017 a las 16:03

Aquí hay otra solución que se basa en la respuesta de Daniel Möller, pero usa tf.multiply como el código original.

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:]
1
buckithed 17 nov. 2017 a las 15:34