1

I have a dataset containing a matrix of features X and a matrix of labels y of size N where each element y_i belongs to [0,1]. I have the following loss function

where g(.) is a function that depends on the input matrix X. I know that Keras custom loss function has to be of the form customLoss(y_true,y_predicted), however, I'm having difficulties incorporating the term g(X) in the loss function since this depends on the input matrix.

For each data point in my dataset, my input is of the form X_i = (H, P) where these two parameters are matrices and the function g is defined for each data point as g(X_i) = H x P. Can I pass a = (H, P) in the loss function since this depends on each example or do I need to pass all the matrices at once by concatenating them?

Edit (based on Daniel's answer):

original_model_inputs = keras.layers.Input(shape=X_train.shape[1])
y_true_inputs = keras.layers.Input(shape=y_train.shape[1])
hidden1 = keras.layers.Dense(256, activation="relu")(original_model_inputs)
hidden2 = keras.layers.Dense(128, activation="relu")(hidden1)
output = keras.layers.Dense(K)(hidden2)

def lambdaLoss(x):
    yTrue, yPred, alpha = x
    return (K.log(yTrue) - K.log(yPred))**2+alpha*yPred

loss = Lambda(lambdaLoss)(y_true_inputs, output, a)

model = Keras.Model(inputs=[original_model_inputs, y_true_inputs], outputs=[output], loss)

def dummyLoss(true, pred):
    return pred

model.compile(loss = dummyLoss, optimizer=Adam())

train_model = model.fit([X_train, y_train], None, batch_size = 32, 
      epochs = 50, 
      validation_data = ([X_valid, y_valid], None), 
      callbacks=callbacks)
Jeremy
  • 35
  • 5
  • 2
    Can you be specific about the difficulties you are facing ? – ravikt Nov 01 '19 at 09:23
  • @ravikt: I've updated my post regarding your question. Thanks. – Jeremy Nov 01 '19 at 10:27
  • I have updated the answer – ravikt Nov 01 '19 at 10:52
  • Answer here, where `a` will be `g`: https://stackoverflow.com/questions/58566096/custom-loss-function-that-updates-at-each-step-via-gradient-descent/58647668#58647668 – Daniel Möller Nov 01 '19 at 12:44
  • @DanielMöller: In the answer you refer it me to, you wrote `model.fit([x_train, y_train], anything_maybe_None_or_np_zeros ,....)`. Now, I understand that `[x_train, y_train]` are considered as the inputs of the model but should I put `y_train` instead of `anything_maybe_None_or_np_zeros`. I don't actually understand why you're saying we can put `anything_maybe_None_or_np_zeros`. Is it possible to explain this point? Thanks! – Jeremy Nov 13 '19 at 08:56
  • Follow the answer in the link. The output of the model is the loss and you are using a dummy loss function that simply disconsiders the truth outputs. – Daniel Möller Nov 13 '19 at 12:52
  • @DanielMöller: sorry as I'm not very familiar with how Keras works, as I happen to start to learn it. That is why I was asking if you could clarify to me what you meant in the other link by `anything_maybe_None_or_np_zeros`. So, if I put `np.zeros` for instance that will make things work properly? – Jeremy Nov 13 '19 at 14:23
  • Yes, provided you follow the instructions in the linked answer. (It will work only because your loss function in that answer simply ignores whatever you put there, maybe you can even put None, if Keras doesn't complain) – Daniel Möller Nov 13 '19 at 14:24
  • @DanielMöller: as a side question, does the function `lambdaLoss` computes the loss per example or for all examples at once? i.e. what is the size of `yTrue`, and `yPred` in this case? – Jeremy Nov 25 '19 at 08:03
  • @DanielMöller: I've updated the original question. Could let me know if my understanding for your answer is correct and also I included a question in this post, I would be grateful if you could help answering this. Thanks! – Jeremy Nov 25 '19 at 08:57

2 Answers2

1

Fixing the understanding of my answer:

original_model_inputs = keras.layers.Input(shape=X_train.shape[1:]) #must be a tuple, not an int
y_true_inputs = keras.layers.Input(shape=y_train.shape[1:]) #must be a tuple, not an int

hidden1 = keras.layers.Dense(256, activation="relu")(original_model_inputs)
hidden2 = keras.layers.Dense(128, activation="relu")(hidden1)
output = keras.layers.Dense(K)(hidden2)

You need something to do g(X), I have no idea of what it is, but you need to do it somewhere. And yes, you need to pass the whole tensor at once, you cannot make x_i and everything else.

def g(x):
    return something

gResults = Lambda(g)(original_model_inputs)

Continuing my answer:

def lambdaLoss(x):
    yTrue, yPred, G = x

    .... #wait.... where is Y_true in your loss formula?

loss = Lambda(lambdaLoss)([y_true_inputs, output, gResults]) #must be a list of inputs including G

You need a model for training and another to get the outputs, because we're doing a frankenstein model because of the different loss.

training_model = keras.Model(inputs=[original_model_inputs, y_true_inputs], outputs=loss)
prediction_model = keras.Model(original_model_inputs, output) 

Only the training model must be compiled:

def dummyLoss(true, pred):
    return pred

training_model.compile(loss = dummyLoss, optimizer=Adam())

training_model = model.fit([X_train, y_train], None, batch_size = 32, 
      epochs = 50, 
      validation_data = ([X_valid, y_valid], None), 
      callbacks=callbacks)

Use the other model to get result data:

results = prediction_model.predict(some_x)
Daniel Möller
  • 84,878
  • 18
  • 192
  • 214
0

Looks like a GAN of some sort. I will refer to (x) as "x_input", Two methods:

Method 1) Inherit from tf.keras.model class and write your own (not recommended, not shown)

Method 2) Inherit from tf.keras.losses.Loss class. and return tuple of (custom) tf.keras.losses.Loss instance and tf.keras.layers.Layer that does nothing more than act as shell to grab and save a copy of the x_input (x). This layer instance can then be added as the top layer in model. The (custom) tf.keraslosses. Loss instance can then access the input on demand. This method also has best future support throughout the life of Tensorflow.

First, create a custom layer and custom loss class:

class Acrylic_Layer(tf.keras.layers.Layer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.x_input = None
        
    def build(self, *args, **kwargs):
        pass

    def call(self, input):
        self.x_input = input
        return input # Pass input directly through to next layer

class Custom_Loss(tf.keras.losses.Loss):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.input_thief = Acrylic_Layer() # <<< Magic, python is pass by reference!

    def __call__(self, y_true, y_pred, sample_weight=None):
        x_input = self.input_thief.x_input # <<< x_input pulled from model

Second, add layer and loss function to model

loss_fn = Custom_Loss(*args, **kwargs)
input_thief = loss_fn.input_thief

model = tf.keras.models.Sequential([
    input_thief, # <<< transparent layer
    Other_layers,
])

model.fit(loss=loss_fn) # <<< loss function

Lastly, I'm the market looking for a ML/python role, giving a shout out.