DEEP LEARNING LESSON 10 CONVOLUTIONAL NEURAL NETWORKS

Understand the Python Code

In the previous lesson, we built a Convolutional Neural Network using TensorFlow and Keras. Now we will understand the Python code line by line and see exactly how the data moves through the CNN.

The Complete Code

First, look at the complete program without breaking it into pieces.

import tensorflow as tf


# Load MNIST dataset
(x_train, y_train), (x_test, y_test) = (
    tf.keras.datasets.mnist.load_data()
)


# Normalize pixel values
x_train = x_train / 255.0
x_test = x_test / 255.0


# Add channel dimension
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]


# Create CNN
model = tf.keras.Sequential([

    tf.keras.layers.Conv2D(
        32,
        (3, 3),
        activation='relu',
        input_shape=(28, 28, 1)
    ),

    tf.keras.layers.MaxPooling2D(
        (2, 2)
    ),

    tf.keras.layers.Conv2D(
        64,
        (3, 3),
        activation='relu'
    ),

    tf.keras.layers.MaxPooling2D(
        (2, 2)
    ),

    tf.keras.layers.Flatten(),

    tf.keras.layers.Dense(
        128,
        activation='relu'
    ),

    tf.keras.layers.Dense(
        10,
        activation='softmax'
    )
])


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


# Train
history = model.fit(
    x_train,
    y_train,
    epochs=5,
    batch_size=64,
    validation_split=0.1
)


# Evaluate
test_loss, test_accuracy = model.evaluate(
    x_test,
    y_test
)

print("Test Accuracy:", test_accuracy)


# Make predictions
predictions = model.predict(x_test[:5])

predicted_classes = tf.argmax(
    predictions,
    axis=1
)

print("Predictions:")
print(predicted_classes.numpy())

print("Actual Labels:")
print(y_test[:5])

First Understand the Big Picture

Do not try to memorize the code. Understand the job of each section first.

1. Import TensorFlow
        ↓
2. Load images
        ↓
3. Normalize images
        ↓
4. Prepare image shape
        ↓
5. Build CNN
        ↓
6. Compile CNN
        ↓
7. Train CNN
        ↓
8. Evaluate CNN
        ↓
9. Make predictions

Think of this as a pipeline. Each step prepares something for the next step.

1. Import TensorFlow

import tensorflow as tf

This imports TensorFlow into our Python program.

The as tf part gives TensorFlow a short name. Instead of writing tensorflow every time, we can write tf.

tensorflow.keras

becomes:

tf.keras

Simple example:

import tensorflow as tf

print(tf.__version__)

This prints the installed TensorFlow version.

2. Load the MNIST Dataset

(x_train, y_train), (x_test, y_test) = (
    tf.keras.datasets.mnist.load_data()
)

This line loads the MNIST dataset and separates it into training and testing data.

x_train
→ Training images

y_train
→ Training labels

x_test
→ Testing images

y_test
→ Testing labels

Think about one training example:

x_train[0]
→ Image of a digit

y_train[0]
→ Correct answer for that image

For example, if:

y_train[0] = 7

then the corresponding image x_train[0] represents the digit 7.

Training Data vs Test Data

Training Data

x_train → Images
y_train → Correct answers

        ↓

Used to teach the CNN


Test Data

x_test → Images
y_test → Correct answers

        ↓

Used to check the CNN

The important point is that the test images are not used to update the model's weights during training.

3. Normalize the Pixel Values

x_train = x_train / 255.0
x_test = x_test / 255.0

A grayscale image has pixel values from 0 to 255.

0
→ Black

255
→ White

Dividing by 255 converts the values to approximately 0 through 1.

Original:

0
128
255


After dividing by 255:

0
0.502
1

This makes the numerical input easier for the neural network to work with.

4. Add the Channel Dimension

x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]

Before this step, an MNIST image has the shape:

28 × 28

A Conv2D layer expects the channel dimension as well. For a grayscale image there is one channel.

28 × 28 × 1

28 → height
28 → width
1  → grayscale channel

The expression tf.newaxis adds this extra dimension.

So this:

(28, 28)

becomes:

(28, 28, 1)

5. Create the CNN

model = tf.keras.Sequential([
    ...
])

Sequential means the layers are connected one after another in a simple sequence.

Input
 ↓
Layer 1
 ↓
Layer 2
 ↓
Layer 3
 ↓
Layer 4
 ↓
...
 ↓
Output

In our CNN, the output of one layer becomes the input to the next layer.

6. First Conv2D Layer

tf.keras.layers.Conv2D(
    32,
    (3, 3),
    activation='relu',
    input_shape=(28, 28, 1)
)

Let's break this into four parts.

32
→ Number of filters

(3, 3)
→ Filter/kernel size

relu
→ Activation function

(28, 28, 1)
→ Input image shape

The CNN starts looking for useful visual patterns in the image.

Image
 ↓
32 Filters
 ↓
32 Feature Maps

We do not manually tell the filters what to detect. Their weights are learned during training.

What Does ReLU Do?

