I have a problem which deals with predicting two outputs when given a vector of predictors. Assume that a predictor vector looks like x1, y1, att1, att2, ..., attn, which says x1, y1 are coordinates and att's are the other attributes attached to the occurrence of x1, y1 coordinates. Based on this predictor set I want to predict x2, y2. This is a time series problem, which I am trying to solve using multiple regresssion. My question is how do I setup keras, which can give me 2 outputs in the final layer.

from keras.models import Model
from keras.layers import *    

#inp is a "tensor", that can be passed when calling other layers to produce an output 
inp = Input((10,)) #supposing you have ten numeric values as input 

#here, SomeLayer() is defining a layer, 
#and calling it with (inp) produces the output tensor x
x = SomeLayer(blablabla)(inp) 
x = SomeOtherLayer(blablabla)(x) #here, I just replace x, because this intermediate output is not interesting to keep

#here, I want to keep the two different outputs for defining the model
#notice that both left and right are called with the same input x, creating a fork
out1 = LeftSideLastLayer(balbalba)(x)    
out2 = RightSideLastLayer(banblabala)(x)

#here, you define which path you will follow in the graph you've drawn with layers
#notice the two outputs passed in a list, telling the model I want it to have two outputs.
model = Model(inp, [out1,out2])
model.compile(optimizer = ...., loss = ....) #loss can be one for both sides or a list with different loss functions for out1 and out2    

model.fit(inputData,[outputYLeft, outputYRight], epochs=..., batch_size=...)
  • So if I understand you correctly then what you mean is: InputShape = (10, ) model_1 = Sequential() model_1.add(Dense(250, activation='tanh', input_shape=(InputShape))) model_1.add(Dense(2, activation='relu')) model_1.compile(optimizer='adam', loss='mse', metrics=['accuracy']) model_1.fit(predictors, targets, epochs=whatever, ....) . My question is how is this different than yours, where you are specifying two outputs exclusively. – Neelabh Pant May 19 '17 at 16:49
  • 5
    Added comments to my answer :) -- You cannot create branches with a sequential model, it's simply not possible. – Daniel Möller May 19 '17 at 17:10
  • @Daniel Hi Daniel, could you expand on that? What I'm looking for is to have a network that attempts to predict two different things and so I was picturing a branch happening at my penultimate layer which feeds into two different softmax layers, I then concatenate the results of those two layers and then backpropogate with respect to that. Is this not possible in keras? – tryingtolearn Jun 30 '17 at 15:35
  • If you know the true values for both sides, you don't need to concatenate them. The model will do everything automatically. (The only reasons I can think of to concatenate both branches are: 1 - your true data is already concatenated; 2 - you want to add further layers taking that as input). – Daniel Möller Jun 30 '17 at 17:12

You can make a model with multiple output with

  1. the Functional API

  2. by subclassing tf.keras.Model.

Here's an example of dual inputs (regression and classification) on the Iris Dataset, using the Functional API:

from sklearn.datasets import load_iris
from tensorflow.keras.layers import Dense
from tensorflow.keras import Input, Model
import tensorflow as tf

data, target = load_iris(return_X_y=True)
X = data[:, (0, 1, 2)]
Y = data[:, 3]
Z = target

inputs = Input(shape=(3,), name='input')
x = Dense(16, activation='relu', name='16')(inputs)
x = Dense(32, activation='relu', name='32')(x)
output1 = Dense(1, name='cont_out')(x)
output2 = Dense(3, activation='softmax', name='cat_out')(x)

model = Model(inputs=inputs, outputs=[output1, output2])

model.compile(loss={'cont_out': 'mean_absolute_error', 
                    'cat_out': 'sparse_categorical_crossentropy'},
              metrics={'cat_out': tf.metrics.SparseCategoricalAccuracy(name='acc')})

history = model.fit(X, {'cont_out': Y, 'cat_out': Z}, epochs=10, batch_size=8)

Here's a simplified version:

from sklearn.datasets import load_iris
from tensorflow.keras.layers import Dense
from tensorflow.keras import Input, Model

data, target = load_iris(return_X_y=True)
Y = data[:, 3]
Z = target

inputs = Input(shape=(3,))
x = Dense(16, activation='relu')(inputs)
x = Dense(32, activation='relu')(x)
output1 = Dense(1)(x)
output2 = Dense(3, activation='softmax')(x)


model.compile(loss=['mae', 'sparse_categorical_crossentropy'], optimizer='adam')

history = model.fit(X, [Y, Z], epochs=10, batch_size=8)

Here's the same example, subclassing tf.keras.Model and with a custom training loop:

import tensorflow as tf
from tensorflow.keras.layers import Dense
from tensorflow.keras import Model
from sklearn.datasets import load_iris
iris, target = load_iris(return_X_y=True)

X = iris[:, :3]
y = iris[:, 3]
z = target

ds = tf.data.Dataset.from_tensor_slices((X, y, z)).shuffle(150).batch(8)

class MyModel(Model):
    def __init__(self):
        super(MyModel, self).__init__()
        self.d0 = Dense(16, activation='relu')
        self.d1 = Dense(32, activation='relu')
        self.d2 = Dense(1)
        self.d3 = Dense(3, activation='softmax')

    def call(self, x, training=None, **kwargs):
        x = self.d0(x)
        x = self.d1(x)
        a = self.d2(x)
        b = self.d3(x)
        return a, b

model = MyModel()

loss_obj_reg = tf.keras.losses.MeanAbsoluteError()
loss_obj_cat = tf.keras.losses.SparseCategoricalCrossentropy()

optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)

loss_reg = tf.keras.metrics.Mean(name='regression loss')
loss_cat = tf.keras.metrics.Mean(name='categorical loss')

error_reg = tf.keras.metrics.MeanAbsoluteError()
error_cat = tf.keras.metrics.SparseCategoricalAccuracy()

def train_step(inputs, y_reg, y_cat):
    with tf.GradientTape() as tape:
        pred_reg, pred_cat = model(inputs)
        reg_loss = loss_obj_reg(y_reg, pred_reg)
        cat_loss = loss_obj_cat(y_cat, pred_cat)

    gradients = tape.gradient([reg_loss, cat_loss], model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

    error_reg(y_reg, pred_reg)
    error_cat(y_cat, pred_cat)

for epoch in range(50):
    for xx, yy, zz in ds:
        train_step(xx, yy, zz)

    template = 'Epoch {:>2}, SCCE: {:>5.2f},' \
               ' MAE: {:>4.2f}, SAcc: {:>5.1%}'


  • I am just curious what would be the benefit of having dual inputs? Would it be better to have two separate models (classification and regression)? Thx.. – devon Jan 3 at 17:06
  • The benefit is that the neural net can learn a structure in the data that is useful for both tasks. So less parameters – Nicolas Gervais Jan 3 at 17:14
  • I guess I am just curious to see how this would perform compared to two individual tasks. I'll try it out. Thanks for the code. – devon Jan 3 at 18:18
  • How does the network account for the fact that mean absolute error may be much smaller than the cross entropy, particularly if the output is normalized to 0-1 range (MAE <1, entropy>1)? – JafetGado Apr 24 at 23:04

Your Answer

