← Back Case study · Learning log Course capstone · 2024

AQ Forecasting

A deep-learning capstone on satellite air quality: six years of Sentinel-5P carbon monoxide over Ho Chi Minh City, a PyTorch GRU, and an honest account of what a first forecasting model gets wrong, and why that mattered more than the score.

Data Science capstone · CoderSchool 2024 · archived Sentinel-5P TROPOMI · Google Earth Engine Python · PyTorch · GRU

00 / 03Data

Six years of CO from orbit.

The dataset is real and self-collected: Sentinel-5P TROPOMI near-real-time carbon monoxide column density, pulled through Google Earth Engine at one-kilometre scale over Ho Chi Minh City. November 2018 to September 2024, 1,307 daily samples after cleaning: a proper, gappy, seasonal satellite time series rather than a course-provided CSV.

That choice was the point. Remote sensing is where I came from, and the capstone was the first attempt to connect it to deep learning end to end: acquisition, cleaning, modelling, evaluation.

01 / 03Model

A GRU, and a baseline.

A PyTorch GRU (single layer, hidden size 16, Adam, MSE loss) against a linear-regression baseline. Trained on 70% of the series, evaluated on a held-out test set, checkpointed and reloaded cleanly: the full supervised workflow, built by hand rather than borrowed.

The result was honest and unflattering: test R² of 0.085 for the GRU, with the linear baseline at 0.035. The model had collapsed to predicting the mean. The interesting part is why, and the notebook says so plainly instead of hiding it.

02 / 03Lessons

What it taught.

Features carry the signal

The model was fed a bare time index: no lagged windows, no monsoon seasonality, no meteorology. A recurrent network with nothing to recur over cannot beat the mean, and it did not.

Respect the arrow of time

The split was random rather than chronological, which leaks the future into training and still could not rescue the score. Every forecasting system I have built since splits by time, without exception.

Baselines first

The linear baseline was the most useful model in the notebook: it set the bar the GRU failed to clear, and made the failure visible. Seasonal and persistence baselines now come first in everything I ship.

Where it led

The discipline in 3DREAMS (calibrated probabilities, explicit gaps, physics-gated scores, baselines before models) started as the corrections to this notebook. A weak first model, taken seriously, was worth more than a lucky strong one.

Next case study 3DREAMS@SG →
© 2026 Anthony Meijer · Singapore AQ Forecasting · CoderSchool capstone