0
votes

I am using TensorFlow 2.0 with Python 3.7.5 to build a neural network for Iris classification using Model sub-classing approach.

The code I have is as follows:

import tensorflow as tf
from tensorflow.keras import Sequential, Model
from tensorflow.keras.layers import Dense, Input
import pandas as pd
import numpy as np


# Read in data-
data = pd.read_csv("iris.csv")

# Get data types for different attributes-
data.dtypes
'''
sepallength    float64
sepalwidth     float64
petallength    float64
petalwidth     float64
class           object
dtype: object
'''


# Get shape of data-
data.shape
# (150, 5)


# Check for missing values-
data.isnull().values.any()
# False

# Perform label encoding for target variable-

# Initialize a label encoder-
le = LabelEncoder()

# Label encode target attribute-
data['class'] = le.fit_transform(data['class'])

# Get different classes which are label encoded-
le.classes_
# array(['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'], dtype=object)

# Split data into features (X) and target (y)-
X = data.drop('class', axis = 1)
y = data['class']


# Get training & testing sets using features and labels-
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# Convert from Pandas to numpy arrays-
X_train = X_train.to_numpy()
X_test = X_test.to_numpy()

y_train = y_train.to_numpy()
y_test = y_test.to_numpy()

print("\nTraining and Testing set dimensions:")
print("X_train.shape = {0}, y_train.shape = {1}".format(X_train.shape, y_train.shape))
print("X_test.shape = {0}, y_test.shape = {1}\n".format(X_test.shape, y_test.shape))
# Training and Testing set dimensions:
# X_train.shape = (105, 4), y_train.shape = (105,)
# X_test.shape = (45, 4), y_test.shape = (45,)



class IrisClassifier(Model):

    def __init__(self):
        super(IrisClassifier, self).__init__()

        '''
        self.layer1 = Dense(
            units = 4, activation = 'relu',
            kernel_initializer = tf.keras.initializers.GlorotNormal()
            )
        '''

        self.input_layer = Input(
            shape = (4,)
            )

        self.layer1 = Dense(
            units = 10, activation = 'relu',
            input_dim = 4,
            kernel_initializer = tf.keras.initializers.GlorotNormal()
            )

        self.layer2 = Dense(
            units = 10, activation = 'relu',
            kernel_initializer = tf.keras.initializers.GlorotNormal()
            )

        self.outputlayer = Dense(
            units = 3, activation = 'softmax'
            )


    def call(self, x):
        x = self.input_layer(x)
        x = self.layer1(x)
        x = self.layer2(x)
        # x = self.layer3(x)

        return self.outputlayer(x)


# Instantiate a model of defined neural network class-
model = IrisClassifier()

# Define EarlyStopping callback-
callback = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3)

# Compile defined model-
model.compile(
    optimizer=tf.keras.optimizers.Adam(lr = 0.001),
    loss = 'sparse_categorical_crossentropy',
    metrics = ['accuracy']
     )


# Train model-
history2 = model.fit(
    x = X_train, y = y_train,
    validation_data = [X_test, y_test],
    epochs = 50, batch_size = 16,
    callbacks = [callback]
    )

When I execute 'history2' code, I get the following error:

--------------------------------------------------------------------------- ValueError Traceback (most recent call last) in 3 validation_data = [X_test, y_test], 4 epochs = 50, batch_size = 16, ----> 5 callbacks = [callback] 6 )

~/.local/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_freq, max_queue_size, workers, use_multiprocessing, **kwargs) 726 max_queue_size=max_queue_size, 727 workers=workers, --> 728 use_multiprocessing=use_multiprocessing) 729 730 def evaluate(self,

~/.local/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training_arrays.py in fit(self, model, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_freq, **kwargs) 640 steps=steps_per_epoch, 641 validation_split=validation_split, --> 642 shuffle=shuffle) 643 644 if validation_data:

~/.local/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py in _standardize_user_data(self, x, y, sample_weight, class_weight, batch_size, check_steps, steps_name, steps, validation_split, shuffle, extract_tensors_from_dataset) 2417 # First, we build the model on the fly if necessary. 2418 if not self.inputs: -> 2419 all_inputs, y_input, dict_inputs = self._build_model_with_inputs(x, y) 2420 is_build_called = True 2421 else:

~/.local/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py in _build_model_with_inputs(self, inputs, targets) 2580 # or lists of arrays, and extract a flat list of inputs from the passed
2581 # structure. -> 2582 training_utils.validate_input_types(inputs, orig_inputs) 2583 2584 if isinstance(inputs, (list, tuple)):

