Skip to content

Repository files navigation

EuroSAT Land Cover Classification & Ghost Class Discovery

Train a land-cover classifier on 6 known EuroSAT classes from scratch, then build an OOD detection + unsupervised clustering system that discovers 4 previously unseen ("ghost") terrain classes — without ever using their labels during training or detection.


Project Structure

eurosat_classifier/
├── data/
│   └── dataset.py              # Data pipeline, augmentation, normalization
├── models/
│   ├── simple_cnn.py            # Baseline: Simple ConvNet
│   └── resnet_scratch.py        # Strong: ResNet-18 (trained from scratch)
├── training/
│   ├── trainer.py               # Training loop, early stopping, LR schedulers
│   └── losses.py                # Cross-entropy, label smoothing, focal loss
├── evaluation/
│   └── evaluator.py             # Metrics, confusion matrix, misclassified viz
├── utils/
│   ├── utils.py                 # Reproducibility, logging, optimizer helpers
│   └── weights_manager.py       # Google Drive checkpoint downloader ← NEW
├── experiments/
│   └── run_experiments.py       # HP sweeps, overfitting/underfitting demos
├── task2/
│   ├── deployment_pool.py       # Unlabeled pool construction
│   ├── ood_detection.py         # MSP, Energy, Mahalanobis, KNN detectors
│   ├── feature_extraction.py    # Neural + spectral feature extraction
│   ├── clustering.py            # UMAP + HDBSCAN ghost-class discovery
│   └── analysis_plots.py        # Task 2 visualizations
├── main.py                      # Task 1 entry point
├── task2_main.py                # Task 2 entry point
├── config.py                    # All hyperparameters + Google Drive IDs
├── 01_data_pipeline.ipynb       # Notebook 1 - Dataset exploration
├── 02_models_and_training.ipynb # Notebook 2 — Model Architectures & Training
├── 03_evaluation.ipynb          # Notebook 3 - Evaluation & Model Comparison
├── 04_task2_ood_discovery.ipynb # Notebook 4 — Task 2: OOD Detection & Ghost Class Discovery
├── Task1_report.md              # Task 1 technical report
├── Task2_report.md              # Task 2 technical report
└── requirements.txt             # pip dependencies, pinned

Dataset

Download EuroSAT RGB from one of:

Extract so the folder structure is ./EuroSAT_RGB/AnnualCrop/, ./EuroSAT_RGB/Forest/, etc.


Class Split (Table 1)

Split Classes Meaning
Known (6) AnnualCrop, Forest, Highway, Industrial, Residential, SeaLake Trained on
Ghost (4) HerbaceousVegetation, Pasture, PermanentCrop, River OOD — never seen in Task 1

Install dependencies

pip install --upgrade pip

pip install -r requirements.txt


Model Weights (Google Drive)

Pre-trained weights are hosted on Google Drive and downloaded automatically when running with --resume or when Task 2 cannot find the checkpoint.

Model Google Drive Link Description
resnet18.pt Download ResNet-18 from scratch — best Task 1 model
simple_cnn.pt Download SimpleCNN baseline

Replace PLACEHOLDER_*_FILE_ID with real Google Drive file IDs after uploading. See utils/weights_manager.py → WEIGHTS_REGISTRY for configuration.

Download weights manually

python utils/weights_manager.py download            # all weights
python utils/weights_manager.py download --name resnet18   # specific
python utils/weights_manager.py list                # check status

Run — Task 1

python main.py                            # full pipeline: train + evaluate
python main.py --resume                   # skip training; load weights (auto-downloads from Drive)
python main.py --resume --skip_experiments  # weights + evaluation only

Run — Task 2

python task2_main.py                      # full OOD discovery pipeline
python task2_main.py --skip_clustering    # OOD detection only (faster)

Task 2 auto-downloads resnet18.pt from Google Drive if not found locally.


Reproduce from Scratch

git clone <repo_url> && cd eurosat_classifier
pip install -r requirements.txt
wget https://madm.dfki.de/files/sentinel/EuroSAT.zip && unzip EuroSAT.zip -d EuroSAT_RGB
python main.py          # Task 1 (~60 epochs)
python task2_main.py    # Task 2

Or, using pre-trained weights:

python main.py --resume      # downloads weights from Google Drive automatically
python task2_main.py

All seeds fixed via SEED = 42 in config.py. Results are fully reproducible.


Key Results

Model Test Acc Macro F1 Params
SimpleCNN (baseline) 92.27% 0.9196 ~0.3M
ResNet-18 (from scratch) 97.45% 0.9745 ~11M
OOD Method AUROC FPR@95TPR
MSP 0.9086 0.3870
Energy Score 0.8289 0.7960
Mahalanobis 0.6348 0.6690
KNN (k=10) 0.9285 0.3280

Design Decisions

  • No pretrained weights — every parameter learned from EuroSAT data only.
  • Ghost class exclusion — filtered at dataset level; cannot leak into Task 1.
  • Test set evaluated once — enforced via EVALUATE_TEST_ONCE=True in config.
  • Normalization from train set only — no validation/test statistics used.
  • Intermediate layer features for OODstage3 features, not final logits.
  • HDBSCAN — no prior on number of ghost classes required.
  • UMAP — preserves global + local structure; deterministic with fixed seed.

About

An experimental repository working with EuroSAT RGB dataset.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages