---
title: Intel Scene CNN
emoji: 🏔️
colorFrom: blue
colorTo: green
sdk: gradio
sdk_version: 5.9.1
app_file: app.py
pinned: false
license: mit
---
# Intel Scene Classification: a CNN built from scratch
A plain convolutional neural network (no ResNet, no pretrained weights) trained on the
[Intel Image Classification](https://www.kaggle.com/datasets/puneet6060/intel-image-classification)
dataset: ~25k natural scene photos in 6 classes: **buildings, forest, glacier, mountain, sea, street**.
This is the follow-up to my EuroSAT satellite CNN. It tests whether the same architecture
generalizes to street-level photos, where orientation matters and the classes overlap visually.
**Live demo:** upload an image, get the predicted class and a Grad-CAM heatmap, plus the test metrics.
## Results
| Metric | Value |
|---|---|
| Test accuracy (seg_test, 3,000 images) | **89.87%** |
| Macro F1 | **0.900** |
| Best validation accuracy | **90.98%** |
| Parameters | **1,206,886** |
| Class | Precision | Recall | F1 | Test images |
|---|---|---|---|---|
| buildings | 0.913 | 0.860 | 0.886 | 437 |
| forest | 0.979 | 0.985 | 0.982 | 474 |
| glacier | 0.846 | 0.866 | 0.856 | 553 |
| mountain | 0.876 | 0.830 | 0.852 | 525 |
| sea | 0.895 | 0.937 | 0.916 | 510 |
| street | 0.897 | 0.918 | 0.907 | 501 |
**Where it fails:** mountain → glacier (65), buildings → street (46), glacier → mountain (46),
street → buildings (29). Almost all errors come from two pairs: snow-covered peaks vs. glaciers,
and urban scenes where a photo of a street is also a photo of buildings. Forest is near-perfect
(98% F1) because its texture and color are unique. Several high-confidence "mistakes" are
labeling errors in the dataset itself.



## Dataset
| Split | Images |
|---|---|
| seg_train → train (85%) | ~11,900 |
| seg_train → validation (15%, stratified) | ~2,100 |
| seg_test (held-out test) | 3,000 |
| seg_pred (unlabeled) | ~7,300 |



## Architecture
```mermaid
flowchart LR
A[Input 3×128×128] --> B1[Block 1
32ch → 64×64]
B1 --> B2[Block 2
64ch → 32×32]
B2 --> B3[Block 3
128ch → 16×16]
B3 --> B4[Block 4
256ch → 8×8]
B4 --> G[Global Avg Pool
256]
G --> F1[Dropout 0.4 → FC 128 → ReLU]
F1 --> F2[FC 6 → softmax]
```
Each block is `Conv3×3 → BN → ReLU → Conv3×3 → BN → ReLU → MaxPool 2×2 → Dropout2d(0.1)`.
| Stage | Output shape |
|---|---|
| Input | 3 × 128 × 128 |
| Block 1 | 32 × 64 × 64 |
| Block 2 | 64 × 32 × 32 |
| Block 3 | 128 × 16 × 16 |
| Block 4 | 256 × 8 × 8 |
| Global average pool | 256 |
| FC + ReLU | 128 |
| FC (logits) | 6 |
## Training setup
- **Optimizer:** AdamW (lr 1e-3, weight decay 1e-2), cosine LR schedule, 30 epochs
- **Loss:** cross-entropy with label smoothing 0.1
- **Image size:** 128×128, normalized with per-channel stats computed from the training data
- **Augmentation:** random resized crop (0.75–1.0), horizontal flip, ±10° rotation, mild color jitter
- **No vertical flips or large rotations.** Scenes have gravity (sky on top), unlike satellite images.
- **Model selection:** best validation accuracy checkpoint


## What the CNN learns
**Feature maps before vs. after training**


**First-layer filters before vs. after training**


**Grad-CAM: where the network looks**


**Confidence and mistakes**



## Run locally
```bash
pip install -r requirements.txt gradio
python app.py
```
## Files
| File | Purpose |
|---|---|
| `app.py` | Gradio app: upload, prediction, Grad-CAM, metrics, plots |
| `model.py` | `SceneCNN` architecture + Grad-CAM |
| `cnn_intel.pth` | Trained weights (state_dict) |
| `cnn_intel_meta.json` | Class names, normalization stats, width, image size |
| `metrics.json` | Test metrics shown in the app |
| `images/` | All plots from training and evaluation |
## Author
Chandra Prakash Bathula, Adjunct Faculty, Saint Louis University (School for Professional Studies)