web-dev-qa-db-fra.com

fonction add_loss dans keras

Actuellement, je suis tombé sur des autoencodeurs variationnels et j'ai essayé de les faire fonctionner sur MNIST à l'aide de keras. J'ai trouvé un tutoriel sur github .

Ma question concerne les lignes de code suivantes:

# Build model
vae = Model(x, x_decoded_mean)

# Calculate custom loss
xent_loss = original_dim * metrics.binary_crossentropy(x, x_decoded_mean)
kl_loss = - 0.5 * K.sum(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1)
vae_loss = K.mean(xent_loss + kl_loss)

# Compile
vae.add_loss(vae_loss)
vae.compile(optimizer='rmsprop')

Pourquoi add_loss est-il utilisé au lieu de le spécifier comme option de compilation? Quelque chose comme vae.compile(optimizer='rmsprop', loss=vae_loss) ne semble pas fonctionner et génère l'erreur suivante:

ValueError: The model cannot be compiled because it has no loss to optimize.

Quelle est la différence entre cette fonction et une fonction de perte personnalisée, que je peux ajouter comme argument pour Model.fit ()?

Merci d'avance! 

P.S .: Je sais qu'il y a plusieurs problèmes à ce sujet sur github, mais la plupart d'entre eux étaient ouverts et non commentés. Si cela a déjà été résolu, partagez le lien!

EDIT: J'ai supprimé la ligne qui ajoute la perte au modèle et utilisé l'argument de perte de la fonction de compilation. Cela ressemble à ceci maintenant:

# Build model
vae = Model(x, x_decoded_mean)

# Calculate custom loss
xent_loss = original_dim * metrics.binary_crossentropy(x, x_decoded_mean)
kl_loss = - 0.5 * K.sum(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1)
vae_loss = K.mean(xent_loss + kl_loss)

# Compile
vae.compile(optimizer='rmsprop', loss=vae_loss)

Cela jette un TypeError:

TypeError: Using a 'tf.Tensor' as a Python 'bool' is not allowed. Use 'if t is not None:' instead of 'if t:' to test if a tensor is defined, and use TensorFlow ops such as tf.cond to execute subgraphs conditioned on the value of a tensor.

EDIT2: Contournement Grâce aux efforts de @ MarioZ, j'ai pu trouver une solution de contournement à cet égard. 

# Build model
vae = Model(x, x_decoded_mean)

# Calculate custom loss in separate function
def vae_loss(x, x_decoded_mean):
    xent_loss = original_dim * metrics.binary_crossentropy(x, x_decoded_mean)
    kl_loss = - 0.5 * K.sum(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1)
    vae_loss = K.mean(xent_loss + kl_loss)
    return vae_loss

# Compile
vae.compile(optimizer='rmsprop', loss=vae_loss)

...

vae.fit(x_train, 
    x_train,        # <-- did not need this previously
    shuffle=True,
    epochs=epochs,
    batch_size=batch_size,
    validation_data=(x_test, x_test))     # <-- worked with (x_test, None) before

Pour une raison étrange, j'ai dû spécifier explicitement y et y_test lors de l'ajustement du modèle. À l'origine, je n'avais pas besoin de faire ça. Les échantillons produits me semblent raisonnables. 

Bien que je puisse résoudre ce problème, je ne sais toujours pas quelles sont les différences/(dés) avantages de ces deux méthodes (à part le besoin d'une syntaxe différente). Quelqu'un peut-il me donner plus de perspicacité? Merci!

8
DocDriven

Je vais essayer de répondre à la question initiale de savoir pourquoi model.add_loss() est utilisé au lieu de spécifier une fonction de perte personnalisée à model.compile(loss=...).

Toutes les fonctions de perte dans Keras prennent toujours deux paramètres y_true et y_pred. Jetez un coup d’œil à la définition des différentes fonctions d’affaiblissement standard disponibles dans Keras, elles ont toutes ces deux paramètres. Ce sont les «cibles» (la variable Y dans de nombreux manuels) et la sortie réelle du modèle. La plupart des fonctions de perte standard peuvent être écrites comme une expression de ces deux tenseurs. Mais certaines pertes plus complexes ne peuvent pas être écrites de cette façon. Pour votre exemple VAE, c'est le cas, car la fonction de perte dépend également de tenseurs supplémentaires, à savoir z_log_var et z_mean, qui ne sont pas disponibles pour les fonctions de perte. L'utilisation de model.add_loss() n'a pas cette restriction et vous permet d'écrire des pertes beaucoup plus complexes qui dépendent de nombreux autres tenseurs, mais cela présente l'inconvénient d'être plus dépendant du modèle, alors que les fonctions de perte standard fonctionnent avec n'importe quel modèle.

(Remarque: le code proposé dans les autres réponses ici est quelque peu trompeur, car il utilise simplement des variables globales pour se faufiler dans les dépendances supplémentaires requises. La fonction de perte n'est donc pas une vraie fonction du point de vue mathématique. moins de code propre et je m'attends à ce qu'il soit plus sujet aux erreurs.)

7
jlh

Essaye ça:

import pandas as pd
import numpy as np
import pickle
import matplotlib.pyplot as plt
from scipy import stats
import tensorflow as tf
import seaborn as sns
from pylab import rcParams
from sklearn.model_selection import train_test_split
from keras.models import Model, load_model, Sequential
from keras.layers import Input, Lambda, Dense, Dropout, Layer, Bidirectional, Embedding, Lambda, LSTM, RepeatVector, TimeDistributed, BatchNormalization, Activation, Merge
from keras.callbacks import ModelCheckpoint, TensorBoard
from keras import regularizers
from keras import backend as K
from keras import metrics
from scipy.stats import norm
from keras.utils import to_categorical
from keras import initializers
bias = bias_initializer='zeros'

from keras import objectives




np.random.seed(22)



data1 = np.array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
       1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0], dtype='int32')

