"A robot that cannot perceive its world cannot act within it. Teaching machines to see road signs, segment lane markings, and cluster scenes without labels is not merely computer vision — it is the foundation of autonomous robotic intelligence."
Author: Umer Ahmed Baig Mughal
Programme: MSc Robotics and Artificial Intelligence
Specialization: Machine Learning · Computer Vision · Human-Robot Interaction · Autonomous Systems · Robotic Motion Control
Institution: ITMO University — Faculty of Control Systems and Robotics
- 📖 About This Repository
- 🗂️ Repository Structure
- 🔬 Course Overview
- 🧪 Lab Summaries
- 🔵 Lab 1 — Road Sign Classification with Custom CNN and Transfer Learning
- 🟠 Lab 2 — Road Sign Segmentation with SegNet and Data Augmentation
- 🟣 Lab 3 — Road Scene Reconstruction and Clustering with Convolutional Autoencoder
- 🟢 Lab 4 — Autoencoder Comparison: Latent Space Interpolation, Vector Arithmetic and Denoising
- 🔗 Progressive Learning Pathway
- ⚙️ Common Platform — Shared Dataset and Training Infrastructure
- 🚀 Quick Start
- 📊 Results at a Glance
- 🧰 Tech Stack
- 👤 Author
- 📄 License
This repository contains the complete implementation of all four laboratory assignments from the Machine Learning in Robotics (MLR) course, part of the MSc in Robotics and Artificial Intelligence at ITMO University. The labs form a carefully sequenced progression — from supervised image-level classification to pixel-level segmentation to fully unsupervised latent space exploration — covering the core machine learning paradigms applied directly to autonomous vehicle perception data.
The course progresses in a deliberate two-phase structure: Phase 1 (Labs 1 and 2) establishes the supervised learning foundation by training models that require labelled ground truth — binary image classification and pixel-wise segmentation of road photographs. Phase 2 (Labs 3 and 4) makes a fundamental paradigm shift to unsupervised learning, where no labels are used at all — the model learns entirely from the structure of the images themselves via a convolutional autoencoder, then uses its learned representations for clustering, interpolation, semantic vector arithmetic, and zero-shot denoising.
All architectures are implemented from first principles in PyTorch. Every component — the SimpleCnn classifier, the SegNet encoder-decoder, the SimpleAutoencoder, the WeightedRandomSampler balancing pipeline, the IoU metric, and the latent interpolation experiments — is coded explicitly without relying on any pre-built segmentation or clustering framework, ensuring deep architectural understanding at every level of the machine learning stack.
| 📁 Lab | 🏷️ Topic | 🧠 Core Concept | 🛠️ Key Tools |
|---|---|---|---|
| Lab 1 | Binary Image Classification | Custom CNN · WeightedSampler · Transfer Learning | PyTorch · torchvision · scikit-learn |
| Lab 2 | Pixel-Wise Segmentation | SegNet · MaxPool Indices · IoU Metric | PyTorch · torchsummary |
| Lab 3 | Unsupervised Reconstruction | Convolutional Autoencoder · t-SNE · KMeans | PyTorch · scikit-learn |
| Lab 4 | Latent Space Exploration | Interpolation · Vector Arithmetic · Denoising | PyTorch · scikit-learn |
📦 Machine-Learning-in-Robotics/
│
├── 📁 Lab_1/
│ ├── 📄 Readme.md # Lab 1 full documentation
│ ├── 📁 src/
│ │ └── 📓 Road_Sign_Classification.ipynb # Custom CNN + Augmentation + ResNet50
│ └── 📁 results/
│ ├── 🖼️ Road_Sign.png # Dataset sample visualisation
│ ├── 🖼️ Loss_Curve_CustomCNN.png # Training loss — Model 1
│ ├── 🖼️ Loss_Curve_AugmentedCNN.png # Training loss — Model 2
│ └── 🖼️ Loss_Curve_ResNet50.png # Training loss — Model 3
│
├── 📁 Lab_2/
│ ├── 📄 Readme.md # Lab 2 full documentation
│ ├── 📁 src/
│ │ └── 📓 Road_Sign_Segmentation.ipynb # SegNet baseline + augmented
│ └── 📁 results/
│ ├── 🖼️ Road_Sign_Dataset.png # Dataset + mask visualisation
│ ├── 🖼️ Loss_IoU_Curve_Baseline.png # Loss + IoU vs epoch — Model 1
│ ├── 🖼️ Loss_IoU_Curve_Augmented.png # Loss + IoU vs epoch — Model 2
│ ├── 🖼️ Prediction_Samples_Baseline.png # Test predictions — Model 1
│ └── 🖼️ Prediction_Samples_Augmented.png # Test predictions — Model 2
│
├── 📁 Lab_3/
│ ├── 📄 Readme.md # Lab 3 full documentation
│ ├── 📁 src/
│ │ └── 📓 Road_Scene_Autoencoder.ipynb # Autoencoder + clustering pipeline
│ └── 📁 results/
│ ├── 🖼️ Road_Sign_Data.png # Dataset sample visualisation
│ ├── 🖼️ Epoch_10_from_100.png # Reconstruction snapshot — epoch 10
│ ├── 🖼️ Epoch_50_from_100.png # Reconstruction snapshot — epoch 50
│ ├── 🖼️ Epoch_100_from_100.png # Reconstruction snapshot — epoch 100
│ ├── 🖼️ Loss_Curve_Phase1.png # MSE loss — Phase 1 (100 epochs)
│ ├── 🖼️ Loss_Curve_Phase2.png # MSE loss — Phase 2 (100 epochs)
│ ├── 🖼️ tSNE_Clusters.png # t-SNE scatter plot, KMeans labels
│ └── 🖼️ Cluster_Image_Grid.png # 6 clusters × 5 images grid
│
├── 📁 Lab_4/
│ ├── 📄 Readme.md # Lab 4 full documentation
│ ├── 📁 src/
│ │ └── 📓 Autoencoder_Comparison_and_Latent_Space.ipynb # model64 vs model32 — all experiments
│ └── 📁 results/
│ ├── 🖼️ Loss_Model64.png # MSE loss vs epoch — model64
│ ├── 🖼️ Loss_Model32.png # MSE loss vs epoch — model32
│ ├── 🖼️ tSNE_Clusters_Model64.png # t-SNE scatter — model64
│ ├── 🖼️ tSNE_Clusters_Model32.png # t-SNE scatter — model32
│ ├── 🖼️ Cluster_Grid_Model64.png # 6×5 cluster image grid — model64
│ ├── 🖼️ Cluster_Grid_Model32.png # 6×5 cluster image grid — model32
│ ├── 🖼️ Interpolation_Model64.png # 1×7 interpolation figure — model64
│ ├── 🖼️ Interpolation_Model32.png # 1×7 interpolation figure — model32
│ ├── 🖼️ VectorArithmetic_Model64.png # 3×4 arithmetic grid — model64
│ ├── 🖼️ VectorArithmetic_Model32.png # 3×4 arithmetic grid — model32
│ ├── 🖼️ Denoising_Model64.png # 5×3 denoising grid — model64
│ └── 🖼️ Denoising_Model32.png # 5×3 denoising grid — model32
│
└── 📄 README.md # ← You are here
The Machine Learning in Robotics course develops both the theoretical foundations and the practical engineering skills required to apply modern machine learning techniques to robotic perception tasks. The curriculum spans supervised classification and segmentation, unsupervised representation learning, and advanced latent space analysis — all applied to a real-world road-scene dataset that connects directly to autonomous vehicle and mobile robotics applications.
The four labs advance in a deliberate two-phase sequence:
Phase 1 — Supervised Learning (Labs 1 and 2): The course begins with labelled learning. Lab 1 establishes the supervised classification pipeline — deriving labels from raw segmentation masks, handling class imbalance with WeightedRandomSampler, training a custom CNN from scratch, and comparing it against ResNet50 transfer learning. Lab 2 scales the output space from a single scalar per image to a full spatial mask, introducing pixel-level segmentation with the SegNet architecture and the IoU metric. Together these two labs build the complete supervised perception pipeline from data preparation through evaluation.
Phase 2 — Unsupervised Learning (Labs 3 and 4): With the supervised foundation in place, the course makes a fundamental paradigm shift. Lab 3 trains a convolutional autoencoder without a single label — the model learns by reconstructing its own inputs, producing a compact 16,384-dimensional latent representation of each road scene that is then used for t-SNE projection and KMeans clustering. Lab 4 probes the geometry of this learned latent space through four systematic experiments — cluster comparison, latent interpolation, vector arithmetic, and zero-shot denoising — comparing two autoencoder variants of different capacity across all experiments.
Supervised Supervised Unsupervised Unsupervised
Classification Segmentation Reconstruction Latent Exploration
────────────── ──────────── ───────────── ──────────────────
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ LAB 1 │──────────► │ LAB 2 │────────► │ LAB 3 │────────► │ LAB 4 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
Custom CNN SegNet SimpleAutoencoder model64 vs model32
WeightedSampler MaxPool Indices Latent Bottleneck Interpolation
Balanced Accuracy IoU Metric t-SNE + KMeans Vector Arithmetic
ResNet50 Transfer pos_weight=2.0 MSE Reconstruction Zero-Shot Denoising
Given a dataset of 256×256 RGB road photographs paired with segmentation masks, derive binary classification labels from the mask pixel sums, handle the resulting class imbalance, and train three progressively sophisticated models to classify whether a road sign is present or absent — evaluating all using balanced accuracy.
This lab establishes the complete supervised image classification pipeline, answering the foundational question: "Given a raw unlabelled dataset and its associated segmentation masks, how do you construct a reliable binary classifier that is robust to class imbalance and generalises well to unseen data?"
What the task requires:
- Derive binary labels from segmentation masks by computing per-image pixel sums and applying a threshold of 1205 — converting a segmentation dataset into a classification one without manual labelling.
- Diagnose the class imbalance between road-sign-present and road-sign-absent images, and apply
WeightedRandomSamplerwith inverse-frequency weights to draw balanced mini-batches during training. - Design and train a
SimpleCnnclassifier from scratch: four convolutional blocks (3→8→16→32→64channels,Conv2d + ReLU + MaxPool2d + BatchNorm2d) followed by a three-layer fully connected head (16384→480→384→1) with Dropout regularization. - Train a second
SimpleCnnvariant using anOurOwnDatasetclass that applies random rotation (±10°) and horizontal flipping on-the-fly — isolating the effect of data augmentation on overfitting reduction. - Load a ResNet50 backbone with ImageNet weights, freeze all parameters, replace the final
fclayer withLinear(2048→1), and fine-tune only the new head for 5 epochs — demonstrating transfer learning efficiency. - Use
balanced_accuracy_scorefrom scikit-learn as the primary metric, ensuring that errors on the minority class are penalized equally to errors on the majority class.
| Concept | Description |
|---|---|
| 🏷️ Label Derivation | np.sum(Y, axes=(1,2,3)) > 1205 — pixel-sum threshold converts mask dataset to classification labels |
| ⚖️ WeightedRandomSampler | Per-sample inverse-frequency weights; draws balanced batches without discarding any data |
| 🧱 SimpleCnn | 4 conv blocks → flatten 16,384 → FC(480) → FC(384) → FC(1) — all implemented from scratch |
| 📐 BCEWithLogitsLoss | Fused sigmoid + BCE — numerically stable binary cross-entropy for logit outputs |
| 🔄 On-the-Fly Augmentation | OurOwnDataset.__getitem__ applies fresh RandomRotation(±10°) + RandomHorizontalFlip per access |
| 🏛️ ResNet50 Transfer | Frozen backbone (23M params) + new Linear(2048→1) head — only 2,049 parameters trained |
| 📊 Balanced Accuracy | = 0.5 × (recall_class_0 + recall_class_1) — corrects for imbalance in evaluation |
| 🎯 ImageNet Normalization | mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225) — applied in-place per image |
Dataset: x_train.npy (256×256×3 RGB) + y_train.npy (masks → pixel-sum threshold labels)
Labels: sum > 1205 → sign present (1) | sum ≤ 1205 → absent (0)
Split: 2700 train / 200 val / ~100+ test | SEED=60 | Batch=128
Balanced Accuracy Scores
┌──────────────────────────────┬──────────┬───────────┬───────────┬──────────────────┐
│ Model │ Train │ Val │ Test │ Pass Requirement │
├──────────────────────────────┼──────────┼───────────┼───────────┼──────────────────┤
│ Custom CNN (no augmentation) │ 99.60% │ 97.20% │ 92.30% │ ≥ 95.0% test ✅ │
│ Augmented CNN │ 97.16% │ 97.45% │ — │ ≥ 96.0% val ✅ │
│ ResNet50 Transfer Learning │ 99.41% │ 95.01% │ — │ ≥ 95.0% val ✅ │
└──────────────────────────────┴──────────┴───────────┴───────────┴──────────────────┘
All three pass requirements satisfied | Augmented model: train-val gap reduced to 0.29%
📂 → View Lab 1 Full Documentation
Using the same road photograph dataset as Lab 1, subsample 1000 images, binarize their multi-class segmentation masks, and train a SegNet encoder-decoder to predict a pixel-wise binary foreground mask for each image — with the final augmented model achieving IoU ≥ 0.65 on the test set.
This lab makes the critical architectural leap from image-level classification to pixel-level segmentation — the model must now predict a label for every one of the 65,536 pixels in a 256×256 image, not just a single scalar. The defining architectural innovation is SegNet's pooling index propagation, which routes the exact location of each MaxPool operation back to the corresponding unpooling step in the decoder.
What the task requires:
- Subsample the full dataset at stride 3 to obtain 1000 images, and binarize the multi-class masks: collapse road signs (class 1) and crosswalk markings (class 2) into a single foreground class 1, leaving background as class 0.
- Build SegNet entirely from scratch: five encoder blocks (
3→64→128→256→512→512channels), each applyingConv2d + BatchNorm + ReLUfollowed byMaxPool2d(return_indices=True); a symmetric five-block decoder usingMaxUnpool2dto restore spatial resolution using saved pool indices. - Apply
BCEWithLogitsLoss(pos_weight=2.0)to compensate for the severe pixel-level imbalance between background (majority) and foreground (minority) pixels. - Implement the IoU metric in PyTorch:
IoU = (|target ∩ pred| + ε) / (|target ∪ pred| + ε)using bitwise AND/OR operations, withSMOOTH=1e-8to prevent zero division on all-background batches. - Train a unified loop across
{'train': ..., 'valid': ...}stages for 65 epochs, visualising six validation image/mask/prediction triplets after every epoch, and computing final IoU on all three splits after training. - Extend to a second model trained with an
OurOwnDatasetthat concatenates image and mask into a 4-channel tensor for joint geometric transforms — ensuring the augmentation is applied identically to both image and its corresponding mask.
| Concept | Description |
|---|---|
| 🖼️ Pixel-Wise Output | Model outputs (B, 1, 256, 256) logit mask — one prediction per pixel per image |
| 🔗 MaxPool Index Propagation | MaxPool2d(return_indices=True) saves argmax → MaxUnpool2d restores exact spatial locations |
| 📦 5-Block Encoder | 3→64→128→256→512→512 channels; 5× MaxPool2d(2,2) → 8×8 spatial bottleneck |
| 🔁 Symmetric Decoder | MaxUnpool2d mirrors each encoder block; channels reversed 512→512→256→128→64→1 |
| ⚖️ pos_weight=2.0 | Upweights foreground BCE loss 2× to counter pixel-level class imbalance |
| 📐 IoU Metric | intersection / union per image — penalises both false positives and false negatives equally |
| 🎨 Joint Augmentation | 4-channel tensor (R,G,B,mask) allows single transform to affect image and mask identically |
| 📊 score_model() | Evaluates mean IoU over full DataLoader under torch.no_grad() — one scalar per split |
Dataset: x_train.npy[0:3000:3] → 1000 images | masks binarized {0,1,2} → {0,1}
Split: 700 train / 150 val / 150 test | SEED=44 | Batch=32
SegNet: 5-block Enc-Dec, MaxPool index propagation, Adam lr=0.0001
Mean IoU Scores (pixel-level)
┌─────────────────────────┬───────────┬──────────┬───────────┬──────────────────┐
│ Model │ Train IoU │ Val IoU │ Test IoU │ Pass Requirement │
├─────────────────────────┼───────────┼──────────┼───────────┼──────────────────┤
│ Baseline SegNet │ 0.939 │ 0.613 │ 0.619 │ ≥ 0.50 test ✅ │
│ Augmented SegNet │ 0.929 │ 0.656 │ 0.669 │ ≥ 0.65 test ✅ │
└─────────────────────────┴───────────┴──────────┴───────────┴──────────────────┘
Both pass requirements satisfied | Augmentation improves test IoU by +0.050 (8.1% gain)
📂 → View Lab 2 Full Documentation
Using all 3,000 road photographs downscaled to 128×128, train a convolutional autoencoder entirely without labels to reconstruct its own inputs through a 16,384-dimensional bottleneck — then use the learned latent representations to discover six natural scene clusters via t-SNE projection and KMeans clustering.
This lab marks the fundamental paradigm shift from supervised to unsupervised learning. No ground-truth labels, masks, or thresholds are used anywhere. The TensorDataset(X_t, X_t) self-supervised pairing makes each image both the input and the reconstruction target; the model learns by minimising its own pixel-wise reconstruction error. The bottleneck forces it to encode only the most essential visual structure, producing a compact latent space rich enough to reflect semantic groupings.
What the task requires:
- Process all 3000 images (
X[0:3000:1]), downscaling from 256×256 to 128×128 viaTVF.resize()and applying ImageNet normalization — creating aTensorDataset(X_t, X_t)where input equals target. - Design
SimpleAutoencoder(latent_channels=64)from scratch: a three-layer strided-convolution encoder reducing spatial resolution from 128×128 to 16×16 — producing a(B, 64, 16, 16) = 16,384-elementbottleneck (3.0× compression) — followed by a symmetric three-layer transposed-convolution decoder with no final activation. - Train in two phases using
MSELossandAdam(lr=0.001): Phase 1 for 100 epochs targeting val loss ≤ 0.05; Phase 2 continuing the same model for a further 100 epochs targeting val loss ≤ 0.01. Save a reconstruction snapshot every 2 epochs. - Extract flattened latent vectors
(2100, 16384)from the encoder for all validation images, applyTSNE(n_components=2, perplexity=30)to reduce to 2D, and fitKMeans(n_clusters=6)to assign cluster labels. - Visually interpret each of the six clusters by sampling five representative images per cluster — connecting the unsupervised groupings to semantic scene categories based on lighting conditions, road environment, and time of day.
| Concept | Description |
|---|---|
| 🔄 Self-Supervised Pairing | TensorDataset(X_t, X_t) — input equals target; no external labels needed |
| 🏗️ Strided Conv Encoder | Conv2d(stride=2) × 3 reduces 128→64→32→16; learnable downsampling |
| 📦 Latent Bottleneck | (B, 64, 16, 16) = 16,384 elements; 3.0× compression of 49,152-element input |
| 🔁 ConvTranspose2d Decoder | ConvTranspose2d(output_padding=1) × 3 restores exact 128×128 resolution |
| 📉 MSELoss | Pixel-wise L² regression — appropriate for continuous normalized pixel targets |
| 🚫 No Final Activation | Decoder outputs raw logits — ImageNet normalization range extends beyond [0,1] |
| 🗺️ t-SNE Projection | TSNE(n_components=2, perplexity=30) maps 16,384-dim → 2D for visualisation |
| 🔵 KMeans Clustering | Fitted on 2D t-SNE features; n_clusters=6 reveals scene-type groupings |
Dataset: x_train.npy X[0:3000:1] → 3000 images, downscaled 256→128 via TVF.resize()
Split: 900 train / 2100 val (no test set) | SEED=44 | Batch=256
SimpleAutoencoder(latent_channels=64) | Bottleneck: 16,384 elements | 3.0× compression
MSE Reconstruction Loss
┌──────────────────────────┬────────┬──────────────┬──────────────┬────────────────┐
│ Training Phase │ Epochs │ Start Loss │ Final Loss │ Pass Target │
├──────────────────────────┼────────┼──────────────┼──────────────┼────────────────┤
│ Phase 1 — initial train │ 100 │ ~1.000 │ ~0.044 │ ≤ 0.05 ✅ │
│ Phase 2 — continued │ 100 │ ~0.044 │ ≤ 0.010 │ ≤ 0.01 ✅ │
└──────────────────────────┴────────┴──────────────┴──────────────┴────────────────┘
6 Semantic Clusters Discovered (KMeans on 2D t-SNE of validation latent features):
Cluster 0 — Night / High-Illumination scenes
Cluster 1 — Overcast Daytime roads
Cluster 2 — Bright Urban Crosswalks
Cluster 3 — Dusk / Evening Urban environments
Cluster 4 — Mixed-Light Urban scenes
Cluster 5 — Bright Rural / Suburban roads
📂 → View Lab 3 Full Documentation
Introduce a second
SimpleAutoencodervariant (model32, latent_channels=32) alongside the Lab 3 model (model64, latent_channels=64) and evaluate both across four systematic experiments that probe the geometry, capacity, and semantic structure of their respective latent spaces: clustering comparison, latent interpolation, vector arithmetic, and zero-shot denoising.
This lab transforms the autoencoder from a reconstruction tool into a subject of systematic study. Rather than simply training a model, the student runs controlled experiments that reveal how the learned latent space is structured — whether it is smooth enough to interpolate between images, whether it encodes semantic directions that can be transferred between scenes, and whether the bottleneck compression incidentally filters Gaussian noise. The dual-model design isolates the precise effect of halving the latent capacity from 16,384 to 8,192 elements.
What the task requires:
- Train
model64(latent_channels=64) to val MSE ≤ 0.01 andmodel32(latent_channels=32) to val MSE ≤ 0.05 — both usingAdam(lr=0.003)for 100 epochs each on the identical Lab 3 dataset and split. - Experiment 1 — Clustering comparison: Extract and flatten latent features from both encoders across the 2,100-image validation set; apply independent
TSNE(n_components=2, perplexity=30)projections andKMeans(n_clusters=6)to each; compare t-SNE scatter plots and 6×5 cluster image grids side-by-side. - Experiment 2 — Latent interpolation (image fusion): Select two validation images from different clusters; compute
z(α) = (1−α)·z₁ + α·z₂for α ∈ {0, 0.25, 0.5, 0.75, 1.0}; decode each intermediate vector to a 1×7 visualisation; compare transition smoothness between models. - Experiment 3 — Vector arithmetic: For each model, select images A, B, C from three different clusters and compute
D = C + (B − A)in latent space; decode D and interpret whether the semantic transformation from A to B has been transferred to C — repeated for three cyclic cluster permutations per model. - Experiment 4 — Zero-shot denoising: Add Gaussian noise (
noise_level=0.7) to clean validation images, clamp to[-1.0, 1.0], and pass through each autoencoder without any denoising-specific training; computeF.mse_loss(denoised, clean)for both models and visualise the clean / noisy / denoised triplets.
| Concept | Description |
|---|---|
| 📦 Latent Capacity | model64: 16,384 elements, 3.0× compression · model32: 8,192 elements, 6.0× compression |
| 🗺️ Dual Clustering | Independent t-SNE + KMeans applied per model — reveals how capacity affects feature geometry |
| 🔀 Latent Interpolation | z(α) = (1−α)·z₁ + α·z₂ — smooth manifold → coherent transitions; rough manifold → blurring |
| ➕ Vector Arithmetic | D = C + (B − A) — tests whether semantic directions are linearly separable in latent space |
| 🔇 Zero-Shot Denoising | Bottleneck discards noise dimensions not present in training — no retraining needed |
| 📐 Reshape for Decoder | z.view(1, C, 16, 16) required before decoding flattened interpolated/arithmetic vectors |
| 📊 MSE Denoising Metric | F.mse_loss(denoised, clean) — quantifies noise suppression relative to corrupted baseline |
| ⚖️ Capacity vs. Quality | Higher latent dim → richer geometry, smoother interpolation, better arithmetic, lower denoising MSE |
Two-model comparison — identical architecture, same dataset, different bottleneck depth
model64: latent (B, 64, 16, 16) = 16,384 elements | compression: 3.0× | target ≤ 0.01 MSE ✅
model32: latent (B, 32, 16, 16) = 8,192 elements | compression: 6.0× | target ≤ 0.05 MSE ✅
Four-Experiment Comparison Summary
┌────────────────────────────────┬────────────────────────┬────────────────────────┐
│ Experiment │ model64 (16,384-dim) │ model32 (8,192-dim) │
├────────────────────────────────┼────────────────────────┼────────────────────────┤
│ Clustering (t-SNE + KMeans) │ Tight, well-separated │ More overlap between │
│ │ cluster boundaries. │ adjacent clusters. │
│ Interpolation quality │ Smooth, semantically │ Blurring at midpoints; │
│ │ coherent transitions. │ abrupt element fading. │
│ Vector arithmetic D=C+(B−A) │ Global changes (light) │ Degraded fidelity; │
│ │ transfer cleanly ✅ │ structural noise ❌ │
│ Denoising MSE (noise=0.7) │ 0.224126 ✅ │ 0.231699 ❌ │
└────────────────────────────────┴────────────────────────┴────────────────────────┘
model64 outperforms model32 across all four experiments — performance gap widens with task complexity
Both models reduce MSE by ~53–54% from the noisy baseline — zero-shot denoising is robust at both capacities
📂 → View Lab 4 Full Documentation
The four labs form an interlocking progression where each lab builds directly on the foundations of the previous one. The dependency is not merely thematic — specific architectures, dataset pipelines, training functions, and evaluation methods introduced in each lab are carried forward and extended in the next, building cumulative engineering depth.
╔═════════════════════════════════════════════════════════════════════════════════════════╗
║ MACHINE LEARNING IN ROBOTICS — PROGRESSION ║
╠══════════════════╦═══════════════════╦═══════════════════╦══════════════════════════════╣
║ 🔵 LAB 1 🔵 ║ 🟠 LAB 2 🟠 ║ 🟣 LAB 3 🟣 ║ 🟢 LAB 4 🟢 ║
║ Road Sign ║ Road Sign ║ Road Scene ║ Autoencoder ║
║ Classification ║ Segmentation ║ Autoencoder ║ Comparison ║
╠══════════════════╬═══════════════════╬═══════════════════╬══════════════════════════════╣
║ Supervised ║ Supervised ║ Unsupervised ║ Unsupervised ║
║ Classification ║ Pixel-Wise Seg. ║ Reconstruction ║ Latent Exploration ║
║ Balanced Acc. ║ IoU Metric ║ MSELoss ║ 4 Experiments ║
╠══════════════════╬═══════════════════╬═══════════════════╬══════════════════════════════╣
║ SimpleCnn ║ SegNet ║ SimpleAutoencoder║ model64 vs model32 ║
║ 4 conv blocks ║ 5-block Enc-Dec ║ 3 strided Conv ║ 16,384 vs 8,192 latent ║
║ + ResNet50 TL ║ MaxPool indices ║ + 3 ConvTranspose║ dual model comparison ║
╠══════════════════╬═══════════════════╬═══════════════════╬══════════════════════════════╣
║ Adam lr=0.0003 ║ Adam lr=0.0001 ║ Adam lr=0.001 ║ Adam lr=0.003 ║
║ 20–25 epochs ║ 65 epochs ║ 200 epochs total ║ 100 epochs each ║
║ BCEWithLogits ║ BCEWithLogits ║ MSELoss ║ MSELoss ║
║ WeightedSampler ║ pos_weight=2.0 ║ TensorDataset ║ TensorDataset ║
╠══════════════════╬═══════════════════╬═══════════════════╬══════════════════════════════╣
║ Establishes ║ Scales output: ║ Paradigm shift: ║ Extends Lab 3: ║
║ supervised ║ image-level → ║ supervised → ║ adds model32, runs ║
║ classification ║ pixel-level mask ║ unsupervised ║ clustering, interpolation ║
║ baseline ║ + IoU metric ║ + latent space ║ arithmetic, denoising ║
╚══════════════════╩═══════════════════╩═══════════════════╩══════════════════════════════╝
Dependency chain:
- 🔵 Lab 1 establishes the full supervised pipeline — dataset loading, ImageNet normalization, DataLoader creation, the
train()/predict()loop pattern, and balanced accuracy evaluation. These infrastructure components are reused verbatim in Lab 2. - 🟠 Lab 2 inherits the Lab 1 dataset and normalization pipeline, scales the output from a scalar to a full spatial mask, and introduces the shared
loaders = {'train': ..., 'valid': ...}training loop pattern with per-stagemodel.train()/model.eval()switching — a pattern carried forward into Labs 3 and 4. - 🟣 Lab 3 inherits the training loop pattern from Lab 2, reuses the same
segmentation_new.zipdataset with stride-1 sampling, and introduces theSimpleAutoencoderarchitecture and unsupervisedTensorDataset(X_t, X_t)self-supervised pairing — both directly reused in Lab 4. - 🟢 Lab 4 inherits
SimpleAutoencoder, the Lab 3 dataset pipeline, and the training loop entirely, adding only the second capacity variant and four new experiment functions that probe the geometry of the learned latent space.
All four labs operate on a single shared source dataset of road photographs and segmentation masks, downloaded once and used across the entire course. The dataset is what connects the machine learning techniques to a genuine robotic perception application — autonomous vehicles must classify, segment, and understand road scenes in real time.
Source: http://mlr.vedyakov.com/segmentation_new.zip
Files: x_train.npy — shape (N, 256, 256, 3), uint8, RGB road photographs
y_train.npy — shape (N, 256, 256, 1), uint8, pixel-class segmentation masks
Each lab draws from this single source but uses it differently, with progressively richer exploitation of the data's structure:
| 📁 Lab | Images Used | Resolution | Labels | Split | Batch | SEED |
|---|---|---|---|---|---|---|
| Lab 1 | ~3000 (stride=1) |
256 × 256 | Binary class (mask pixel-sum > 1205) | 2700 / 200 / ~100 | 128 | 60 |
| Lab 2 | 1000 (stride=3) |
256 × 256 | Binary pixel mask ({0,1,2}→{0,1}) | 700 / 150 / 150 | 32 | 44 |
| Lab 3 | 3000 (stride=1) |
128 × 128 ↓ | None (self-supervised) | 900 / 2100 / — | 256 | 44 |
| Lab 4 | 3000 (stride=1) |
128 × 128 ↓ | None (self-supervised) | 900 / 2100 / — | 256 | 44 |
ImageNet normalization is applied identically across all four labs — the only consistent preprocessing step shared from Lab 1 through Lab 4:
| Channel | Mean | Standard Deviation |
|---|---|---|
| Red | 0.485 | 0.229 |
| Green | 0.456 | 0.224 |
| Blue | 0.406 | 0.225 |
Labs 2, 3, and 4 share the same fundamental training loop architecture. The loaders = {'train': ..., 'valid': ...} dictionary pattern, model.train() / model.eval() stage transitions, per-batch GPU-to-CPU memory cleanup, and torch.cuda.empty_cache() calls are reused across all three labs with only the loss function, metric, and visualisation callback changing.
# Shared dataset preparation pattern (all labs)
X = np.load('./x_train.npy') # (N, H, W, 3)
X_t = torch.FloatTensor(np.transpose(X / 255, (0,3,1,2))) # CHW float
for x in X_t: # normalize in-place
TVF.normalize(x, mean=(0.485,0.456,0.406),
std=(0.229,0.224,0.225), inplace=True)
# Shared training loop pattern (Labs 2, 3, 4)
for epoch in range(epochs):
for stage, dataloader in loaders.items():
model.train() if stage == 'train' else model.eval()
for X_batch, Y_batch in dataloader:
X_batch = X_batch.to(device)
Y_batch = Y_batch.to(device)
if stage == 'train':
optimizer.zero_grad()
output = model(X_batch)
loss = loss_fn(output, Y_batch) # BCEWithLogitsLoss or MSELoss
loss.backward()
optimizer.step()
else:
with torch.no_grad():
output = model(X_batch)
loss = loss_fn(output, Y_batch)
# Explicit GPU memory cleanup — required for large batch sizes
X_batch.cpu(); Y_batch.cpu()
torch.cuda.empty_cache()PyTorch API elements introduced progressively across the four labs:
| API Element | Introduced | Purpose |
|---|---|---|
BCEWithLogitsLoss, WeightedRandomSampler |
Lab 1 | Stable binary loss + class-imbalance correction |
balanced_accuracy_score, predict() |
Lab 1 | Imbalance-aware evaluation metric |
BCEWithLogitsLoss(pos_weight=2.0) |
Lab 2 | Pixel-level foreground weight amplification |
MaxPool2d(return_indices=True), MaxUnpool2d |
Lab 2 | SegNet pooling index propagation |
iou_pytorch(), score_model() |
Lab 2 | Per-image and full-split IoU evaluation |
TVF.resize(), MSELoss, TensorDataset(X,X) |
Lab 3 | Downscaling, reconstruction loss, self-supervised pair |
TSNE, KMeans, plot_example() |
Lab 3 | Latent space projection, clustering, snapshot saves |
| Latent interpolation, vector arithmetic | Lab 4 | Explicit latent manifold geometry manipulation |
F.mse_loss denoising evaluation |
Lab 4 | Zero-shot denoising quantification |
git clone https://github.com/umerahmedbaig7/Machine-Learning-in-Robotics.git
cd Machine-Learning-in-RoboticsAll labs run on Google Colab where packages are pre-installed. For a local environment:
# All labs
pip install torch torchvision numpy scikit-learn matplotlib seaborn tqdm torchsummary📌 For a clean local virtual environment:
python -m venv mlr_env source mlr_env/bin/activate # Windows: mlr_env\Scripts\activate pip install torch torchvision numpy scikit-learn matplotlib seaborn tqdm torchsummary
Each lab is a self-contained Colab notebook. Before running any lab:
- Upload the notebook to Google Drive or open directly from GitHub.
- In the menu: Runtime → Change runtime type → Hardware accelerator → GPU (T4).
- Confirm GPU is available — the notebook will print the GPU device name on startup.
⚠️ All labs are designed for T4 GPU execution. CPU execution is possible but training times will increase by 10–30× for Labs 3 and 4.
The dataset download is embedded in every notebook. Alternatively, mount Google Drive with a pre-uploaded copy:
# Way 1 — Direct download (all labs, runs automatically)
!wget -c http://mlr.vedyakov.com/segmentation_new.zip -O segmentation.zip
!unzip -q ./segmentation.zip
# Way 2 — From Google Drive (if pre-uploaded)
from google.colab import drive
drive.mount('/content/gdrive/')
!unzip -q /content/gdrive/MyDrive/University/MLR/segmentation.zipThen execute all cells sequentially (Runtime → Run all):
# 🔵 Lab 1 — Road Sign Classification (~15–25 min on T4)
Open: Lab_1/src/Road_Sign_Classification.ipynb
# 🟠 Lab 2 — Road Sign Segmentation (~25–35 min on T4)
Open: Lab_2/src/Road_Sign_Segmentation.ipynb
# 🟣 Lab 3 — Road Scene Autoencoder (~60–75 min on T4)
Open: Lab_3/src/Road_Scene_Autoencoder.ipynb
# 🟢 Lab 4 — Autoencoder Comparison (~75–105 min on T4)
Open: Lab_4/src/Autoencoder_Comparison_and_Latent_Space.ipynb
| 📁 Lab | Notebook | Estimated T4 Runtime |
|---|---|---|
| Lab 1 | Road_Sign_Classification.ipynb |
~15–25 min |
| Lab 2 | Road_Sign_Segmentation.ipynb |
~25–35 min |
| Lab 3 | Road_Scene_Autoencoder.ipynb |
~60–75 min |
| Lab 4 | Autoencoder_Comparison_and_Latent_Space.ipynb |
~75–105 min |
| Model | Train Acc. | Val Acc. | Test Acc. | Train-Val Gap | Pass ✓ |
|---|---|---|---|---|---|
| Custom CNN (no augmentation) | 99.60% | 97.20% | 92.30% | 2.40% | ≥ 95.0% test ✅ |
| Augmented CNN | 97.16% | 97.45% | — | 0.29% | ≥ 96.0% val ✅ |
| ResNet50 Transfer Learning | 99.41% | 95.01% | — | 4.40% | ≥ 95.0% val ✅ |
| Model | Train IoU | Val IoU | Test IoU | IoU Improvement | Pass ✓ |
|---|---|---|---|---|---|
| Baseline SegNet | 0.939 | 0.613 | 0.619 | — | ≥ 0.50 test ✅ |
| Augmented SegNet | 0.929 | 0.656 | 0.669 | +0.050 (+8.1%) | ≥ 0.65 test ✅ |
| Phase | Epochs | Starting Val MSE | Final Val MSE | Pass Target | Status |
|---|---|---|---|---|---|
| Phase 1 | 100 | ~1.000 | ~0.044 | ≤ 0.05 | ✅ |
| Phase 2 (continued) | 100 | ~0.044 | ≤ 0.010 | ≤ 0.01 | ✅ |
| Experiment | model64 (16,384 latent) | model32 (8,192 latent) | Winner |
|---|---|---|---|
| Training convergence | Val MSE ≤ 0.01 ✅ | Val MSE ≤ 0.05 ✅ | Both pass |
| Clustering quality | Tighter t-SNE separation | More cluster overlap | model64 |
| Interpolation | Smooth, coherent transitions | Blurring at midpoints | model64 |
| Vector arithmetic | Global semantics transfer cleanly | Structural noise | model64 |
| Denoising MSE | 0.224126 | 0.231699 | model64 |
No pre-built segmentation pipelines. No AutoML frameworks. No external model zoos beyond a single ResNet50 backbone in Lab 1. Every architecture in this course — from the SimpleCnn classifier with its four convolutional blocks, to the SegNet encoder-decoder with full MaxPool index propagation, to the SimpleAutoencoder with its 16,384-dimensional bottleneck — is implemented explicitly in PyTorch from first principles, ensuring depth of understanding at every layer of the machine learning stack.
🎓 MSc Robotics and Artificial Intelligence — ITMO University
🏛️ Faculty of Control Systems and Robotics
🔬 Specialization: Machine Learning · Computer Vision · Human-Robot Interaction · Autonomous Systems · Robotic Motion Control
This repository is intended for academic and research use. All work was developed as part of the Machine Learning in Robotics course within the MSc Robotics and Artificial Intelligence program at ITMO University. Redistribution, modification, and use in derivative academic work are permitted with appropriate attribution to the original author.
Machine Learning in Robotics — MSc Robotics and Artificial Intelligence | ITMO University
⭐ If this repository helped you understand machine learning for robotic perception, consider giving it a star! ⭐