web-dev-qa-db-fra.com

Fonction de perte pondérée personnalisée dans Keras pour peser chaque élément

J'essaie de créer une fonction de perte pondérée simple.

Disons que j'ai des dimensions d'entrée 100 * 5 et des dimensions de sortie également 100 * 5. J'ai également une matrice de poids de la même dimension.

Quelque chose comme ceci:

import numpy as np
train_X = np.random.randn(100, 5)
train_Y = np.random.randn(100, 5)*0.01 + train_X

weights = np.random.randn(*train_X.shape)

Définition de la fonction de perte personnalisée

def custom_loss_1(y_true, y_pred):
    return K.mean(K.abs(y_true-y_pred)*weights)

Définition du modèle

from keras.layers import Dense, Input
from keras import Model
import keras.backend as K

input_layer = Input(shape=(5,))
out = Dense(5)(input_layer)
model = Model(input_layer, out)

Les tests avec les métriques existantes fonctionnent correctement

model.compile('adam','mean_absolute_error')
model.fit(train_X, train_Y, epochs=1)

Les tests avec notre fonction de perte personnalisée ne fonctionnent pas

model.compile('adam',custom_loss_1)
model.fit(train_X, train_Y, epochs=10)

Il donne la trace de pile suivante:

InvalidArgumentError (see above for traceback): Incompatible shapes: [32,5] vs. [100,5]
 [[Node: loss_9/dense_8_loss/mul = Mul[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/device:CPU:0"](loss_9/dense_8_loss/Abs, loss_9/dense_8_loss/mul/y)]]

D'où vient le numéro 32?

Test d'une fonction de perte avec des poids comme tenseurs Keras

def custom_loss_2(y_true, y_pred):
    return K.mean(K.abs(y_true-y_pred)*K.ones_like(y_true))

Cette fonction semble faire le travail. Donc, suggère probablement qu'un tenseur Keras en tant que matrice de poids fonctionnerait. J'ai donc créé une autre version de la fonction de perte.

Fonction de perte essayez 3

from functools import partial

def custom_loss_3(y_true, y_pred, weights):
    return K.mean(K.abs(y_true-y_pred)*K.variable(weights, dtype=y_true.dtype))

cl3 = partial(custom_loss_3, weights=weights)  

L'ajustement des données à l'aide de cl3 donne la même erreur que ci-dessus.

InvalidArgumentError (see above for traceback): Incompatible shapes: [32,5] vs. [100,5]
     [[Node: loss_11/dense_8_loss/mul = Mul[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/device:CPU:0"](loss_11/dense_8_loss/Abs, loss_11/dense_8_loss/Variable/read)]]

Je me demande ce qui me manque! J'aurais pu utiliser la notion de sample_weight dans Keras; mais alors je devrais remodeler mes entrées en un vecteur 3D.

Je pensais que cette fonction de perte personnalisée aurait vraiment dû être triviale.

12
Nipun Batra

Dans model.fit La taille du lot est de 32 par défaut, c'est de là que vient ce nombre. Voici ce qui se passe:

  • Dans custom_loss_1, Le tenseur K.abs(y_true-y_pred) a la forme (batch_size=32, 5), Tandis que le tableau numpy weights a la forme (100, 5). Il s'agit d'une multiplication non valide, car les dimensions ne correspondent pas et la diffusion ne peut pas être appliquée.

  • Dans custom_loss_2, Ce problème n'existe pas car vous multipliez 2 tenseurs de même forme (batch_size=32, 5).

  • Dans custom_loss_3, Le problème est le même que dans custom_loss_1, Car la conversion de weights en une variable Keras ne change pas leur forme.


PDATE: Il semble que vous souhaitiez donner un poids différent à chaque élément de chaque échantillon d'apprentissage, donc le tableau weights devrait avoir la forme (100, 5). Dans ce cas, j'entrerais le tableau de vos poids dans votre modèle, puis j'utiliserais ce tenseur dans la fonction de perte:

import numpy as np
from keras.layers import Dense, Input
from keras import Model
import keras.backend as K
from functools import partial


def custom_loss_4(y_true, y_pred, weights):
    return K.mean(K.abs(y_true - y_pred) * weights)


train_X = np.random.randn(100, 5)
train_Y = np.random.randn(100, 5) * 0.01 + train_X
weights = np.random.randn(*train_X.shape)

input_layer = Input(shape=(5,))
weights_tensor = Input(shape=(5,))
out = Dense(5)(input_layer)
cl4 = partial(custom_loss_4, weights=weights_tensor)
model = Model([input_layer, weights_tensor], out)
model.compile('adam', cl4)
model.fit(x=[train_X, weights], y=train_Y, epochs=10)
12
rvinas