Online · Szczecin · Poland

Online STEM Education for Kids in Szczecin

Online STEM education for kids in Szczecin emphasises structured problem-solving—hypothesis, test, revise—from a standard home computer.

STEM Szczecin

Chapter 167 — U-Net Explained: A Deep Learning Model for Image Segmentation

1. Introduction: What is Image Segmentation & U-Net?

In previous modules, we explored Object Detection architectures like YOLO, which localize objects using rectangular bounding boxes. While bounding boxes work well for coarse localization, many applications—such as medical imaging, autonomous driving, and satellite analysis—require defining exact object boundaries down to the individual pixel level.

  ┌─────────────────────────────────────────────────────────────┐
  │                 Pixel-Level Image Segmentation              │
  ├─────────────────────────────────────────────────────────────┤
  │  Goal: Assign every individual pixel in an input image      │
  │        to a specific class or object mask label.            │
  │                                                             │
  │  Input Image (128 × 128 × 3) ──► U-Net Model ──► Output Mask│
  │                                                  (128×128×1)│
  └─────────────────────────────────────────────────────────────┘
    

Core Definition:

Image Segmentation is the task of partitioning an image into distinct segments by assigning a specific class label to every single pixel in the image.

To solve image segmentation efficiently, the U-Net architecture was introduced. U-Net is a specialized deep learning convolutional neural network designed to map input images directly to precise pixel-level segmentation masks.

2. The U-Net Architectural Framework (The U-Shape)

U-Net derives its name from its symmetrical U-shaped network architecture. It consists of three primary structural pathways:

  ┌─────────────────────────────────────────────────────────────────────────┐
  │                        U-Net Symmetrical Architecture                   │
  ├─────────────────────────────────────────────────────────────────────────┤
  │                                                                         │
  │  Input Image (128×128×3)                               Output Mask      │
  │       │                                                    ▲            │
  │  [Enc 1: 32 Filters]  ─────── Skip Connection ──────► [Dec 3: 32 Filters] │
  │       │ (Max Pool)                                         ▲ (UpSample) │
  │  [Enc 2: 64 Filters]  ─────── Skip Connection ──────► [Dec 2: 64 Filters] │
  │       │ (Max Pool)                                         ▲ (UpSample) │
  │  [Enc 3: 128 Filters] ─────── Skip Connection ──────► [Dec 1: 128 Filters]│
  │       │ (Max Pool)                                         ▲ (UpSample) │
  │       └────────────────► [ Bottleneck ] ───────────────────┘            │
  │                          (256 Filters)                                  │
  └─────────────────────────────────────────────────────────────────────────┘
    

A. The Contracting Path (Encoder):

  • Function: Acts as a feature extractor that captures spatial context.
  • Structure: Consists of repeated pairs of Convolutional Blocks followed by Max Pooling layers.
  • Spatial Shrinking: Max pooling reduces spatial dimensions (height and width) while progressively expanding feature channel depth (e.g., 32 → 64 → 128 filters).

B. The Bottleneck:

  • Function: Connects the encoder pathway to the decoder pathway.
  • Structure: Contains the deepest convolutional block (e.g., 256 filters). It compresses the image into its most compact, high-level feature representation.

C. The Expanding Path (Decoder):

  • Function: Reconstructs fine spatial details to expand low-resolution feature maps back to the original image dimensions.
  • Structure: Consists of Up-Sampling layers, Concatenation steps, and Convolutional Blocks.
  • Spatial Expansion: Up-sampling restores spatial resolution while gradually decreasing channel depth (e.g., 128 → 64 → 32 filters).

D. Skip Connections:

  • Function: Connects encoder feature maps directly to corresponding decoder feature maps via concatenation.
  • Why Skip Connections Matter: Downsampling in the encoder discards fine spatial localization details. Skip connections pass high-resolution spatial feature maps directly across to the decoder, ensuring precise pixel localization.

3. Data Pipeline & Synthetic Dataset Creation in Google Colab

Because U-Net requires substantial memory for pixel-level computations, training is executed in Google Colab using cloud GPU hardware acceleration.

import tensorflow as tf
from tensorflow.keras import layers, models
import numpy as np
import cv2
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
    

Synthetic Dataset Generation:

We create a custom synthetic dataset consisting of 300 image samples of size 128 × 128 × 3 alongside their corresponding binary segmentation masks.

