Resolved: "No loss to compute" for model with preprocessing

Hello,
I try to make a model with preprocessing for a categorical classification problem (for classes, using hot_one encoding of target).

I get a “ValueError: No loss to compute. Provide a loss argument in compile()” that I cannot understand nor resolve. Here is a gist with a minimal version of it https://gist.github.com/Bruno-891/6d0c2b38fb671a93538ec02a26d85ff5 .

Please help. Thank you.

Bruno

Here is the code (in case gist is not working;)

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
from pprint import pprint

import tensorflow as tf

BATCH_SIZE = 5

# Define the data
data = {
    'StringFeature': ['apple', 'banana', 'cherry', 'orange', 'mango'],
    'target': [0, 1, 2, 3, 0],
}

# Create the DataFrame
train_df = pd.DataFrame(data)

train_target_df = train_df.pop('target')
train_target_df = pd.get_dummies(train_target_df, columns=['target'])
train_target_df = {key: value.to_numpy()[:,tf.newaxis] for key, value in train_target_df.items()}

train_ds =  tf.data.Dataset.from_tensor_slices((dict(train_df), train_target_df)).batch(BATCH_SIZE)


def get_category_encoding_layer(name, dataset):
    # Create a layer that turns strings into integer indices.
    index = tf.keras.layers.StringLookup()
    
    # Prepare a `tf.data.Dataset` that only yields the feature.
    feature_ds = dataset.map(lambda x, y: x[name])    
    
    # Learn the set of possible values and assign them a fixed integer index.
    index.adapt(feature_ds)
    
    # Encode the integer indices.
    encoder = tf.keras.layers.CategoryEncoding(num_tokens=index.vocabulary_size(), output_mode='one_hot')
    
    # Apply multi-hot encoding to the indices. The lambda function captures the
    # layer, so you can use them, or include them in the Keras Functional model later.
    return lambda feature: encoder(index(feature))

#####
train_all_inputs = {}
train_preprocessed_features = []

##### Categorical features str.
categorical_col = tf.keras.Input(shape=(1,), name='StringFeature', dtype='string')
encoding_layer = get_category_encoding_layer(name='StringFeature', dataset=train_ds)

encoded_categorical_col = encoding_layer(categorical_col)
train_all_inputs['StringFeature'] = categorical_col
train_preprocessed_features.append(encoded_categorical_col)

####
train_all_features = tf.keras.layers.concatenate(train_preprocessed_features)


####
output = tf.keras.layers.Dense(4)(train_all_features)
model = tf.keras.Model(train_all_inputs, output)

model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=['accuracy'])
#model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])


tf.keras.utils.plot_model(model, show_shapes=True, show_layer_names=True, rankdir="LR")
history = model.fit(train_ds, epochs=2)#, validation_data=(validation_features, validation_target))

Bruno

Hi @Bruno, while creating labels I can see you are creating a dictionary structure it is recommended to use array format. with that change I have executed the code in colab I did not face any error. Thank You.

1 Like

Hello,

Thank you. :tada:

Your answer made me find the solution I needed.

  1. Side note: one of the loss I used was “sparse*”, which was not the one to use here, I know use 'categorical_crossentropy'. But that was not the main issue there.

  2. The main issue was indeed the target size/dimensions. I had missed the point where I should have used an array (thanks again on that count, I lost some many hours on this one…). As I wanted the one_hot on the target (hopefully that is not a mistake), I had to change the dimension of the target so I used np.reshape. The full code below (still not familiar enough with gist).

I had to add a dimension, I believe it is due to the one_hot output_mode but not sure yet (probably won’t check if I don’t run into the problem again)

Bruno

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
from pprint import pprint

import tensorflow as tf

BATCH_SIZE = 5

# Define the data
data = {
    'StringFeature': ['apple', 'banana', 'cherry', 'orange', 'mango'],
    'target': [0, 1, 2, 3, 0],
}

# Create the DataFrame
train_df = pd.DataFrame(data)

train_target_df = train_df.pop('target')
train_target_numpy = pd.get_dummies(train_target_df, columns=['target']).to_numpy() #Solution here
arr = train_target_numpy.reshape(5, 1, 4) #Solution and here
#train_target_df = {key: value.to_numpy()[:,tf.newaxis] for key, value in train_target_df.items()}

train_ds =  tf.data.Dataset.from_tensor_slices((dict(train_df), arr)).batch(BATCH_SIZE)


def get_category_encoding_layer(name, dataset):
    # Create a layer that turns strings into integer indices.
    index = tf.keras.layers.StringLookup()
    
    # Prepare a `tf.data.Dataset` that only yields the feature.
    feature_ds = dataset.map(lambda x, y: x[name])    
    
    # Learn the set of possible values and assign them a fixed integer index.
    index.adapt(feature_ds)
    
    # Encode the integer indices.
    encoder = tf.keras.layers.CategoryEncoding(num_tokens=index.vocabulary_size(), output_mode='one_hot')
    
    # Apply multi-hot encoding to the indices. The lambda function captures the
    # layer, so you can use them, or include them in the Keras Functional model later.
    return lambda feature: encoder(index(feature))

#####
train_all_inputs = {}
train_preprocessed_features = []

##### Categorical features str.
categorical_col = tf.keras.Input(shape=(1,), name='StringFeature', dtype='string')
encoding_layer = get_category_encoding_layer(name='StringFeature', dataset=train_ds)

encoded_categorical_col = encoding_layer(categorical_col)
train_all_inputs['StringFeature'] = categorical_col
train_preprocessed_features.append(encoded_categorical_col)

####
train_all_features = tf.keras.layers.concatenate(train_preprocessed_features)


####
output = tf.keras.layers.Dense(4)(train_all_features)
model = tf.keras.Model(train_all_inputs, output)

#model.compile(optimizer='adam', loss=tf.keras.losses.CategoricalCrossentropy(), metrics=['accuracy'])
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])


tf.keras.utils.plot_model(model, show_shapes=True, show_layer_names=True, rankdir="LR")
history = model.fit(train_ds, epochs=2)#, validation_data=(validation_features, validation_target))