data2 = np.array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
       1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0], dtype='int32')


data3 = np.array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
       1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0], dtype='int32')

#train = np.zeros(shape=(992,54))
#test = np.zeros(shape=(921,54))

train = np.zeros(shape=(300,54))
test = np.zeros(shape=(300,54))

for n, i in enumerate(train):
    if (n<=100):
        train[n] = data1
    Elif (n>100 and n<=200):
        train[n] = data2
    Elif(n>200):
        train[n] = data3


for n, i in enumerate(test):
    if (n<=100):
        test[n] = data1
    Elif(n>100 and n<=200):
        test[n] = data2
    Elif(n>200):
        test[n] = data3


batch_size = 5
original_dim = train.shape[1]

intermediate_dim45 = 45
intermediate_dim35 = 35
intermediate_dim25 = 25
intermediate_dim15 = 15
intermediate_dim10 = 10
intermediate_dim5 = 5
latent_dim = 3
epochs = 50
epsilon_std = 1.0

def sampling(args):
    z_mean, z_log_var = args
    epsilon = K.random_normal(shape=(K.shape(z_mean)[0], latent_dim), mean=0.,
                              stddev=epsilon_std)
    return z_mean + K.exp(z_log_var / 2) * epsilon

x = Input(shape=(original_dim,), name = 'first_input_mario')

h1 = Dense(intermediate_dim45, activation='relu', name='h1')(x)
hD = Dropout(0.5)(h1)
h2 = Dense(intermediate_dim25, activation='relu', name='h2')(hD)
h3 = Dense(intermediate_dim10, activation='relu', name='h3')(h2)
h = Dense(intermediate_dim5, activation='relu', name='h')(h3) #bilo je relu
h = Dropout(0.1)(h)

z_mean = Dense(latent_dim, activation='relu')(h)
z_log_var = Dense(latent_dim, activation='relu')(h)

z = Lambda(sampling, output_shape=(latent_dim,))([z_mean, z_log_var])

decoder_h = Dense(latent_dim, activation='relu')
decoder_h1 = Dense(intermediate_dim5, activation='relu')
decoder_h2 = Dense(intermediate_dim10, activation='relu')
decoder_h3 = Dense(intermediate_dim25, activation='relu')
decoder_h4 = Dense(intermediate_dim45, activation='relu')

decoder_mean = Dense(original_dim, activation='sigmoid')


h_decoded = decoder_h(z)
h_decoded1 = decoder_h1(h_decoded)
h_decoded2 = decoder_h2(h_decoded1)
h_decoded3 = decoder_h3(h_decoded2)
h_decoded4 = decoder_h4(h_decoded3)

x_decoded_mean = decoder_mean(h_decoded4)

vae = Model(x, x_decoded_mean)


def vae_loss(x, x_decoded_mean):
    xent_loss = objectives.binary_crossentropy(x, x_decoded_mean)
    kl_loss = -0.5 * K.mean(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var))
    loss = xent_loss + kl_loss
    return loss

vae.compile(optimizer='rmsprop', loss=vae_loss)

vae.fit(train, train, batch_size = batch_size, epochs=epochs, shuffle=True,
        validation_data=(test, test))


vae = Model(x, x_decoded_mean)

encoder = Model(x, z_mean)

decoder_input = Input(shape=(latent_dim,))

_h_decoded = decoder_h  (decoder_input)
_h_decoded1 = decoder_h1  (_h_decoded)
_h_decoded2 = decoder_h2  (_h_decoded1)
_h_decoded3 = decoder_h3  (_h_decoded2)
_h_decoded4 = decoder_h4  (_h_decoded3)

_x_decoded_mean = decoder_mean(_h_decoded4)
generator = Model(decoder_input, _x_decoded_mean)
generator.summary()
0
MarioZ