# Function to generate synthetic circular shape segmentation dataset
def create_synthetic_dataset(num_samples=300, img_size=128):
    images = []
    masks = []

    for _ in range(num_samples):
        # Create blank canvas for image and single-channel mask
        img = np.zeros((img_size, img_size, 3), dtype=np.uint8)
        mask = np.zeros((img_size, img_size, 1), dtype=np.uint8)

        # Generate random circle coordinates and radius
        center_x = np.random.randint(30, 98)
        center_y = np.random.randint(30, 98)
        radius = np.random.randint(15, 35)

        # Draw random colored circle on image and white circle on binary mask
        color = np.random.randint(100, 255, size=(3,)).tolist()
        cv2.circle(img, (center_x, center_y), radius, color, -1)
        cv2.circle(mask, (center_x, center_y), radius, 255, -1)

        images.append(img / 255.0)       # Normalize image pixels (0.0 to 1.0)
        masks.append(mask / 255.0)       # Normalize mask pixels (0 or 1)

    return np.array(images, dtype=np.float32), np.array(masks, dtype=np.float32)

# Generate synthetic dataset
X, Y = create_synthetic_dataset(num_samples=300, img_size=128)

# Split into 80% Training and 20% Testing sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42)
print(f"Training set: {X_train.shape}, Testing set: {X_test.shape}")
    

4. Building U-Net with Keras Functional API

U-Net cannot be built using models.Sequential because skip connections require branching and concatenating multiple layer tensors. Instead, we use the Keras Functional API.

A. Reusable Convolutional Block Function:

Each block applies two sequential 3 × 3 convolutions with padding='same' (to preserve spatial size) and ReLU activation:

# Reusable Convolutional Block (2 x Conv2D + ReLU + Padding Same)
def conv_block(x, num_filters):
    x = layers.Conv2D(num_filters, (3, 3), padding='same', activation='relu')(x)
    x = layers.Conv2D(num_filters, (3, 3), padding='same', activation='relu')(x)
    return x
    

B. Full Architecture Construction:

def build_unet(input_shape=(128, 128, 3)):
    inputs = layers.Input(input_shape)  # Input tensor: (128, 128, 3)

    # -----------------------------------------------------------------
    # ENCODER (Contracting Path)
    # -----------------------------------------------------------------
    c1 = conv_block(inputs, 32)
    p1 = layers.MaxPooling2D((2, 2))(c1)  # Spatial shape: 64x64x32

    c2 = conv_block(p1, 64)
    p2 = layers.MaxPooling2D((2, 2))(c2)  # Spatial shape: 32x32x64

    c3 = conv_block(p2, 128)
    p3 = layers.MaxPooling2D((2, 2))(c3)  # Spatial shape: 16x16x128

    # -----------------------------------------------------------------
    # BOTTLENECK
    # -----------------------------------------------------------------
    b = conv_block(p3, 256)               # Spatial shape: 16x16x256

    # -----------------------------------------------------------------
    # DECODER (Expanding Path with Skip Connections)
    # -----------------------------------------------------------------
    u1 = layers.UpSampling2D((2, 2))(b)
    concat1 = layers.Concatenate()([u1, c3])  # Skip connection from c3
    c4 = conv_block(concat1, 128)

    u2 = layers.UpSampling2D((2, 2))(c4)
    concat2 = layers.Concatenate()([u2, c2])  # Skip connection from c2
    c5 = conv_block(concat2, 64)

    u3 = layers.UpSampling2D((2, 2))(c5)
    concat3 = layers.Concatenate()([u3, c1])  # Skip connection from c1
    c6 = conv_block(concat3, 32)

    # -----------------------------------------------------------------
    # OUTPUT LAYER
    # -----------------------------------------------------------------
    outputs = layers.Conv2D(1, (1, 1), activation='sigmoid')(c6)

    model = models.Model(inputs=inputs, outputs=outputs, name="U-Net")
    return model

model = build_unet()
    

5. Model Diagnostics & Parameter Analysis (model.summary())

Executing model.summary() displays the complete layer connectivity and trainable parameter counts:

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

