© David Paper 2021
D. PaperTensorFlow 2.x in the Colaboratory Cloudhttps://doi.org/10.1007/978-1-4842-6649-6_5

5. Classification

David Paper1  
(1)
Logan, UT, USA
 

Classification is a supervised learning method for predicting a class label for a given example of input data. Although we introduced classification with MNIST, we work through the famous Fashion-MNIST dataset to delve deeper into the topic.

Fashion-MNIST is intended as a direct replacement for MNIST to better benchmark machine learning algorithms. It shares the same image size and structure of training and test splits, but is a more challenging classification problem.

MNIST benchmarking has several associated problems. It’s far too easy for standard machine learning algorithms to achieve over 97% accuracy. It’s even easier for deep learning models to achieve over 99% accuracy. The dataset is overused. Finally, MNIST cannot represent modern computer vision tasks.

Notebooks for chapters are located at the following URL: https://github.com/paperd/tensorflow.

Enable the GPU (if not already enabled):
  1. 1.

    Click Runtime in the top-left menu.

     
  2. 2.

    Click Change runtime type from the drop-down menu.

     
  3. 3.

    Choose GPU from the Hardware accelerator drop-down menu.

     
  4. 4.

    Click SAVE.

     
Test if GPU is active:
import tensorflow as tf
# display tf version and test if GPU is active
tf.__version__, tf.test.gpu_device_name()

Import the tensorflow library. If ‘/device:GPU:0’ is displayed, the GPU is active. If ‘..’ is displayed, the regular CPU is active.

Fashion-MNIST Dataset

Fashion-MNIST is a dataset of clothing article images created by Zalando Research consisting of a training set of 60,000 examples and a test set of 10,000 examples. Each example is a 28 × 28 grayscale image associated with a label from ten classes.

Zalando Research is an organization that utilizes an agile design process that combines invaluable human experience with the power of machine learning. Zalando concentrates on exploring novel ways to use generative models in fashion design for rapid visualizations and prototyping.

Load Fashion-MNIST as a TFDS

Since Fashion-MNIST is a tfds.data.Dataset, we can easily load it with tfds.load. To get a list of all TFDS, just run the tfds.list_builders method as demonstrated in Chapter 3.

Load train and test examples as a tf.data.Dataset:
import tensorflow_datasets as tfds
train, info = tfds.load('fashion_mnist', split="train",
                        with_info=True, shuffle_files=True)
test = tfds.load('fashion_mnist', split="test")

Since we already have info from the train data, we don’t need to load it again for the test data.

Verify train and test data:
train.element_spec, test.element_spec

Each image consists of 28 × 28 pixels. The 1 dimension indicates that images are grayscale. Each label is a scalar.

Explore the Dataset

Display information about the dataset:
info

We see the name, description, and homepage. We also see the shape and datatype of feature images and labels. We see that we have 70,000 examples with train and test splits of 60,000 and 10,000, respectively. A lot of other information is also included.

Extract the number of classes and class labels:
br = ' '
num_classes = info.features['label'].num_classes
class_labels = info.features['label'].names
print ('number of classes:', num_classes, br)
print ('class labels:', class_labels)

We have ten classes representing ten clothing articles.

Let’s see some training examples:
fig = tfds.show_examples(train, info)

The show_examples method displays sample images and labels. The label name and associated class number is displayed under each image. For example, the class number for Pullover is 2 because it is the third label in the class labels list.

Listing 5-1 is a custom function that displays samples from the train dataset.
import matplotlib.pyplot as plt, numpy as np
def display_samples(data, num, cmap):
  for example in data.take(num):
    image, label = example['image'], example['label']
    print ('Label:', class_labels[label.numpy()], end=', ')
    print ('Index:', label.numpy())
    plt.imshow(image.numpy()[:, :, 0].astype(np.float32),
               cmap=plt.get_cmap(cmap))
    plt.show()
Listing 5-1

Custom function for displaying sample data

Import a couple of libraries. The function accepts a dataset, number of samples to display, and a colormap. A colormap is an array of colors used to map pixel data to the actual color values. The matplotlib library provides a variety of built-in colormaps.

We assign an example image and label to variables. We then display the label name and its associated class number. We end by displaying the image with the imshow() function. We use [:, :, 0] to grab all pixels from each image.

Invoke the function to display a couple of training images:
# choose colormap by changing 'indx'
indx = 5
cmap = ['coolwarm', 'viridis', 'plasma',
        'seismic', 'copper', 'twilight']
