функция add_loss в керасе - PullRequest
       15

функция add_loss в керасе

0 голосов
/ 27 апреля 2018

В настоящее время я наткнулся на вариационные автоэнкодеры и попытался заставить их работать в MNIST, используя керасы. Я нашел учебник по github .

Мой вопрос касается следующих строк кода:

# 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')

Почему add_loss используется вместо указания его в качестве опции компиляции? Что-то вроде vae.compile(optimizer='rmsprop', loss=vae_loss), похоже, не работает и выдает следующую ошибку:

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

В чем разница между этой функцией и пользовательской функцией потерь, которую я могу добавить в качестве аргумента для Model.fit ()?

Заранее спасибо!

P.S .: Я знаю, что на github есть несколько проблем, связанных с этим, но большинство из них были открытыми и без комментариев. Если это уже решено, пожалуйста, поделитесь ссылкой!

EDIT: Я удалил строку, которая добавляет потерю к модели, и использовал аргумент потери функции компиляции. Теперь это выглядит так:

# 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)

Это выдает ошибку типа:

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.

РЕДАКТИРОВАТЬ 2: ВРЕМЕННОЕ РЕШЕНИЕ Благодаря усилиям @ MarioZ мне удалось найти обходной путь для этого.

# 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

По какой-то странной причине мне пришлось явно указать y и y_test при подгонке модели. Первоначально мне не нужно было этого делать. Полученные образцы мне кажутся разумными.

Хотя я мог бы решить эту проблему, я все еще не знаю, в чем заключаются различия / (недостатки) этих двух методов (кроме необходимости использовать другой синтаксис). Может кто-нибудь дать мне больше понимания? Спасибо!

Ответы [ 4 ]

0 голосов
/ 09 июня 2019

Ответ JIH, конечно, прав, но, возможно, полезно добавить:

model.add_loss () не имеет ограничений, но также устраняет удобство использования, например, целей в model.fit ()

Если у вас есть потери, которые зависят от дополнительных параметров модели, других моделей или внешних переменных, вы все равно можете использовать инкапсулированную функцию потерь типа keras, имея инкапсулирующую функцию, где вы передаете все дополнительные параметры:

def loss_carrier(extra_param1, extra_param2):
    def loss(y_true,y_pred):
        #x = complicated math involving extra_param1, extraparam2, y_true, y_pred
        #remember to use tensor objects, so for example keras.sum, keras.square, keras.mean
        #also remember that if extra_param1, extra_maram2 are variable tensors instead of simple floats,
        #you need to have them defined as inputs=(main,extra_param1, extraparam2) in your keras.model instantiation.
        #and have them defind as keras.Input or tf.placeholder with the right shape.
        return x
    return loss

model.compile(optimizer='adam', loss=loss_carrier)

Трюк - это последняя строка, в которой вы возвращаете функцию, поскольку keras ожидает их только с двумя параметрами y_true и y_pred

Возможно, выглядит сложнее, чем версия model.add_loss, но потеря остается модульной.

0 голосов
/ 03 мая 2018

Попробуйте это:

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 голосов
/ 07 октября 2018

Я попытаюсь ответить на оригинальный вопрос о том, почему model.add_loss() используется вместо указания пользовательской функции потерь для model.compile(loss=...).

Все функции потерь в Керасе всегда принимают два параметра y_true и y_pred. Взгляните на определение различных стандартных функций потерь, доступных в Keras, все они имеют эти два параметра. Это «цели» (переменная Y во многих учебниках) и фактический результат модели. Большинство стандартных функций потерь могут быть записаны как выражение этих двух тензоров. Но некоторые более сложные потери не могут быть записаны таким образом. Для вашего примера VAE это так, потому что функция потерь также зависит от дополнительных тензоров, а именно z_log_var и z_mean, которые недоступны для функций потерь. Использование model.add_loss() не имеет такого ограничения и позволяет записывать гораздо более сложные потери, которые зависят от многих других тензоров, но неудобство заключается в большей зависимости от модели, тогда как стандартные функции потерь работают только с любой моделью.

(Примечание: код, предложенный в других ответах здесь, несколько обманывает, поскольку он просто использует глобальные переменные для проникновения в дополнительные обязательные зависимости. Это делает функцию потерь не истинной функцией в математическом смысле. Я считаю это будет намного менее чистым кодом, и я ожидаю, что он будет более подвержен ошибкам.)

0 голосов
/ 29 апреля 2018

Вам нужно изменить строку компиляции на vae.compile (оптимизатор = 'rmsprop', потеря = vae_loss)

...