'Custom Loss Function returning - InvalidArgumentError: The second input must be a scalar, but it has shape [64]
I'm trying to use a modified version of this custom loss and I'm getting the error below
InvalidArgumentError: The second input must be a scalar, but it has shape [64] [[{{node gradient_tape/custom_loss/cond_1/StatelessIf/gradient_tape/custom_loss/weighted_loss/Mul/_30}}]] [Op:__inference_train_function_147002]
Function call stack:
train_function
This is the code
import time
import numpy as np
import tensorflow as tf
from tensorflow.keras.losses import Loss
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.layers import Dense, Dropout, LSTM, BatchNormalization, Flatten
from tensorflow.compat.v1.keras.layers import CuDNNLSTM
from tensorflow.keras.callbacks import TensorBoard, ModelCheckpoint
def custom_loss(y_true, y_pred):
mse = tf.keras.losses.MeanSquaredError()
penalty = 10
# penalize the loss heavily if the actual and the prediction are on different sides of zero
loss = tf.cond( tf.logical_or(
(tf.logical_and(tf.greater(y_true, 0.0), tf.less(y_pred, 0.0))),
(tf.logical_and(tf.less(y_true, 0.0), tf.greater(y_pred, 0.0)))
),
lambda: mse(y_true, y_pred) * penalty,
lambda: mse(y_true, y_pred) * penalty / 4)
print("starting second condition")
# add slightly more penalty if prediction overshoots actual in any direction
loss = tf.cond( tf.logical_or(
(tf.logical_and(tf.greater(y_true, 0.0), tf.greater(y_pred, y_true))),
(tf.logical_and(tf.less(y_true, 0.0), tf.less(y_pred, y_true)))
),
lambda: loss * penalty / 5,
lambda: loss * penalty / 10)
return loss
EPOCHS = 25
BATCH_SIZE = 64
MODEL_NAME = f"MODEL 01-{str(int(time.time())}"
model = Sequential()
model.add(LSTM(128, input_shape=(train_x.shape[1:]), return_sequences=True))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(LSTM(128, input_shape=(train_x.shape[1:]), return_sequences=True))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(LSTM(128, input_shape=(train_x.shape[1:])))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(Flatten())
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(Dense(1))
opt = tf.keras.optimizers.Adam(learning_rate=1e-3, decay=1e-6)
metric= tf.keras.metrics.MeanSquaredError()
model.compile(loss=custom_loss, optimizer=opt, metrics=[metric])
val_metric = 'val_'+metric.name
tensorboard = TensorBoard(log_dir=f'logs/{MODEL_NAME}')
filepath = base_path+"cryptodata/models/RNN_Final-{epoch:02d}-{val_mean_squared_error:.3f}-"+str(int(time.time()))+".hd5"
checkpoint = ModelCheckpoint(filepath=filepath, monitor=val_metric, verbose=0, mode='max',metric=metric)
train_x = np.random.randn(1588, 60, 34)
train_y = np.random.rand(1588,)
val_x = np.random.randn(85, 60, 34)
val_y = np.random.randn(85,)
history = model.fit(train_x, train_y,
batch_size=BATCH_SIZE,
epochs=100,
validation_data=(val_x, val_y),
callbacks=[checkpoint, tensorboard])
I've tried casting the y_true
and y_pred
in the custom loss function like so y_pred=tf.convert_to_tensor(y_pred); y_true = tf.cast(y_true, y_pred.dtype
but that didn't work. Also adding the print function showed that the function was called twice successfully but failed after that.
I don't get the error when I use in-built loss functions.
Solution 1:[1]
The problem is that your custom_loss
is returning a function rather than a scalar value. If you replace tf.cond
with tf.where
your code will work.
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, LSTM, BatchNormalization, Flatten
def custom_loss(y_true, y_pred):
mse = tf.keras.losses.MeanSquaredError()
penalty = 10
# penalize the loss heavily if the actual and the prediction are on different sides of zero
loss = tf.where(
condition=tf.logical_or((tf.logical_and(tf.greater(y_true, 0.0), tf.less(y_pred, 0.0))), (tf.logical_and(tf.less(y_true, 0.0), tf.greater(y_pred, 0.0)))),
x=mse(y_true, y_pred) * penalty,
y=mse(y_true, y_pred) * penalty / 4
)
# add slightly more penalty if prediction overshoots actual in any direction
loss = tf.where(
condition=tf.logical_or((tf.logical_and(tf.greater(y_true, 0.0), tf.greater(y_pred, y_true))), (tf.logical_and(tf.less(y_true, 0.0), tf.less(y_pred, y_true)))),
x=loss * penalty / 5,
y=loss * penalty / 10
)
return loss
train_x = np.random.randn(1588, 60, 34)
train_y = np.random.rand(1588, )
val_x = np.random.randn(85, 60, 34)
val_y = np.random.randn(85, )
model = Sequential()
model.add(LSTM(128, input_shape=(train_x.shape[1:]), return_sequences=True))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(LSTM(128, input_shape=(train_x.shape[1:]), return_sequences=True))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(LSTM(128, input_shape=(train_x.shape[1:])))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(Flatten())
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(Dense(1))
opt = tf.keras.optimizers.Adam(learning_rate=1e-3, decay=1e-6)
model.compile(loss=custom_loss, optimizer=opt, metrics=['mse'])
model.fit(train_x, train_y, batch_size=128,
epochs=3, validation_data=(val_x, val_y))
# Epoch 1/3
# 13/13 [==============================] - 8s 321ms/step - loss: 11.3129 - mse: 1.6341 - val_loss: 6.9313 - val_mse: 1.1116
# Epoch 2/3
# 13/13 [==============================] - 3s 234ms/step - loss: 7.3409 - mse: 1.0789 - val_loss: 7.2055 - val_mse: 1.1238
# Epoch 3/3
# 13/13 [==============================] - 3s 231ms/step - loss: 5.3962 - mse: 0.8513 - val_loss: 7.4492 - val_mse: 1.1512
model.predict(train_x)
# array([[0.25150445],
# [0.2647993 ],
# [0.2405027 ],
# ...,
# [0.31251353],
# [0.29376918],
# [0.21620636]], dtype=float32)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | M.Innat |