Pi5AI Lab 01: Handwritten Digit Recognition

Introduction

In this lab, students will build and deploy a machine-learning pipeline to recognize handwritten digits (0–9). Starting on a Windows workstation using Docker and Jupyter Notebook, you will:

  1. Prepare a Docker-based development environment with Python and TensorFlow 2.
  2. Train a Convolutional Neural Network (CNN) on the MNIST dataset and evaluate its performance.
  3. Implement real-time digit recognition using a webcam.
  4. Convert and deploy the trained model to a Raspberry Pi 5 with a Hailo-8/8L AI module for edge inference.
  5. Load and classify static images from disk to verify model accuracy.

By completing this lab, you will gain hands-on experience with containerized development, model training, and evaluation, real-time inference on both desktop and embedded hardware, and image-based inference workflows.

Environment Setup

  1. Install Docker Desktop (Windows 10/11)
    • Download and install the latest Docker Desktop. Enable WSL 2 support.
    • Under Settings > General, tick Use the WSL 2 based engine.
  2. Project Setup
    First, create the following folder structure on your computer:
    
    <AI_Project_Folder>/
    |-- AI_Lab/
    |   `-- Lab01_HandwrittenDigitRecognition/
    |       |-- docker/
    |       |-- notebooks/
    |       |-- src/
    |       `-- saved_model/
    
  3. Create the Dockerfile
    This file defines the environment for this project.
    File: <AI_Project_Folder>\AI_Lab\Lab01_HandwrittenDigitRecognition\docker\Dockerfile
    
    # Use the official TensorFlow image as a base
    FROM tensorflow/tensorflow:2.13.0
    
    # Install OpenCV fuinctions to display and process images
    RUN apt-get update && apt-get install -y \
        libgl1-mesa-glx \
        libglib2.0-0 \
        libsm6 \
        libxrender1 \
        libxext6 \
      && rm -rf /var/lib/apt/lists/*
    
    # Set the working directory inside the container
    WORKDIR /app
    
    # Install required Python packages
    RUN pip install --no-cache-dir jupyterlab opencv-python matplotlib
    
    # Expose the Jupyter Notebook port and a port for a web app if needed
    EXPOSE 8888 6006
    
    # Command to run Jupyter Lab
    CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root", "--NotebookApp.token=''"]
    
  4. Build and Run the Docker Container
    Open a terminal (Command Prompt or PowerShell) and run the following commands:
      
    # Navigate to your project directory
    cd \<AI_Project_Folder>\AI_Lab\Lab01_HandwrittenDigitRecognition
    
    # Build the Docker image
    docker build -t ai-lab-windows -f docker/Dockerfile .
    
    # Run the Docker container
    docker run -p 8888:8888 -v "${pwd}:/app" --name ai-lab-instance ai-lab-windows  
    
    After the container starts, you will see Jupyter Notebook's startup messages in the command prompt or PowerShell, including a URL to access Jupyter Notebook. Typically, this URL will be http://127.0.0.1:8888/lab. Copy this URL and open it in your web browser.

PART I: AI Model Training (Jupyter Notebook)

In the Jupyter Notebook environment, we will use TensorFlow 2 to train a handwritten digit recognition model. We will use the MNIST dataset, a standard dataset containing images of handwritten digits from 0-9. After training, the model will be saved as handwritten_digit_model.h5.

In the Jupyter Notebook interface, create a new Python 3 Notebook and paste the following code. This code will load the MNIST dataset, normalize the images, build a simple Convolutional Neural Network (CNN) model, and then train and save the model.


# Load functions
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, MaxPooling2D
import matplotlib.pyplot as plt

# Load the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 重塑資料以符合模型需求
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)

# Normalize the images to a range of 0 to 1
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255

# Build the model
model = Sequential([
    Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)),
    MaxPooling2D(pool_size=(2, 2)),
    Flatten(),
    Dense(128, activation='relu'),
    Dense(10, activation='softmax')
])

# 在第四個儲存格:編譯模型
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 在第五個儲存格:訓練模型
model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test))

# 在第六個儲存格:儲存訓練好的模型
model.save('/app/saved_model/digit_recognition_model.h5')

print("模型已成功儲存!")

© 2025 Air Supply Information Center (Air Supply BBS)