samples = 2
display_samples(train, samples, cmap[indx])

Now, let’s build a custom function to display a grid of samples.

Before we create the function, take 30 samples from the train set as shown in Listing 5-2.
num = 30
images, labels = [], []
for example in train.take(num):
  image, label = example['image'], example['label']
  images.append(tf.squeeze(image.numpy()))
  labels.append(tf.squeeze(label.numpy()))
Listing 5-2

Take samples from the train set

Create the function as shown in Listing 5-3.
def display_grid(feature, target, n_rows, n_cols, cl):
  plt.figure(figsize=(n_cols * 1.5, n_rows * 1.5))
  for row in range(n_rows):
    for col in range(n_cols):
      index = n_cols * row + col
      plt.subplot(n_rows, n_cols, index + 1)
      plt.imshow(feature[index], cmap="binary",
                 interpolation='nearest')
      plt.axis('off')
      plt.title(cl[target[index]], fontsize=12)
  plt.subplots_adjust(wspace=0.2, hspace=0.5)
Listing 5-3

Function that displays a grid of examples

Invoke the function:
rows = 5
cols = 6
display_grid(images, labels, rows, cols, class_labels)
We can also pinpoint metadata with DatasetInfo:
print ('Number of training examples:', end=' ')
print (info.splits['train'].num_examples)
print ('Number of test examples:', end=' ')
print (info.splits['test'].num_examples)

Build the Input Pipeline

Build the input pipeline for train and test data as shown in Listing 5-4.
BATCH_SIZE = 128
SHUFFLE_SIZE = 5000
train_f1 = train.shuffle(SHUFFLE_SIZE).batch(BATCH_SIZE)
train_f2 = train_f1.map(lambda items: (
    tf.cast(items['image'], tf.float32) / 255., items['label']))
train_fs = train_f2.cache().prefetch(1)
test_f1 = test.batch(BATCH_SIZE)
test_f2 = test_f1.map(lambda items: (
    tf.cast(items['image'], tf.float32) / 255., items['label']))
test_fs = test_f2.cache().prefetch(1)
Listing 5-4

Build the input pipeline

Shuffle and batch train data. Scale train images. Cache and prefetch train images. Batch test data. Scale test images. Cache and prefetch test images. Use batch size of 128 and shuffle buffer size of 5,000 for this experiment.

Note

Do not shuffle test data because it is considered new to the neural network model.

Caching a TFDS can significantly improve performance. The cache method of a tf.data.Dataset can cache a dataset either in memory or on local storage, which saves operations like file opening and data reading from being executed during each epoch.

Adding prefetch is a good idea because it adds efficiency to the batching process. While our training algorithm is working on one batch, TensorFlow is working on the dataset in parallel to get the next batch ready. So prefetch can dramatically improve training performance.

For more information on TFDS performance improvement, peruse
Verify train and test tensors:
train_fs.element_spec, test_fs.element_spec

Both train and test images are 28 × 28 × 1. The 1 value means that images are grayscale. That is, images are in black and white.

Build the Model

Let’s build a simple feedforward neural net as shown in Listing 5-5.
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Dropout
# clear previous model and generate a seed
tf.keras.backend.clear_session()
np.random.seed(0)
tf.random.set_seed(0)
model = Sequential([
  Flatten(input_shape=[28, 28, 1]),
  Dense(512, activation="relu"),
  Dropout(0.4),
  Dense(10, activation="softmax")
])
Listing 5-5

Simple feedforward neural network

Import requisite libraries. Clear any previous model sessions and generate a seed to facilitate reproducibility of results. The first layer flattens images. The second layer uses relu activation on 512 neurons to process the data. The third layer uses dropout to reduce overfitting. The fourth layer uses softmax activation on ten neurons to account for class labels.

Model Summary

Display a summary of the model:
model.summary()

Output shape of the first layer is (None, 784). We get 784 by multiplying 28 by 28. We have no parameters at this layer because it is only used to bring data into the model.

Output shape of the second layer is (None, 512) because we have 512 neurons at this layer. We get parameters of 401,920 by multiplying 512 neurons at this layer by 784 neurons from the previous layer and adding 512 at this layer.

Output shape of the third layer is (None, 512), and parameters are 0 because dropout doesn’t impact neurons or parameters. Output shape of the fourth layer is (None, 10) because we have ten neurons at this layer to deal with ten output classes. We get parameters of 5,130 by multiplying 10 neurons at this layer by 512 from the previous layer and adding 10 neurons at this layer.

