Mpizos Dimitris
Mpizos Dimitris

Reputation: 5001

Keras: Metric and Loss with different input

When creating custom loss and metric function in a keras model it assumes for both cases that inputs are of (y_true, y_pred):

def custom_loss(y_true, y_pred):
    .
    return loss

def custom_metric(y_true, y_pred):
    .
    return metric

And the input of y_pred is the output of the Model. Example:

model = Model(inputs = [input1,..inputN], outputs=loss)
model.compile(loss=costum_loss, metrics=costum_metric)

In this case above for both loss and metric the y_pred will be the loss.

What if I want different input in costum_loss and different in the costum_metric. Is there a way to do it?

Edit:

More speciffically I want my loss to be:

def warp_loss(X):
    z, positive_entity, negatives_entities = X
    positiveSim = Lambda(lambda x: similarity(x[0], x[1]), output_shape=(1,), name="positive_sim")([z, positive_entity])
    z_reshaped = Reshape((1, z.shape[1].value))(z)
    negativeSim = Lambda(lambda x: similarity(x[0], x[1]), output_shape=(negatives_titles.shape[1].value, 1,), name="negative_sim")([z_reshaped, negatives_entities])
    loss = Lambda(lambda x: max_margin_loss(x[0], x[1]), output_shape=(1,), name="max_margin")([positiveSim, negativeSim])
    return loss

def mean_loss(y_true, y_pred):
    return K.mean(y_pred - 0 * y_true)

and the metric:

def metric(X):
    z, positive_entity, negatives_entities = X
    positiveSim = Lambda(lambda x: similarity(x[0], x[1]), output_shape=(1,), name="positive_sim")([z, positive_entity])
    z_reshaped = Reshape((1, z.shape[1].value))(z)
    negativeSim = Lambda(lambda x: similarity(x[0], x[1]), output_shape=(negatives_titles.shape[1].value, 1,), name="negative_sim")([z_reshaped, negatives_entities])
    position = K.sum(K.cast(K.greater(positiveSim, negativeSim), dtype="int32"), axis=1, keepdims=True)
    accuracy = Lambda(lambda x: x / _NUMBER_OF_NEGATIVE_EXAMPLES)(position)
    return accuracy


def mean_acc(y_true, y_pred):
    return K.mean(y_pred - 0 * y_true)

So the first 4 lines are the same and after the two functions change. Could it be possible to use a Callback to print mean_acc?

Upvotes: 0

Views: 1193

Answers (1)

Daniel Möller
Daniel Möller

Reputation: 86650

You don't need the loss to be part of your model, you can make your model output its own outputs and later you apply the loss.

Here is a working code (it could be optimized to avoid repeating operations in both metrics and loss, by adding the commom part to the model)

I had some issues with your shapes, then I made it with arbitrary shapes. Your original lines are commented.

This code works for Keras 2.0.8, with Tensorflow 1.3.0. I suspect you're using Theano, right?

from keras.layers import *
from keras.models import *
import keras.backend as K


def get_divisor(x):
    return K.sqrt(K.sum(K.square(x), axis=-1))


def similarity(a, b):
    numerator = K.sum(a * b, axis=-1)
    denominator = get_divisor(a) * get_divisor(b)
    denominator = K.maximum(denominator, K.epsilon())
    return numerator / denominator


def max_margin_loss(positive, negative):
    #loss_matrix = K.maximum(0.0, 1.0 + negative - Reshape((1,))(positive))
    loss_matrix = K.maximum(0.0, 1.0 + negative - positive)
    loss = K.sum(loss_matrix, axis=-1, keepdims=True)
    return loss


def warp_loss(X):
    z = X[0]
    positive_entity = X[1]
    negative_entities = X[2]
    positiveSim = similarity(z, positive_entity)
    #z_reshaped = Reshape((1, z.shape[1].value))(z)
    z_reshaped = K.expand_dims(z,axis=1)
    negativeSim = similarity(z_reshaped, negative_entities)
    #negativeSim = Reshape((negatives_titles.shape[1].value, 1,))
    negativeSim = K.expand_dims(negativeSim,axis=-1)
    loss = max_margin_loss(positiveSim, negativeSim)
    return loss


def warp_metricsX(X):
    z = X[0]
    positive_entity = X[1]
    negative_entities = X[2]
    positiveSim = similarity(z, positive_entity)
    #z_reshaped = Reshape((1, z.shape[1].value))(z)
    z_reshaped = K.expand_dims(z,axis=1)
    negativeSim = similarity(z_reshaped, negative_entities)
    #Reshape((negatives_titles.shape[1].value, 1,))
    negativeSim = K.expand_dims(negativeSim,axis=-1)

    position = K.sum(K.cast(K.greater(positiveSim, negativeSim), dtype="int32"), axis=1, keepdims=True)
    #accuracy = position / _NUMBER_OF_NEGATIVE_EXAMPLES
    accuracy = position / 30
    return accuracy


def mean_loss(yTrue,yPred):
    return K.mean(warp_loss(yPred))

def warp_metrics(yTrue,yPred):
    return warp_metricsX(yPred)


def build_nn_model():
    #wl, tl = load_vector_lookups()
    #embedded_layer_1 = initialize_embedding_matrix(wl)
    #embedded_layer_2 = initialize_embedding_matrix(tl)
    embedded_layer_1 =  Embedding(200,25)
    embedded_layer_2 =  Embedding(200,25)

    #sequence_input_1 = Input(shape=(_NUMBER_OF_LENGTH,), dtype='int32',name="text")
    sequence_input_1 = Input(shape=(30,), dtype='int32',name="text")
    sequence_input_positive = Input(shape=(1,), dtype='int32', name="positive")
    sequence_input_negatives = Input(shape=(10,), dtype='int32', name="negatives")

    embedded_sequences_1 = embedded_layer_1(sequence_input_1)
    #embedded_sequences_positive = Reshape((tl.shape[1],))(embedded_layer_2(sequence_input_positive))
    embedded_sequences_positive = Reshape((25,))(embedded_layer_2(sequence_input_positive))
    embedded_sequences_negatives = embedded_layer_2(sequence_input_negatives)

    conv_step1 = Convolution1D(
        filters=1000,
        kernel_size=5,
        activation="tanh",
        name="conv_layer_mp",
        padding="valid")(embedded_sequences_1)

    conv_step2 = GlobalMaxPooling1D(name="max_pool_mp")(conv_step1)
    conv_step3 = Activation("tanh")(conv_step2)
    conv_step4 = Dropout(0.2, name="dropout_mp")(conv_step3)
    #z = Dense(wl.shape[1], name="predicted_vec")(conv_step4) # activation="linear"
    z = Dense(25, name="predicted_vec")(conv_step4) # activation="linear"

    model = Model(
            inputs=[sequence_input_1, sequence_input_positive, sequence_input_negatives],
            outputs = [z,embedded_sequences_positive,embedded_sequences_negatives]
        )


    model.compile(loss=mean_loss, optimizer='adam',metrics=[warp_metrics])
    return model

Upvotes: 1

Related Questions