I am trying to create a multi-output model in Keras. The model starts with a convolution and aims to stack the results of two separate dense layers. I created some random data for a regression task where x1 is the inputs and df is labels. The df contains three columns. After defining the train and test split and forming the model, I receive an error in fitting the model. Can anyone help me to correct my code?
x1 = np.random.rand(500, 244, 244, 20)
df = pd.DataFrame(np.random.uniform(0,1,size=(500, 3)), columns=list('XYZ'))
x_train, x_test, y_train, y_test = train_test_split(x1,df ,test_size=0.2)
n1_y_train=y_train['X'].values
n1_y_test=y_test['X'].values
n2_y_train=y_train['Y'].values
n2_y_test=y_test['Y'].values
n3_y_train=y_train['Z'].values
n3_y_test=y_test['Z'].values
train_shape = x_train.shape
inputs = layers.Input(shape = train_shape[1:])
x = layers.Conv2D(16, (3,3), activation='relu', padding="same")(inputs)
x = layers.Flatten()(x)
# section1:
l1 = layers.Dense(16, activation='relu')(x)
l1 = layers.Dense(1)(l1)
# section2:
l2 = layers.Dense(32, activation='relu')(x)
l2 = layers.Dense(1)(l2)
output1 = tf.reduce_mean(tf.stack([l1, l2], axis=0), axis=0, name = "output1")
output2 = tf.reduce_mean(tf.stack([l1, l2], axis=0), axis=0, name = "output2")
output3 = tf.reduce_mean(tf.stack([l1, l2], axis=0), axis=0, name = "output3")
model = tf.keras.models.Model(inputs, [output1,output2,output3])
model.compile(
optimizer=tf.keras.optimizers.Adam(),
loss= tf.keras.losses.mse,
metrics=tf.keras.metrics.RootMeanSquaredError(name="rmse"))
history = model.fit(x_train,{"output1": n1_y_train, "output2": n2_y_train, "output3": n3_y_train},
validation_data = (x_test,{"output1": n1_y_test, "output2": n2_y_test, "output3": n3_y_test}),
verbose=2,
epochs=100,
batch_size=32)
error:
ValueError: Found unexpected losses or metrics that do not correspond to any Model output: dict_keys(['output1', 'output2', 'output3']). Valid mode output names: ['tf.math.reduce_mean', 'tf.math.reduce_mean_1', 'tf.math.reduce_mean_2']. Received struct is: {'output1': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=float32>, 'output2': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=float32>, 'output3': <tf.Tensor 'IteratorGetNext:3' shape=(None,) dtype=float32>}.
CodePudding user response:
You should wrap the output layers with Lambda layers and use tf.concat instead of tf.stack. By using Lambda layers, you can explicitly set the names of the outputs, which will be captured by your model. Here is a working example:
import pandas as pd
import numpy as np
import tensorflow as tf
from sklearn.model_selection import train_test_split
x1 = np.random.rand(10, 244, 244, 20)
df = pd.DataFrame(np.random.uniform(0,1,size=(10, 3)), columns=list('XYZ'))
x_train, x_test, y_train, y_test = train_test_split(x1,df ,test_size=0.2)
n1_y_train=y_train['X'].values
n1_y_test=y_test['X'].values
n2_y_train=y_train['Y'].values
n2_y_test=y_test['Y'].values
n3_y_train=y_train['Z'].values
n3_y_test=y_test['Z'].values
train_shape = x_train.shape
inputs = tf.keras.layers.Input(shape = train_shape[1:])
x = tf.keras.layers.Conv2D(16, (3,3), activation='relu', padding="same")(inputs)
x = tf.keras.layers.Flatten()(x)
l1 = tf.keras.layers.Dense(16, activation='relu')(x)
l1 = tf.keras.layers.Dense(1)(l1)
l2 = tf.keras.layers.Dense(32, activation='relu')(x)
l2 = tf.keras.layers.Dense(1)(l2)
output1 = tf.keras.layers.Lambda(lambda x: tf.reduce_mean(x, axis=1, keepdims=True), name='output1')(tf.concat([l1, l2], axis=1))
output2 = tf.keras.layers.Lambda(lambda x: tf.reduce_mean(x, axis=1, keepdims=True), name='output2')(tf.concat([l1, l2], axis=1))
output3 = tf.keras.layers.Lambda(lambda x: tf.reduce_mean(x, axis=1, keepdims=True), name='output3')(tf.concat([l1, l2], axis=1))
model = tf.keras.Model(inputs, [output1,output2,output3])
model.compile(
optimizer=tf.keras.optimizers.Adam(),
loss= tf.keras.losses.mse,
metrics=tf.keras.metrics.RootMeanSquaredError(name="rmse"))
history = model.fit(x_train,{"output1": n1_y_train, "output2": n2_y_train, "output3": n3_y_train},
validation_data = (x_test,{"output1": n1_y_test, "output2": n2_y_test, "output3": n3_y_test}),
verbose=2,
epochs=100,
batch_size=2)