~/.local/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training_utils.py in validate_input_types(inp, orig_inp, allow_dict, field_name) 1149 raise ValueError( 1150 'Please provide as model inputs either a single array or a list of ' -> 1151 'arrays. You passed: {}={}'.format(field_name, orig_inp)) 1152 1153

ValueError: Please provide as model inputs either a single array or a list of arrays. You passed: inputs= sepallength sepalwidth petallength petalwidth 117 7.7 3.8 6.7
2.2 7 5.0 3.4 1.5 0.2 73 6.1 2.8 4.7 1.2 92 5.8 2.6 4.0 1.2 87 6.3 2.3 4.4 1.3 .. ... ... ... ... 93 5.0 2.3 3.3 1.0 30 4.8 3.1 1.6 0.2 25 5.0 3.0 1.6 0.2 31 5.4 3.4 1.5 0.4 97 6.2 2.9 4.3 1.3

[105 rows x 4 columns]

After converting X_train, y_train, X_test and y_test to numpy arrays, when I execute, history2 to train the model, I get the following error:

TypeError: in converted code:

<ipython-input-14-ae6111e00410>:34 call  *
    x = self.input_layer(x)
/home/arjun/.local/lib/python3.7/site-packages/tensorflow_core/python/autograph/impl/api.py:427

converted_call f in m.dict.values() for m in (collections, pdb, copy, inspect, re)): /home/arjun/.local/lib/python3.7/site-packages/tensorflow_core/python/autograph/impl/api.py:427 f in m.dict.values() for m in (collections, pdb, copy, inspect, re)): /home/arjun/.local/lib/python3.7/site-packages/tensorflow_core/python/ops/math_ops.py:1336 tensor_equals return gen_math_ops.equal(self, other) /home/arjun/.local/lib/python3.7/site-packages/tensorflow_core/python/ops/gen_math_ops.py:3627 equal name=name) /home/arjun/.local/lib/python3.7/site-packages/tensorflow_core/python/framework/op_def_library.py:536 _apply_op_helper repr(values), type(values).name, err))

TypeError: Expected float32 passed to parameter 'y' of op 'Equal', got 'collections' of type 'str' instead. Error: Expected float32, got

'collections' of type 'str' instead.

What's going wrong?

Thanks!

2

2 Answers

1
votes

Your problem stems from the way you preprocess your data, before you fit it to your model.

It is highly likely that you pass the entire csv-dataset from iris, including your column headers, hence your issue. You can verify this from

"You passed: inputs= sepallength sepalwidth petallength petalwidth 117 7.7 3.8 6.7".

Ensure that your Xs and ys do not contain the column names, but only the values. Use X_train = X_train.to_numpy() to ensure the conversion works. In older versions, you could also use X_train.values, but the latter has been deprecated.

0
votes

I solved the problem. According to Francois Chollet:

A subclassed model is a piece of Python code (a call method). There is no graph of layers here. We cannot know how layers are connected to each other (because that's defined in the body of call, not as an explicit data structure), so we cannot infer input / output shapes

Therefore, the following code runs fine (where you don't specify the input training data shape):

# Define EarlyStopping callback-
callback = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3)

class IrisClassifier(Model):

    def __init__(self):
        super(IrisClassifier, self).__init__()

        self.layer1 = Dense(
            units = 10, activation = 'relu',
            # input_dim = 4,
            kernel_initializer = tf.keras.initializers.GlorotNormal()
            )

        self.layer2 = Dense(
            units = 10, activation = 'relu',
            kernel_initializer = tf.keras.initializers.GlorotNormal()
            )

        self.outputlayer = Dense(
            units = 3, activation = 'softmax'
            )


    def call(self, x):
        # x = self.input_layer(x)
        x = self.layer1(x)
        x = self.layer2(x)
        # x = self.layer3(x)
        return self.outputlayer(x)


# Instantiate a model of defined neural network class-
model2 = IrisClassifier()


# Compile defined model-
model2.compile(
    optimizer=tf.keras.optimizers.Adam(lr = 0.001),
    loss = 'sparse_categorical_crossentropy',
    metrics = ['accuracy']
     )


# Train model-
history2 = model2.fit(
    x = X_train, y = y_train,
    validation_data = [X_test, y_test],
    epochs = 50, batch_size = 16,
    callbacks = [callback]
    )

Thanks!