web-dev-qa-db-fra.com

multiplication par élément avec diffusion dans la couche personnalisée de keras

Je crée un calque personnalisé avec des poids qui doivent être multipliés par élément avant l'activation. Je peux le faire fonctionner lorsque la sortie et l'entrée ont la même forme. Le problème se produit lorsque j'ai un tableau de premier ordre en entrée avec un tableau de second ordre en sortie. tensorflow.multiply prend en charge la diffusion, mais lorsque je tente de l’utiliser dans Layer.call (x, self.kernel) pour multiplier x par la variable self.kernel, il se plaint du fait qu’il s’agit de formes différentes:

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

voici mon code:

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)

Éditer: Étant donné la forme en entrée (4,) et la forme en sortie (4,3), la matrice de pondération doit avoir la même forme que la sortie et être initialisée avec celles-ci. Ainsi, dans le code ci-dessus, l'entrée est [1,2,3,4], la matrice de pondération doit être [[1,1,1,1], [1,1,1,1], [1,1,1 , 1]] et le résultat devrait ressembler à [[1,2,3,4], [1,2,3,4], [1,2,3,4]]

3
buckithed

Avant de multiplier, vous devez répéter les éléments pour augmenter la forme. Vous pouvez utiliser K.repeat_elements pour cela. (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
1
Daniel Möller

voici une autre solution basée sur la réponse de Daniel Möller, mais qui utilise tf.multiply comme le code d'origine.

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:]
0
buckithed