model.summary()
    
  ┌─────────────────────────────────────────────────────────────────────────┐
  │                        Model Summary Breakdown                          │
  ├──────────────────────────────┬───────────────────┬──────────────────────┤
  │ Layer (Type)                 │ Output Shape      │ Param #              │
  ├──────────────────────────────┼───────────────────┼──────────────────────┤
  │ input_1 (InputLayer)         │ (None, 128,128, 3)│ 0                    │
  │ conv2d (Conv2D - Block 1)    │ (None, 128,128,32)│ 896                  │
  │ conv2d_1 (Conv2D - Block 1)  │ (None, 128,128,32)│ 9,248                │
  │ max_pooling2d (MaxPooling2D) │ (None, 64, 64, 32)│ 0                    │
  │ ...                          │ ...               │ ...                  │
  │ up_sampling2d (UpSampling)   │ (None, 32, 32,256)│ 0                    │
  │ concatenate (Concatenate)    │ (None, 32, 32,384)│ 0                    │
  │ conv2d_ output (Conv2D 1x1)  │ (None, 128,128, 1)│ 33                   │
  ├──────────────────────────────┴───────────────────┴──────────────────────┤
  │ Total params: 1,941,121 (1.94 Million Params / ~7.43 MB memory)         │
  │ Trainable params: 1,941,121                                             │
  │ Non-trainable params: 0                                                 │
  └─────────────────────────────────────────────────────────────────────────┘
    

Parameter Takeaways:

  • Total Trainable Parameters: Contains ~1.9 Million parameters (1,941,121 trainable weights) taking up ~7.43 MB of memory.
  • Zero Parameter Layers: Input, Max Pooling, Up-Sampling, and Concatenate layers require 0 trainable weights.
  • Output 1x1 Conv2D: Uses a 1 × 1 kernel with Sigmoid activation to output single-channel binary pixel probability maps.

6. Training Dynamics & Performance Evaluation (fit & evaluate)

We train the U-Net model using model.fit() over 10 Epochs with a batch size of 8 and a 20% validation split:

# Train U-Net Model
history = model.fit(
    X_train, Y_train,
    epochs=10,
    batch_size=8,
    validation_split=0.2
)
    
  ┌─────────────────────────────────────────────────────────────┐
  │              Training Progression & Computational Weight    │
  ├─────────────────────────────────────────────────────────────┤
  │  Computational Intensity:                                   │
  │    • Segmentation is computationally heavier than object    │
  │      detection because EVERY pixel requires a class         │
  │      label prediction.                                      │
  │                                                             │
  │  Epoch Metrics Progress:                                    │
  │    • Epoch 1  : Accuracy = 96.97% | Loss = 0.3400           │
  │    • Epoch 10 : Accuracy = 99.98% | Loss = 0.00845          │
  │                                                             │
  │  Test Set Evaluation (model.evaluate):                      │
  │    • Test Loss     : 0.00526                                │
  │    • Test Accuracy : 99.98%                                 │
  └─────────────────────────────────────────────────────────────┘
    

Performance Takeaways:

  • High Computational Load: Training takes longer per epoch compared to basic classification networks because every pixel requires a separate classification decision.
  • Convergence: Accuracy increases from 96.97% in Epoch 1 to 99.98% in Epoch 10, while loss drops exponentially from 0.34 down to 0.00845.
  • Final Test Evaluation: Testing on unseen evaluation samples yields a test loss of 0.00526 and a test accuracy of 99.98%. In noisy client datasets, slightly lower real-world accuracy is expected.

7. Visualizing Segmentation Masks (Image vs. Ground Truth vs. Prediction)

We visualize model predictions side-by-side using Matplotlib to compare the Original Image, True Mask (Ground Truth), and Predicted Mask:

# Predict segmentation masks on test samples
predictions = model.predict(X_test[:3])

# Plot comparison figures
plt.figure(figsize=(10, 8))
for i in range(3):
    # Original Image
    plt.subplot(3, 3, i*3 + 1)
    plt.imshow(X_test[i])
    plt.title(f"Sample {i+1}: Input Image")
    plt.axis('off')

    # Ground Truth Mask
    plt.subplot(3, 3, i*3 + 2)
    plt.imshow(Y_test[i].squeeze(), cmap='gray')
    plt.title(f"Sample {i+1}: True Mask")
    plt.axis('off')

    # Predicted Mask
    plt.subplot(3, 3, i*3 + 3)
    plt.imshow(predictions[i].squeeze() > 0.5, cmap='gray')
    plt.title(f"Sample {i+1}: Predicted Mask")
    plt.axis('off')

plt.tight_layout()
plt.show()
    

8. Summary Comparison Table