ReLU stands for Rectified Linear Unit.

ReLU(x) = max(0, x)

In simple terms:

-5 → 0
-2 → 0
 0 → 0
 3 → 3
 8 → 8

Negative values become 0, while positive values remain.

7. MaxPooling2D

tf.keras.layers.MaxPooling2D(
    (2, 2)
)

This uses a 2 × 2 region and keeps the largest value.

1   4
2   3

 ↓

4

It reduces the spatial size of the feature maps while retaining strong activations.

Before
Large Feature Map

        ↓

Max Pooling

        ↓

Smaller Feature Map

8. Second Conv2D Layer

tf.keras.layers.Conv2D(
    64,
    (3, 3),
    activation='relu'
)

This layer contains 64 filters.

The important idea is that this layer does not start directly from the original image. It receives the features produced by the previous layers.

Original Image
      ↓
First Conv
      ↓
Basic Features
      ↓
Second Conv
      ↓
More Useful Features

Deeper layers can build representations from patterns detected by earlier layers.

9. Second Max Pooling Layer

tf.keras.layers.MaxPooling2D(
    (2, 2)
)

This performs another reduction of the feature-map dimensions.

Conv2D
 ↓
Feature Maps
 ↓
MaxPooling
 ↓
Smaller Feature Maps

10. Flatten

tf.keras.layers.Flatten()

Convolutional layers work with spatial feature maps. Dense layers expect a vector.

Flatten converts the multi-dimensional data into one long vector.

Feature Maps
      ↓
Flatten
      ↓
[0.2, 0.8, 0.1, 0.7, ...]

Think of Flatten as changing the format of the data. It does not itself decide which digit the image is.

11. Dense Layer

tf.keras.layers.Dense(
    128,
    activation='relu'
)

This creates a fully connected layer containing 128 neurons.

Flattened Features
        ↓
128 Neurons
        ↓
Combine Information
        ↓
Prepare for Classification

The dense layer uses the features extracted by the CNN to help make the final classification.

12. Output Layer

tf.keras.layers.Dense(
    10,
    activation='softmax'
)

There are 10 output neurons because MNIST contains 10 classes.

Neuron 0 → Digit 0
Neuron 1 → Digit 1
Neuron 2 → Digit 2
...
Neuron 9 → Digit 9

Softmax converts the outputs into probabilities.

Example:

0 → 0.01
1 → 0.01
2 → 0.02
3 → 0.01
4 → 0.01
5 → 0.02
6 → 0.01
7 → 0.89
8 → 0.01
9 → 0.01

The highest probability is for digit 7, so the prediction is 7.

13. Compile the Model

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

This tells Keras how the model should be trained.

optimizer
→ Decides how weights are updated

loss
→ Measures prediction error

metrics
→ Measures performance

We use Adam as the optimizer.

We use sparse categorical cross-entropy because our labels are integer values such as:

0
1
2
3
...
9

Accuracy tells us the percentage of predictions that are correct.

14. Train the Model

history = model.fit(
    x_train,
    y_train,
    epochs=5,
    batch_size=64,
    validation_split=0.1
)

This is where the actual learning happens.

x_train
→ Images

y_train
→ Correct answers

epochs=5
→ Train for 5 passes through the training data

batch_size=64
→ Process 64 examples at a time

validation_split=0.1
→ Use 10% of the training data for validation

During training, the model repeatedly performs:

Prediction
    ↓
Loss
    ↓
Gradients
    ↓
Weight Updates
    ↓
Better Prediction

What Is history?

history = model.fit(...)

model.fit() returns a History object containing information collected during training.

For example, training loss and accuracy can be accessed from:

history.history['loss']

history.history['accuracy']

history.history['val_loss']

history.history['val_accuracy']

This is useful when we want to inspect how training changed over the epochs.

15. Evaluate the Model

test_loss, test_accuracy = model.evaluate(
    x_test,
    y_test
)

This runs the trained model on the test dataset.

x_test
→ Images the model has not trained on

y_test
→ Correct answers

model.evaluate()
→ Measures performance

The results are stored in:

test_loss
→ Test error

test_accuracy
→ Test accuracy

16. Print Test Accuracy

print("Test Accuracy:", test_accuracy)

This simply displays the test accuracy.

Example output:

Test Accuracy: 0.98

This means the model correctly classified approximately 98% of the test examples.

Do not interpret 0.98 as "98 correct images." It is a proportion, so it corresponds to about 98% accuracy.

17. Make Predictions

predictions = model.predict(
    x_test[:5]
)

x_test[:5] selects the first five test images.

x_test[:5]

means:

image 0
image 1
image 2
image 3
image 4

The model returns probabilities for each of the 10 classes.

5 images
   ×
10 class probabilities

        ↓

5 × 10 prediction array

18. Find the Predicted Class

predicted_classes = tf.argmax(
    predictions,
    axis=1
)

predictions contains probabilities. We need to find the class with the highest probability.

Suppose the model produces:

