web-dev-qa-db-fra.com

Implémentation d'une fonction de perte personnalisée dans les keras avec condition

J'ai besoin d'aide avec la fonction de perte de keras. J'ai implémenté une fonction de perte personnalisée sur les keras avec le backend Tensorflow.

J'ai implémenté la fonction de perte personnalisée dans numpy mais ce serait génial si elle pouvait être traduite en fonction de perte de keras. La fonction de perte prend la trame de données et la série d'ID utilisateur. La distance euclidienne pour le même user_id est positive et négative si les user_id sont différents. La fonction renvoie la distance scalaire résumée de la trame de données.

def custom_loss_numpy (encodings, user_id):
# user_id: a pandas series of users
# encodings: a pandas dataframe of encodings

    batch_dist = 0

    for i in range(len(user_id)):
         first_row = encodings.iloc[i,:].values
         first_user = user_id[i]

         for j in range(i+1, len(user_id)):
              second_user = user_id[j]
              second_row = encodings.iloc[j,:].values

        # compute distance: if the users are same then Euclidean distance is positive otherwise negative.
            if first_user == second_user:
                tmp_dist = np.linalg.norm(first_row - second_row)
            else:
                tmp_dist = -np.linalg.norm(first_row - second_row)

            batch_dist += tmp_dist

    return batch_dist

J'ai essayé de mettre en œuvre la fonction de perte de keras. J'ai extrait le tableau numpy des objets tenseurs y_true et y_pred.

def custom_loss_keras(y_true, y_pred):
    # session of my program
    sess = tf_session.TF_Session().get()

    with sess.as_default():
        array_pred = y_pred.eval()
        print(array_pred)

Mais j'obtiens l'erreur suivante.

tensorflow.python.framework.errors_impl.InvalidArgumentError: You must feed a value for placeholder tensor 'dense_1_input' with dtype float and shape [?,102]
 [[Node: dense_1_input = Placeholder[dtype=DT_FLOAT, shape=[?,102], _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]

Toute aide serait très appréciée.

6
Black Mask

Tout d'abord, il n'est pas possible "d'extraire un tableau numpy de y_true Et y_pred" Dans les fonctions de perte Keras. Vous devez utiliser les tenseurs avec les fonctions backend Keras (ou fonctions TF) pour calculer la perte.

En d'autres termes, il serait préférable de penser à une méthode "vectorisée" pour calculer la perte, sans utiliser les boucles if-else et.

Votre fonction de perte peut être calculée selon les étapes suivantes:

  1. Générez une matrice de distances euclidiennes par paire, entre toutes les paires de vecteurs dans encodings.
  2. Générez une matrice I dont l'élément I_ij Est 1 si user_i == user_j Et -1 si user_i != user_j.
  3. Par élément, multipliez les deux matrices et résumez les éléments pour obtenir la perte finale.

Voici une implémentation:

def custom_loss_keras(user_id, encodings):
    # calculate pairwise Euclidean distance matrix
    pairwise_diff = K.expand_dims(encodings, 0) - K.expand_dims(encodings, 1)
    pairwise_squared_distance = K.sum(K.square(pairwise_diff), axis=-1)

    # add a small number before taking K.sqrt for numerical safety
    # (K.sqrt(0) sometimes becomes nan)
    pairwise_distance = K.sqrt(pairwise_squared_distance + K.epsilon())

    # this will be a pairwise matrix of True and False, with shape (batch_size, batch_size)
    pairwise_equal = K.equal(K.expand_dims(user_id, 0), K.expand_dims(user_id, 1))

    # convert True and False to 1 and -1
    pos_neg = K.cast(pairwise_equal, K.floatx()) * 2 - 1

    # divide by 2 to match the output of `custom_loss_numpy`, but it's not really necessary
    return K.sum(pairwise_distance * pos_neg, axis=-1) / 2

J'ai supposé que user_id Sont des entiers dans le code ci-dessus. L'astuce consiste à utiliser K.expand_dims Pour implémenter les opérations par paire. C'est probablement un peu difficile à comprendre à première vue, mais c'est assez utile.

Il devrait donner à peu près la même valeur de perte que custom_loss_numpy (Il y aura une petite différence à cause de K.epsilon()):

encodings = np.random.Rand(32, 10)
user_id = np.random.randint(10, size=32)

print(K.eval(custom_loss_keras(K.variable(user_id), K.variable(encodings))).sum())
-478.4245

print(custom_loss_numpy(pd.DataFrame(encodings), pd.Series(user_id)))
-478.42953553795815

J'ai fait une erreur dans la fonction de perte.

Lorsque cette fonction est utilisée dans la formation, étant donné que Keras change automatiquement y_true Pour être au moins 2D, l'argument user_id N'est plus un tenseur 1D. La forme de celui-ci sera (batch_size, 1).

Pour utiliser cette fonction, l'axe supplémentaire doit être supprimé:

def custom_loss_keras(user_id, encodings):
    pairwise_diff = K.expand_dims(encodings, 0) - K.expand_dims(encodings, 1)
    pairwise_squared_distance = K.sum(K.square(pairwise_diff), axis=-1)
    pairwise_distance = K.sqrt(pairwise_squared_distance + K.epsilon())

    user_id = K.squeeze(user_id, axis=1)  # remove the axis added by Keras
    pairwise_equal = K.equal(K.expand_dims(user_id, 0), K.expand_dims(user_id, 1))

    pos_neg = K.cast(pairwise_equal, K.floatx()) * 2 - 1
    return K.sum(pairwise_distance * pos_neg, axis=-1) / 2
7
Yu-Yang

Il existe deux étapes pour implémenter une fonction de perte personnalisée paramétrée dans Keras. Tout d'abord, écrire une méthode pour le coefficient/métrique. Deuxièmement, écrire une fonction wrapper pour formater les choses comme Keras en a besoin.

  1. Il est en fait un peu plus propre d'utiliser le backend Keras au lieu de tensorflow directement pour des fonctions de perte personnalisées simples comme DICE. Voici un exemple du coefficient implémenté de cette façon:

    import keras.backend as K
    def dice_coef(y_true, y_pred, smooth, thresh):
        y_pred = y_pred > thresh
        y_true_f = K.flatten(y_true)
        y_pred_f = K.flatten(y_pred)
        intersection = K.sum(y_true_f * y_pred_f)
        return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
    
  1. Maintenant, pour la partie délicate. Les fonctions de perte Keras ne doivent prendre que (y_true, y_pred) comme paramètres. Nous avons donc besoin d'une fonction distincte qui renvoie une autre fonction:

    def dice_loss(smooth, thresh):
        def dice(y_true, y_pred)
            return -dice_coef(y_true, y_pred, smooth, thresh)
        return dice
    

Enfin, vous pouvez l'utiliser comme suit dans Keras compile:

# build model 
model = my_model()
# get the loss function
model_dice = dice_loss(smooth=1e-5, thresh=0.5)
# compile model
model.compile(loss=model_dice)
3
PyMatFlow