Dataset Preparation
The scikit-learn library provides a built-in dataset of handwritten digits that serves as an ideal starting point for image classification tasks.
from sklearn.datasets import load_digits
import numpy as np
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split
# Load the digit recognition dataset
dataset = load_digits()
pixel_data = dataset.data.astype(np.float32)
labels = dataset.target.astype(np.float32).reshape(-1, 1)
Data Preprocessing Pipeline
Before feeding data into neural networks, several preprocessing steps are essential including normalization, label encoding, and dataset partitioning.
# Normalize pixel values to range [0,1]
normalizer = MinMaxScaler()
normalized_pixels = normalizer.fit_transform(pixel_data)
print('Normalized data shape:', normalized_pixels.shape)
# Apply one-hot encoding to labels
encoder = OneHotEncoder()
encoded_labels = encoder.fit_transform(labels).toarray()
print('Encoded labels shape:', encoded_labels.shape)
# Reshape data into image format: (samples, height, width, channels)
image_data = normalized_pixels.reshape(-1, 8, 8, 1)
# Split dataset into training and testing subsets
X_train, X_test, Y_train, Y_test = train_test_split(
image_data, encoded_labels, test_size=0.2, random_state=42, stratify=encoded_labels
)
print('Training set shapes:', X_train.shape, Y_train.shape)
print('Testing set shapes:', X_test.shape, Y_test.shape)
CNN Architecture Design
The network architecture is carefully designed considering the small input size and limited feature complexity:
- Limited depth: Only three convolutional layers due to small image dimensions
- Compact kernels: 3x3 filters suitable for 8x8 pixel images
- Feature maps progression: 16 → 32 → 64 → 128 channels across layers
- Regularization: Batch normalization instead of dropout to preserve features
- Activation functions: ReLU to mitigate gradient vanishing issues
- Output layer: Softmax for multi-class probability distribution
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
# Initialize sequential model
network = Sequential()
# First convolutional block
network.add(Conv2D(16, kernel_size=(3, 3), activation='relu', input_shape=(8, 8, 1)))
network.add(MaxPooling2D(pool_size=(2, 2)))
network.add(Dropout(0.25))
# Second convolutional block
network.add(Conv2D(32, kernel_size=(3, 3), activation='relu'))
network.add(MaxPooling2D(pool_size=(2, 2)))
network.add(Dropout(0.25))
# Third convolutional block
network.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
network.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
network.add(MaxPooling2D(pool_size=(2, 2)))
network.add(Dropout(0.25))
# Fully connected layers
network.add(Flatten())
network.add(Dense(128, activation='relu'))
network.add(Dropout(0.25))
network.add(Dense(10, activation='softmax'))
print(network.summary())
Model Training Process
The training configuration uses Adam optimizer with categorical crossentropy loss for multi-class classification.
# Compile model with appropriate loss function and optimizer
network.compile(
loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy']
)
# Execute training process
training_history = network.fit(
x=X_train,
y=Y_train,
validation_split=0.2,
batch_size=300,
epochs=10,
verbose=2
)
# Evaluate model performance on test set
test_score = network.evaluate(X_test, Y_test)
predicted_classes = network.predict_classes(X_test)
print('Test accuracy:', test_score[1])
print('Sample predictions:', predicted_classes[:10])
Training Visualization
Visualizing training progress helps monitor model convergence and detect overfitting.
import matplotlib.pyplot as plt
def plot_training_progress(history, metric, validation_metric):
plt.figure(figsize=(12, 4))
# Plot accuracy
plt.subplot(1, 2, 1)
plt.plot(history.history[metric])
plt.plot(history.history[validation_metric])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='lower right')
# Plot loss
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='upper right')
plt.tight_layout()
plt.show()
plot_training_progress(training_history, 'accuracy', 'val_accuracy')
Performance Evaluation
Evaluation involves both quantitative metrics and visual confusion matrix analysis.
import pandas as pd
import seaborn as sns
# Quantitative evaluation
evaluation_score = network.evaluate(X_test, Y_test)
print(f'Model Performance - Loss: {evaluation_score[0]:.4f}, Accuracy: {evaluation_score[1]:.4f}')
# Generate predictions
test_predictions = network.predict_classes(X_test)
actual_labels = np.argmax(Y_test, axis=1)
# Create confusion matrix
confusion_matrix = pd.crosstab(
actual_labels,
test_predictions,
rownames=['Actual'],
colnames=['Predicted']
)
# Visualize results
plt.figure(figsize=(10, 8))
sns.heatmap(confusion_matrix, annot=True, fmt='d', cmap='Blues', linewidths=0.5)
plt.title('Confusion Matrix for Handwritten Digit Classification')
plt.show()