Menu

Building Your First Neural Network

Building Your First Neural Network

It is time to build a complete, working neural network. You will train a model to classify handwritten digits using the MNIST dataset, arguably the most famous benchmark in deep learning. By the end of this section, you will have a model that achieves over 97% accuracy on data it has never seen.

Preparing the Dataset (MNIST)

MNIST contains 70,000 grayscale images of handwritten digits (0–9), each 28 x 28 pixels: 60,000 for training and 10,000 for testing. TensorFlow includes it as a built-in dataset, so loading it requires just one line. The official TensorFlow MNIST Keras example demonstrates the following preparation steps:

import tensorflow as tf

import numpy as np

# Load the dataset

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

print(x_train.shape)   # (60000, 28, 28) 60k training images

print(x_test.shape) # (10000, 28, 28) 10k test images

print(y_train[:5])  # [5 0 4 1 9]  digit labels

print(x_train.dtype)   # uint8  pixel values 0 to 255

# Normalise: scale pixel values from 0–255 to 0.0–1.0

# Neural networks train faster and more stably when inputs are

# on a consistent, small scale close to the initial weight magnitudes

x_train = x_train.astype('float32') / 255.0

x_test  = x_test.astype('float32')  / 255.0

# Flatten: reshape 28×28 images into 784-element vectors

# Dense (fully-connected) layers expect 1-D inputs

x_train = x_train.reshape(-1, 784)   # (60000, 784)

x_test  = x_test.reshape(-1, 784) # (10000, 784)

print(x_train.shape)   # (60000, 784)

print(x_train.min(), x_train.max())  # 0.0  1.0

Did You Know?

MNIST was created by Yann LeCun, Corinna Cortes, and Christopher Burges in 1998. It was originally used to train and evaluate convolutional networks for optical character recognition on postal envelopes. Today, it serves as a quick sanity check if your model cannot learn MNIST, something is wrong with your setup or code.

Building the Model with tf.keras

With the data ready, define the neural network using Keras's Sequential API. Each call to model.add() (or each layer in the list) stacks one layer on top of the previous one, forming a feed-forward network:
import tensorflow as tf

model = tf.keras.Sequential([

# Input shape: each sample is a 784-element vector

tf.keras.layers.InputLayer(input_shape=(784,)),

# Hidden layer 1: 128 neurons

# ReLU (Rectified Linear Unit) = max(0, x)

# Introduces non-linearity, lets the network learn complex patterns

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

# Dropout: randomly zeros 20% of neurons during training

# Forces the network to learn redundant representations

# This is one of the most effective regularisation techniques

tf.keras.layers.Dropout(0.2),

# Hidden layer 2: 64 neurons

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

# Output layer: 10 neurons (one per digit class)

# Softmax converts raw scores (logits) into probabilities

# Output values are non-negative and sum to 1.0

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

])

# Print architecture summary: layers, shapes, parameter counts

model.summary()

# Total params: ~109,386  (all trainable)

Training and Evaluating the Model

Compiling sets the learning algorithm and loss function. Fitting runs the training loop. Both are single-function calls with Keras:

# Compile

model. compile(

# Adam: adaptive learning rate — currently the most popular

# optimiser for classification tasks

optimizer='adam',

# sparse_categorical_crossentropy: the correct choice when

# labels are integers (0, 1, 2 …), not one-hot vectors

loss='sparse_categorical_crossentropy',

metrics=['accuracy']

)

# Train

history = model.fit(

x_train, y_train,

epochs=10,          # 10 full passes through the training set

batch_size=64,      # process 64 samples per gradient update

validation_split=0.1,  # hold out 10% of train data for validation

verbose=1

)

# Evaluate on unseen test data

test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)

print(f'Test loss:  {test_loss:.4f}')

print(f'Test accuracy: {test_acc:.4f}')  # Typically ~0.9760+

# Make predictions

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

predicted_classes = predictions.argmax(axis=1)

print('Predicted:', predicted_classes)

print('Actual:   ', y_test[:5])

Expected Result

After 10 epochs, you should see a test accuracy above 97%. If it is significantly lower, verify two things: (1) pixel values are divided by 255.0, and (2) you are using sparse_categorical_crossentropy rather than categorical_crossentropy; the latter expects one-hot encoded labels, which y_train is not.