Note

None is used because TensorFlow models can accept any batch size.

Compile the Model

Define a gradient descent optimizer that tweaks model parameters to minimize the loss function:
model.compile(optimizer='adam',
 loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

Train the Model

Train the model with ten epochs:
epochs = 10
history = model.fit(train_fs, epochs=epochs,
                    verbose=1, validation_data=test_fs)

We get pretty good accuracy with not much overfitting.

Generalize on Test Data

Although training provides accuracy and loss metrics, it is always a good idea to explicitly evaluate the model on test data to see how well the model generalizes on new data:
print('Test accuracy:', end=' ')
test_loss, test_acc = model.evaluate(test_fs, verbose=2)

Visualize Performance

The fit method automatically records the history of the training process as a dictionary. So we can assign training information to a variable. In this case, we assign it to history. The history attribute of the variable contains the dictionary information.

Display the dictionary keys to inform us on how to plot the results:
hist_dict = history.history
print (hist_dict, ' ')
print (hist_dict.keys())

The dictionary history.history contains loss, accuracy, val_loss, and val_accuracy metrics that the model measures at the end of each epoch on the training set and validation (or test) set.

The params variable provides all parameters involved with training:
history.params
Plot training performance as shown in Listing 5-6.
plt.plot(history.history['accuracy'], label="accuracy")
plt.plot(history.history['val_accuracy'], label = 'val_accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.ylim([0.5, 1])
plt.legend(loc='lower right')
plt.show()
plt.plot(history.history['loss'], label="loss")
plt.plot(history.history['val_loss'], label = 'val_loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.ylim([0.05, .7])
plt.legend(loc='lower right')
plt.show()
Listing 5-6

Plot training performance

We don’t have much overfitting, and our model accuracy is pretty good for such a simple neural network.

We can also use pandas to plot training performance:
import pandas as pd
pd.DataFrame(history.history).plot(figsize=(8, 5))
plt.grid(True)
plt.gca().set_ylim(0, 1)

Predict Labels for Test Images

Now that we have a trained model, we can make predictions based on test images. We predict from test images because the model sees these images as new data.

Begin by predicting the label for each image in the test set:
tf.random.set_seed(0)
predictions = model.predict(test_fs)

We use the predict method on the processed test set test_fs.

Since Fashion-MNIST has ten class labels, each prediction consists of an array of ten numbers that represent the model’s confidence in how well the image corresponds to each of the ten different articles of clothing.

Let’s take a look at the first prediction:
predictions[0]

The first prediction is at index 0 because Python indexes range from 0 to 9999 for test set size of 10,000. It’s hard to tell which of the values has the highest number by looking at the array of float number values.

Round the numbers in the prediction array to make it easier to see the position in the array with the highest confidence:
np.round(predictions[0], 2)

Now, we can clearly see the value with the highest number. The position with the highest confidence corresponds to the position in the class labels array.

We can directly derive the confidence in the prediction with the following code:
c = 100*np.max(predictions[0])
c
Display the confidence:
'{:.2%}'.format(np.max(predictions[0]))
Display the prediction for the first image in the test set:
np.argmax(predictions[0])

The prediction must be between 0 and 9 because our targets are between 0 and 9.

Use the class_labels array we created earlier to see the actual fashion article:
class_labels[np.argmax(predictions[0])]

So we have the predicted clothing article for the first image in the test set.

Display the first actual test image:
# take the first batch of images
for image, label in test_fs.take(1):
  label
class_labels[label[0].numpy()]

We take the first image from a batch of 128 images because we set batch size to 128. If the prediction matches the test image, it was a correct prediction.

Build a Prediction Plot

Now that we have a trained model, we can build a prediction plot.

Since we want to see how well the model performs on new data, take 30 samples from the test set as shown in Listing 5-7.
num = 30
images, labels = [], []
for example in test.take(num):
  image, label = example['image'], example['label']
  images.append(tf.squeeze(image.numpy()))
  labels.append(tf.squeeze(label.numpy()))
Listing 5-7

Take samples from the test set

Squeeze each image to remove the 1 dimension for plotting purposes.

Build a plotting function as shown in Listing 5-8.
def display_test(feature, target, num_images,
                 n_rows, n_cols, cl, p):
  for i in range(num_images):
    plt.subplot(n_rows, 2*n_cols, 2*i+1)
    if cl[target[i]] != cl[np.argmax(p[i])]:
      plt.imshow(feature[i], cmap="Reds")
    else:
      plt.imshow(feature[i], cmap="Blues")
    val = 100*np.max(p[i])
    rounded = str(np.round(val, 2)) + '%'
    plt.title(cl[target[i]] + ' (' +
 cl[np.argmax(p[i])] + ') ' +
              rounded )
  plt.tight_layout()
plt.show()
Listing 5-8

Function to build a prediction plot

Invoke the function as shown in Listing 5-9.
num_rows, num_cols = 6, 5
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
display_test(images, labels, num_images, num_rows,
             num_cols, class_labels, predictions)
Listing 5-9

Invoke the prediction plot function

Any clothing article in red means that the prediction was incorrect. Above each article of clothing is the actual label, prediction in parentheses, and confidence in the prediction.

Load Fashion-MNIST as a Keras Dataset

Although TFDS are recommended for TensorFlow 2.x application, we show you how to work with a Keras dataset because of its popularity in industry. The newness of TensorFlow 2.x means that it doesn’t yet have the industry penetration of Keras.

Load the Keras dataset:
train, test = tf.keras.datasets.fashion_mnist.load_data()

Explore the Data

Let’s look at the data shapes:
print ('train data:', br)
print (train[0].shape)
print (train[1].shape, br)
print ('test data:', br)
print (test[0].shape)
print (test[1].shape)

The Keras dataset contains the same data as the Fashion-MNIST TFDS. Train data consists of 60,000 28 × 28 feature images and 60,000 labels. Test data consists of 10,000 28 × 28 feature images and 10,000 labels. Train images are contained in the train tuple. So train[0] represents images and train[1] represents labels.

Let’s see what the first image represents:
class_labels[train[1][0]]

Since we access training labels with train[1], we grab the first label with train[1][0].

Visualize the First Image

Plot the first image with Matplotlib’s imshow function:
plt.imshow(train[0][0], cmap="binary")
plt.axis('off')
Assign images and labels to variables for convenience:
train_images = train[0]
train_labels = train[1]
Plot the first image based on the train_images variable:
plt.imshow(train_images[0], cmap="binary")
plt.axis('off')
Access the label name of the first image:
class_labels[train_labels[0]]
Labels are the class IDs ranging from 0 to 9:
train_labels

Visualize Sample Images

Since we can’t use the TFDS show_examples method, we create code as shown in Listing 5-10.
n_rows = 5
n_cols = 6
plt.figure(figsize=(n_cols * 1.5, n_rows * 1.5))
for row in range(n_rows):
  for col in range(n_cols):
    index = n_cols * row + col
    plt.subplot(n_rows, n_cols, index + 1)
    plt.imshow(train_images[index], cmap="binary",
               interpolation='nearest')
    plt.axis('off')
    plt.title(class_labels[train_labels[index]], fontsize=12)
plt.subplots_adjust(wspace=0.2, hspace=0.5)
Listing 5-10

Code to visualize examples

Prepare Data for Training

To prepare training data for TensorFlow consumption, we need to grab images and labels from train and test data. We scale images to ensure that each input parameter (a pixel, in our case) has a similar data distribution. The distribution of such data resembles a Gaussian curve centered at zero. Scaling data makes convergence faster while training the network.

As we know, pixel data is represented by a range from 0 to 255. So we divide each feature image by 255 to scale it. Once images are scaled, we convert images and labels to a tf.Tensor object with from_tensor_slices as shown in Listing 5-11.
# add test images and labels to the mix
test_images, test_labels = test
train_pictures = train_images / 255.  # divide by 255 to scale
train_targets = train_labels.astype(np.int32)
test_pictures = test_images / 255.  # divide by 255 to scale
test_targets = test_labels.astype(np.int32)
print ('train images:', len(train_pictures))
print ('train labels:', len(train_targets), br)
print ('test images', len(test_pictures))
print ('test labels', len(test_targets))
train_ds = tf.data.Dataset.from_tensor_slices(
    (train_pictures, train_targets))
test_ds = tf.data.Dataset.from_tensor_slices(
    (test_pictures, test_targets))
Listing 5-11

Prepare the input pipeline

Display the tensors:
train_ds.element_spec, test_ds.element_spec
Finish the input pipeline by shuffling, batching, and prefetching train data and batching and prefetching test data:
BATCH_SIZE = 128
SHUFFLE_BUFFER_SIZE = 5000
train_ks = train_ds.shuffle(
    SHUFFLE_BUFFER_SIZE).batch(BATCH_SIZE).prefetch(1)
test_ks = test_ds.batch(BATCH_SIZE).prefetch(1)

Set BATCH_SIZE to 128 so the model will run faster than with a smaller batch size. Experiment with this number and see what happens. We also set SHUFFLE_BUFFER_SIZE to 5000 so the shuffle method works well. Again, experiment with this number to see what happens.

Display the finalized input pipeline tensors:
train_ks.element_spec, test_ks.element_spec

Build the Model

Create the same model that we did for the TFDS as shown in Listing 5-12.
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Dropout
# clear previous model and generate a seed
tf.keras.backend.clear_session()
np.random.seed(0)
tf.random.set_seed(0)
model = Sequential([
  Flatten(input_shape=[28, 28]),
  Dense(512, activation="relu"),
  Dropout(0.4),
  Dense(10, activation="softmax")
])
Listing 5-12

Keras model

As we already know, it is always a good idea to clear any previous modeling sessions. Remember that we already created a model and trained it earlier in the chapter. Also, generating a seed ensures that the results are consistent. In machine learning, such consistency is called reproducibility. That is, the seed provides a starting point for the random generator that allows us to reproduce results in a consistent manner. You can use any integer value for the random seed number. We use 0.

The first layer, Flatten(), flattens the 2D matrix of 28 × 28 images to a 1D array of 784 pixels. The second layer is the first true layer. It accepts input images into 128 neurons and performs relu activation. The final layer is the output layer. It accepts output from the input layer with ten neurons that represent the ten classes of clothing articles and performs softmax activation.

Model Summary

Inspect the model:
model.summary()

Compile the Model

Compile:
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

Train the Model

Fit the model. Passing a dataset of (feature, label) pairs is all that’s needed for Model.fit and Model.evaluate:
history = model.fit(train_ks, epochs=10, validation_data=test_ks)

Generalize on Test Data

Although model fit information provides validation loss and accuracy during training, it is always a good idea to explicitly evaluate the model on test data because accuracy and loss values may differ:
print('Test accuracy:', end=' ')
test_loss, test_acc = model.evaluate(test_ks, verbose=2)

Visualize Training

The fit method automatically records the history of the training process as a dictionary. So we can assign training history to a variable. In this case, we assigned it to history. The history attribute of the variable contains the dictionary information.

Display the dictionary keys to inform us on how to plot results:
hist_dict = history.history
print (hist_dict, ' ')
print (hist_dict.keys())

Dictionary history.history contains loss and other metrics the model measures at the end of each epoch on the training set and validation set.

The params variable provides all parameters involved with the model:
history.params
Listing 5-13 plots training history.
plt.plot(history.history['accuracy'], label="accuracy")
plt.plot(history.history['val_accuracy'], label = 'val_accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.ylim([0.5, 1])
plt.legend(loc='lower right')
plt.show()
plt.plot(history.history['loss'], label="loss")
plt.plot(history.history['val_loss'], label = 'val_loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.ylim([0.05, .7])
plt.legend(loc='lower right')
plt.show()
Listing 5-13

Training history plots

Overfitting is minimal because training accuracy is closely aligned with test accuracy. We did employ dropout in the model to reduce overfitting. Dropout is a regularization method that approximates training a large number of neural networks with different architectures in parallel. The definition doesn’t make it easier to understand for anyone new to deep learning. So let’s explain how it works.

During training, some number of layer outputs are randomly ignored or dropped out. By randomly dropping out layer outputs, effectively the layer looks like and is treated like a layer with a different number of nodes and connectivity to the prior layer. So each update to a layer during training is performed with a different view of the configured layer. Dropout is a simple but very effective technique to reduce overfitting.

Setting dropout at 0.4 means that we randomly drop out 40% of the layer outputs. You can easily experiment with dropout by changing this value. However, you should keep dropout rate at or less than 0.5 because otherwise you are removing too much data!

Tip

Experiment with dropout levels, but keep it at or less than 0.5 to avoid removing too much data.

If the model is overfitting (train accuracy is greater than test accuracy) with the dropout value you set, you can increase it. If the model is underfitting (train accuracy is less than test accuracy), you can decrease it.

Use pandas to plot:
import pandas as pd
pd.DataFrame(history.history).plot(figsize=(8, 5))
plt.grid(True)
plt.gca().set_ylim(0, 1)

Predict Labels for Test Images

We can make predictions on test images since we have a trained model.

Predict the label for each image from test set test_ks:
predictions = model.predict(test_ks)

As with the Fashion-MNIST TFDS, a prediction is an array of ten numbers that represent the model’s confidence that the image corresponds to each of the ten articles of clothing.

Predict the First Image

Let’s look at the first prediction:
predictions[0]
Round the array values for convenience:
np.round(predictions[0], 2)
Directly access the confidence in the prediction:
100*np.max(predictions[0])
Convert the value to a percentage:
str(np.round(100*np.max(predictions[0]), 2)) + '%'
Display the prediction for the first image:
np.argmax(predictions[0])

The prediction can only be between 0 and 9 because our targets are between 0 and 9.

Use the class_labels array created earlier to see the actual fashion article:
class_labels[np.argmax(predictions[0])]
Display the first actual test label for comparison:
class_labels[test_labels[0]]

If the prediction and actual images match, the prediction is correct.

Predict Four Images

Make four predictions from the test set:
pred_4 = predictions[:4]
Show the predictions:
ls = [np.argmax(row) for row in pred_4]
ls
Get class labels:
np.array(class_labels)[ls]
Compare predictions to the test dataset:
actual_4 = test_labels[:4]
actual_4
Show actuals as class labels:
np.array(class_labels)[actual_4]
Visualize as shown in Listing 5-14.
# slice off the first four images from the test data
img_4 = test_images[:4]
# plot images
plt.figure(figsize=(7.2, 2.4))
for index, image in enumerate(img_4):
  plt.subplot(1, 4, index + 1)
  plt.imshow(image, cmap="twilight", interpolation="nearest")
  plt.axis('off')
  plt.title(class_labels[actual_4[index]], fontsize=12)
plt.subplots_adjust(wspace=0.2, hspace=0.5)
Listing 5-14

Visualize the first four actual test images

Explore Misclassifications

Let’s explore predictions and actual test image labels to find some misclassifications.

The first step is to identify prediction labels:
rng = (len(predictions))
y_pred = [np.argmax(row) for row in predictions]

The y_pred variable holds a list of prediction labels.

Next, grab some predictions and actual targets:
# find first n predictions and actual targets
n = 20
y_n = [y_pred[i] for i, row in enumerate(range(n))]
y_actual = [test_labels[i] for i, row in enumerate(range(n))]
y_n, y_actual
Find misclassifications:
# compare predictions against actual targets
miss_indx_list = [index for index, (x, y) in
                  enumerate(zip(y_n, y_actual))
                  if x != y]
miss_indx_list

If you don’t get any misclassifications, increase the size of n and rerun the last two code snippets.

We now have an array of misclassifications by their index.

Listing 5-15 displays the confidence for misclassifications.
# display confidence for each misclassification:
for row in miss_indx_list:
  val = 100*np.max(predictions[row])
  rounded = str(round(val, 2)) + '%'
  print ('index:', row, 'confidence:', rounded,
         'pred:', class_labels[np.argmax(predictions[row])],
         'actual:', class_labels[test_labels[row]])
Listing 5-15

Confidence in misclassifications

Visualize Misclassifications

Create a function to visualize misclassifications as shown in Listing 5-16.
def see_misses(indx):
  plt.imshow(test_images[indx], cmap="nipy_spectral")
  plt.show()
  print ('actual:', class_labels[test_labels[indx]])
  print ('predicted:',
 class_labels[np.argmax(predictions[indx])])
  print ('confidence', rounded)
Listing 5-16

Function to visualize misclassifications

Invoke the function:
for row in miss_indx_list:
  val = 100*np.max(predictions[row])
  rounded = str(round(val, 2)) + '%'
  see_misses(row)

The confidence may still be pretty high even though the prediction was incorrect. Although neural networks tend to perform well for prediction, their estimated predicted probabilities by the output of a softmax layer tend to be too high. That is, they are too confident in their predictions. Remember that the probability with the highest value is the predicted class from the prediction array. And this probability is the confidence. However, the prediction is not necessarily correct unless the model has 100% accuracy.

Create a more sophisticated visualization as shown in Listing 5-17.
# Plot the first X test images, their true labels,
# their predicted labels, and prediction confidence.
num_rows = 8
num_cols = 8
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  if class_labels[test_labels[i]] != class_labels[y_pred[i]]:
    plt.imshow(test_images[i], cmap="Reds")
  else:
    plt.imshow(test_images[i], cmap="Blues")
  val = 100*np.max(predictions[i])
  rounded = str(np.round(val, 2)) + '%'
  plt.title(class_labels[test_labels[i]] + ' (' +
            class_labels[y_pred[i]] + ') ' + rounded )
plt.tight_layout()
plt.show()
Listing 5-17

Sophisticated visualization of misclassifications

Misclassifications, if any, are in red. Above each article of clothing is the actual label, prediction in parentheses, and confidence in the prediction.

Predict from a Single Image

We can make a prediction on a single image. We choose a number between 0 and 9,999 because we want an image from the test set. Alternatively, we can generate a random number between 0 and 9,999.

Randomly choose an image from the test set:
beg, end = 0, len(test_images) - 1
rng = np.random.default_rng()
indx = int(rng.uniform(beg, end, size=1))
indx

Displayed is the index of the randomly chosen image.

Grab the image from the test set:
# Grab the image from the test dataset
img = test_images[indx]
label = class_labels[test_labels[indx]]
img.shape, label

TensorFlow models are optimized to make predictions on a batch or collection of examples at once. So add the single image to a batch where it is the only member.

Create a batch with a single image:
img_batch = (np.expand_dims(img, 0))
img_batch.shape

The expand_dims method inserts a new axis that appears at the axis position in the expanded array shape. So the new shape is (1, 28, 28).

Now, we can predict:
pred_single = model.predict(img_batch)
Display prediction:
np.argmax(pred_single)

The index position in the prediction array is displayed.

To make it easier, display prediction by label name:
class_labels[np.argmax(pred_single)]
Display the actual label:
class_labels[test_labels[indx]]

Visualize Single Image Prediction

Visualize the prediction as shown in Listing 5-18.
pred = class_labels[np.argmax(pred_single)]
actual = class_labels[test_labels[indx]]
# get confidence from the predictions object
val = 100*np.max(predictions[indx])
rounded = str(np.round(val, 2)) + '%'
# display actual image
plt.imshow(test_images[indx], cmap=plt.cm.binary)
plt.show()
print ('actual:', actual)
print ('predicted:', pred)
print ('confidence', rounded)
Listing 5-18

Visualization of single image prediction

Get prediction by label name and actual label name for the image. Get prediction confidence from the predictions object that we created earlier. The predictions object holds all predictions based on the test set. The indx value provides the position of the confidence value in the predictions object.

Confusion Matrix

Create a confusion matrix to visually demonstrate how well the model classified articles of clothing from Fashion-MNIST:
tf.math.confusion_matrix(test_labels, y_pred)

To understand the TensorFlow confusion matrix, imagine that above the first row of the matrix are classes 0–9 from left to right. These represent predictions. Imagine that to the left of the first column of the matrix are classes 0–9 from top to bottom. These represent actual labels. Correct classifications are along the diagonal. Misclassifications are not on the diagonal.

The first set of correct classifications is in position row one, column one and represents class 0. The second set is in position row two, column two and represents class 1. And so on…

An example misclassification set is row one, column two. This represents the number of predictions for class 1 that were misclassified as class 0. For a technical explanation of the TensorFlow confusion matrix, peruse the following URL:

https://stackoverflow.com/questions/46616837/understanding-a-tensorflow-confusion-matrix-for-binary-classification

For a general explanation of a confusion matrix, peruse the following URL:

www.dataschool.io/simple-guide-to-confusion-matrix-terminology/

Number of Hidden Layers

For many problems, we can begin with a single hidden layer and get reasonable results as we did with our Fashion-MNIST model in this chapter. For more complex problems, we should add layers until we start overfitting the training set.

Number of Neurons in Hidden Layers

The number of neurons in the input and output layers is based on the type of input and output your task requires. For example, the Fashion-MNIST task requires 28 × 28 = 784 input neurons and ten output neurons. For hidden layers, it is very difficult to determine. You can try increasing the number of neurons gradually until the network starts overfitting. In practice, we typically pick a model with more layers and neurons than we need and then use early stopping and other regularization techniques to reduce overfitting. Since this is an introduction, we won’t delve any deeper into tuning networks.

Generally, we get a better model by increasing the number of layers rather than the number of neurons per layer. Of course, the number of layers and neurons we include is limited by our available computational resources.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.103.16