web-dev-qa-db-ja.com

kerasのadd_loss関数

現在、変則的なオートエンコーダーに出くわし、kerasを使用して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()の引数として追加できるカスタム損失関数の違いは何ですか?

前もって感謝します!

追伸: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がスローされます。

_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:回避策@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を明示的に指定する必要がありました。もともと、私はこれをする必要はありませんでした。作成されたサンプルは私にとっては合理的なようです。

これを解決することはできましたが、これらの2つの方法の違い/(欠点)の利点が何であるかはまだわかりません(異なる構文が必要なこと以外は)。誰かが私にもっと洞察を与えることができますか?ありがとう!

15
DocDriven

model.add_loss()にカスタム損失関数を指定する代わりに、なぜmodel.compile(loss=...)が使用されているのかという元の質問に答えようとします。

Kerasのすべての損失関数は、常に2つのパラメーター_y_true_および_y_pred_を取ります。 Kerasで利用可能なさまざまな標準損失関数の定義を見てください。これらはすべてこれら2つのパラメーターを持っています。それらは、「ターゲット」(多くの教科書のY変数)およびモデルの実際の出力です。ほとんどの標準損失関数は、これらの2つのテンソルの式として記述できます。しかし、いくつかのより複雑な損失はそのように書くことはできません。 VAEの例では、損失関数も追加のテンソル、つまり_z_log_var_と_z_mean_に依存しているため、損失関数では使用できません。 model.add_loss()を使用すると、このような制限はなく、他の多くのテンソルに依存するはるかに複雑な損失を記述できますが、モデルに依存するという不便さがありますが、標準の損失関数は、モデル。

(注:ここの他の回答で提案されているコードは、グローバル変数を使用して追加の必要な依存関係をこっそりとするので、やや不正です。これにより、損失関数は数学的な意味で真の関数ではありません。コードのクリーン度が低くなり、エラーが発生しやすくなります。)

17
jlh

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の2つのパラメーターだけで関数を期待するため、関数を返す最後の行です。

Model.add_lossバージョンよりも複雑に見えるかもしれませんが、損失はモジュラーのままです。

1
Nric

これを試して:

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