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.
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.