[
    0.01,
    0.02,
    0.03,
    0.01,
    0.01,
    0.02,
    0.01,
    0.86,
    0.02,
    0.01
]

The highest value is:

0.86

Its position is:

7

Therefore:

Prediction = 7

axis=1 tells TensorFlow to find the maximum class index separately for each image.

19. Convert Predictions to NumPy

print(predicted_classes.numpy())

TensorFlow tensors have a .numpy() method that converts the result to a NumPy representation in normal eager execution.

For example:

[7 2 1 0 4]

These are the model's predicted classes for the five selected images.

20. Print the Actual Labels

print(y_test[:5])

This prints the correct answers for those same five images.

Predictions:
[7 2 1 0 4]

Actual Labels:
[7 2 1 0 4]

If the two arrays match, those predictions are correct.

Understand the Complete Data Flow

This is the most important part of the lesson. Follow one image through the entire CNN.

Handwritten Digit
       │
       ▼
28 × 28 Image
       │
       ▼
Normalize
0–255 → 0–1
       │
       ▼
Add Channel
28 × 28 → 28 × 28 × 1
       │
       ▼
Conv2D
32 Filters
       │
       ▼
Feature Maps
       │
       ▼
MaxPooling
       │
       ▼
Smaller Feature Maps
       │
       ▼
Conv2D
64 Filters
       │
       ▼
More Feature Maps
       │
       ▼
MaxPooling
       │
       ▼
Flatten
       │
       ▼
One Long Vector
       │
       ▼
Dense
128 Neurons
       │
       ▼
Dense
10 Neurons
       │
       ▼
Softmax
       │
       ▼
Probabilities
       │
       ▼
Highest Probability
       │
       ▼
Prediction

Understand What Happens During Training

Building the model and training the model are different things.

model = tf.keras.Sequential([...])

This creates the architecture. It does not mean the CNN has learned to recognize digits yet.

Learning happens when we call:

model.fit(...)

During training:

Training Image
      ↓
Forward Pass
      ↓
Prediction
      ↓
Calculate Loss
      ↓
Backpropagation
      ↓
Calculate Gradients
      ↓
Adam Updates Weights
      ↓
Next Batch

This cycle repeats many times.

Two Simple Examples

Example 1 — Wrong Prediction

Actual Digit:
7

Model Prediction:
3

        ↓

Prediction is wrong

        ↓

Loss increases

        ↓

Backpropagation calculates
how the weights contributed
to the error

        ↓

Optimizer updates weights

Example 2 — Correct Prediction

Actual Digit:
7

Model Prediction:
7

        ↓

Prediction is correct

        ↓

Loss should generally be lower

        ↓

The model keeps learning
from the training example

Three Things You Should Not Confuse

Conv2D
→ Extracts learned visual features


Dense
→ Combines features for classification


Softmax
→ Converts final scores into class probabilities

Another common mistake is thinking that model.predict() trains the model. It does not.

model.fit()
→ Training

model.evaluate()
→ Evaluation

model.predict()
→ Prediction

Easy Way to Remember the Code

IMPORT
→ Get TensorFlow

LOAD
→ Get dataset

NORMALIZE
→ Scale pixels

RESHAPE
→ Prepare image shape

BUILD
→ Create CNN layers

COMPILE
→ Choose optimizer and loss

FIT
→ Train the model

EVALUATE
→ Test the model

PREDICT
→ Make predictions

The whole program can therefore be remembered as:

Load
 ↓
Prepare
 ↓
Build
 ↓
Compile
 ↓
Train
 ↓
Evaluate
 ↓
Predict

Final Understanding

You do not need to memorize every TensorFlow function. What matters first is understanding the responsibility of each part.

TensorFlow
    ↓
Provides deep-learning tools

Keras
    ↓
Makes model building easier

Conv2D
    ↓
Learns image features

Pooling
    ↓
Reduces feature-map size

Flatten
    ↓
Converts features into a vector

Dense
    ↓
Uses features for classification

Softmax
    ↓
Produces class probabilities

Loss
    ↓
Measures error

Optimizer
    ↓
Updates weights

fit()
    ↓
Trains the CNN

evaluate()
    ↓
Checks performance

predict()
    ↓
Makes predictions

Once you understand this flow, the Python code becomes much easier to read. You are no longer seeing a list of TensorFlow functions; you are seeing the individual stages of a neural network.

QUICK CHECK

Check Your Understanding

Why do we divide the images by 255?
To scale pixel values from 0–255 to approximately 0–1.

Why do we add a channel dimension?
Conv2D expects the image dimensions to include the number of channels.

What does Conv2D do?
It applies learnable filters to extract useful spatial patterns from the image.

What does Flatten do?
It converts the multi-dimensional feature maps into one long vector for the dense layers.

Why are there 10 output neurons?
Because MNIST has 10 classes, from 0 through 9.

What does model.fit() do?
It trains the model by repeatedly performing forward passes, calculating loss, computing gradients, and updating weights.

What is the difference between evaluate() and predict()?
evaluate() measures model performance using labeled data, while predict() produces predictions for input data.