Component / Pipeline Stage Keras Function / Setting Key Operational Details
Input Tensor Shape layers.Input((128, 128, 3)) Color images of size 128×128×3.
Encoder Path Conv2D (3x3) + MaxPooling2D (2x2) Shrinks spatial shape; expands filters (32, 64, 128).
Bottleneck conv_block(p3, 256) Deepest feature representation (256 filters).
Decoder Path UpSampling2D + Concatenate Restores spatial size & concatenates skip connections.
Skip Connections layers.Concatenate()([u, c]) Passes high-res encoder features to decoder.
Output Layer Conv2D(1, (1, 1), activation='sigmoid') Generates 128×128×1 binary mask probabilities.
Total Model Footprint model.summary() 1.94 Million parameters (~7.43 MB).
Compilation Settings adam, binary_crossentropy, accuracy Adam optimizer & binary cross-entropy loss.
Training Parameters 10 Epochs, Batch Size = 8, Split = 0.2 GPU training on synthetic dataset.
Final Test Metrics model.evaluate() Test Loss: 0.00526, Test Accuracy: 99.98%.

Lesson Q&A

Core Definition: U-Net is a specialized deep learning model designed for image segmentation, where the goal is to classify every individual pixel in an image into a specific class category.

Three Structural Stages:

  1. Encoder (Contracting Path): Captures context by passing the image through repeating blocks of convolutional layers and max-pooling layers to downsample spatial dimensions.
  2. Bottleneck: Sits between the encoder and decoder to capture the deepest, most essential feature representations of the input data.
  3. Decoder (Expanding Path): Reconstructs spatial resolution by applying upsampling layers to expand the feature maps back toward the original input image size.

Role of Skip Connections: Skip connections directly concatenate feature maps from corresponding encoder layers to decoder layers. This transfers fine-grained spatial and edge details directly across the network, making final localization significantly more precise.

Input Specifications: Expects an input image tensor of size 128 x 128 across 3 color channels (128, 128, 3).

Encoder & Bottleneck Architecture: Uses 3 encoder blocks combining 2D Convolutional layers (3x3 kernel, same padding, ReLU activation) and Max-Pooling layers with filter counts increasing from 32 to 64 to 128, followed by a bottleneck block with 256 filters.

Decoder & Output Head:

  • Decoder Blocks: Uses 3 expanding blocks consisting of UpSampling2D, feature concatenation (skip connections), and Conv2D blocks with filter counts decreasing from 128 to 64 to 32.
  • Output Layer: A 1x1 Conv2D layer with a Sigmoid activation function to output a pixel-wise probability map for binary segmentation.

Model Scale: The entire functional model contains approximately 1.9 million trainable parameters (~7.43 MB memory footprint) with 0 non-trainable parameters.

Compilation Settings:

  • Optimizer: Adam optimizer.
  • Loss Function: Binary Cross-Entropy (binary_crossentropy).
  • Metric: Accuracy.

Training Execution: Trained on Google Colab GPU over 10 epochs with a batch size of 8 and an 80/20 validation split. Over 10 epochs, training loss dropped from 0.34 down to 0.00845, while accuracy reached 99.98%.

Final Evaluation & Visualization:

  • Testing on unseen data achieved a test loss of 0.00526 and a test accuracy of 99.98%.
  • Predictions were evaluated visually by plotting the original image, true ground-truth mask, and model-predicted mask side-by-side using Matplotlib.

What children can learn

WhatsApp +92 322 421 7059

AI and Robotics Club | The 100 Mission AI & Robotics Hero Journey

Interactive Online STEM: Structured Problem-Solving Pathways in Szczecin

Szczecin families need honest online STEM: a standard PC, modern browser and connection. Solving challenges independently builds resilience. Avenvera.com offers child development insights. Enquire WhatsApp +92 322 4217059. Verified details only on enquiry.

Frequently asked questions

What basic hardware is needed for online STEM classes in Szczecin?

A standard PC or laptop running a modern browser with an active internet connection is all you need.

Does STEM education help build problem-solving confidence?

Yes, solving coding and logic challenges independently builds resilience and cognitive confidence.

Where can parents find child development guidance?

Insights on child growth and learning are available via our partner platform Avenvera.com.

Explore related learning

BydgoszczComputational Thinking · Bydgoszcz KatowicePython · Katowice WarsawRobotics · Warsaw