Earth Observation · Whole-Tile Understanding

Remote-Sensing Scene ClassificationReading the Land from Above

A satellite stares straight down at a square kilometre of Earth and hands you a small image. There are no people to ask, no street signs to read — just a patch of color and texture seen from 786 km up. The question is deceptively simple: what is this place? Forest, residential, industrial, river. This is scene classification turned bird's-eye, fed by a sensor that sees colors your eyes can't, where a single tile is rarely just one thing.

Prerequisites: you know what a CNN roughly is and what a softmax does. If "a network outputs a probability over classes" makes sense, you're ready. We build the rest — multispectral bands, spectral indices, masked autoencoders, multi-label scoring — from zero.
10
Chapters
9
Simulations
0
Assumed Knowledge

Chapter 0: A Square Kilometre, Seen From Space

You are sitting in front of a wall of small images. Each one is a tile cut from a satellite pass over Europe — a square chunk of the planet, maybe one kilometre on a side, photographed from orbit. Your job, repeated a million times, is to put one word on each tile: what kind of land is this? Forest. Residential. Industrial. River. A government wants to track how much farmland is turning into suburbs. An insurer wants to map flood-prone zones. A climate team wants to count how fast a forest is shrinking. All of it starts with the same humble act: name the land in this tile.

Your first instinct is to treat it like an ordinary photo. You glance at one tile and you are stuck. From straight overhead, a parking lot and a flat industrial roof look almost identical — both are grey rectangles. A river and a road are both thin dark ribbons. A young forest and a healthy crop field are both green smears. You have lost every cue you normally rely on: there is no horizon, no familiar object at a familiar size, no front-versus-side view. This is the bird's-eye problem: when you look straight down, the world collapses into texture and color, and your everyday visual intuition stops working.

This is the domain of remote-sensing scene classification — assigning a land-use or land-cover label to an overhead image tile from a satellite or aircraft. "Remote sensing" just means measuring something without touching it; here, measuring the surface of the Earth from a sensor far above it. It is a cousin of ordinary scene classification (naming "kitchen" or "beach" in a phone photo), but the viewpoint, the sensor, and the very definition of a "class" all change.

Two surprises are waiting, and they organize this whole lesson. First, the camera is not a normal camera. A satellite like Sentinel-2 measures not three colors but thirteen — including bands of light your eyes physically cannot see, such as near-infrared. Healthy vegetation glows blindingly bright in near-infrared while looking merely green to you. That invisible channel is often the single most informative thing in the tile. We are going to make heavy use of light you have never seen.

Second, the "one tile, one label" assumption is a polite lie. A real one-kilometre tile usually contains several land covers at once: a patch of forest, a strip of pasture, a road, a few buildings. The honest answer is not a single word but a set of words. This is the multi-label shift, and it forces a quiet but deep change in the machinery — away from softmax (which says "pick exactly one") toward sigmoid (which says "each cover is present or not, independently"). We will earn that distinction carefully.

Play with the toggle below before we go further. The same tile is shown two ways: as the natural-color (RGB) image your eye would form, and as a near-infrared view where vegetation lights up. Notice that the RGB view can leave you genuinely unsure — is that dark patch water or shadow? — while the infrared view answers the vegetation question instantly. The whole craft of remote sensing is learning to combine these channels into one decision.

RGB vs. near-infrared — two ways to see one tile

Toggle the view. The RGB image is what your eye sees; the near-infrared image makes living vegetation glow. Cycle through tiles — some are ambiguous in RGB and obvious in NIR, which is exactly why satellites carry invisible bands.

The misconception to kill on day one: "a satellite tile is just a normal photo from far away, so a normal image classifier will do." Three things break that assumption. (1) The sensor has many more channels than RGB, and the most useful ones are invisible to you, so an RGB-only model throws away the best evidence. (2) The bird's-eye view removes the cues object recognition relies on, so "find the objects and vote" fails. (3) A tile is genuinely multi-label, so forcing one word is wrong from the start. Treat it like a normal photo and you lose on all three counts.

Why care beyond a wall of tiles? Because this single capability — turn an overhead patch into land-use labels — underpins crop monitoring, deforestation tracking, urban-growth mapping, disaster response, and climate accounting. Whenever someone says "we mapped X across the whole country from satellites," the first step was almost certainly a scene classifier reading tiles one at a time.

Over the next nine chapters we will build this from the ground up: pin down the task and its datasets, learn to read the thirteen-band multispectral stack (and compute a vegetation index by hand), build a CNN classifier for it, confront why it is genuinely hard, watch the field move to geospatial foundation models that pretrain by hiding and reconstructing pixels, make the multi-label shift explicit, and finish with a live tile classifier you can break on purpose. Let's start by pinning down the task.

A colleague proposes running an off-the-shelf RGB photo classifier directly on Sentinel-2 satellite tiles. What is the single biggest thing this plan throws away?

Chapter 1: The Task & Its Datasets

Let us make the task precise, because precision is what lets us build instead of wave. Remote-sensing scene classification takes a fixed-size image tile — a small square crop of satellite or aerial imagery — and outputs a land-use or land-cover label (or, as we'll see, a set of labels) from a fixed taxonomy. The unit of analysis is the tile, not the pixel and not a whole scene; you slice a giant satellite image into a grid of tiles and classify each one.

A word on the two phrases you'll hear. Land cover is the physical material on the ground — water, trees, bare soil, concrete. Land use is the human function — residential, industrial, agricultural. They overlap but differ: a grass field (cover) might be a park, a lawn, or a golf course (use). Datasets vary in which they target, and this matters because land use is harder — it asks the model to infer human intent from physical appearance.

Three datasets define the field. Anchor on these three names; they recur in every paper.

DatasetClassesTilesSource & flavor
EuroSAT1027,000Sentinel-2, 64×64 px, single-label land cover. The "MNIST of remote sensing" — small, clean, great for learning.
NWPU-RESISC454531,500Aerial RGB, 256×256 px, single-label land use. Broad scene taxonomy: airport, overpass, baseball field, …
BigEarthNet19 / 43~590,000Sentinel-2, multispectral, multi-label land cover. Huge, realistic — each tile carries several labels at once.

EuroSAT is where almost everyone starts. It cuts 27,000 small tiles from Sentinel-2 imagery and labels each with one of ten covers: AnnualCrop, Forest, HerbaceousVegetation, Highway, Industrial, Pasture, PermanentCrop, Residential, River, SeaLake. Because each tile is forced to one label, EuroSAT is single-label and easy to score — perfect for building intuition, which is why we'll use its ten classes throughout this lesson.

NWPU-RESISC45 raises the stakes to 45 land-use classes from higher-resolution aerial RGB, with the fine distinctions that make scene use hard: it separates "freeway" from "overpass," "church" from "palace." It is the bird's-eye analogue of an indoor-scene benchmark — many classes, subtle differences.

BigEarthNet is the realistic one and the reason Chapter 6 exists. With ~590,000 Sentinel-2 tiles, it abandons the single-label fiction: each tile is annotated with all the land covers present, drawn from a 19- or 43-class nomenclature. A tile can be simultaneously "Coniferous forest," "Pastures," and "Water bodies." Train on BigEarthNet and you are forced to confront multi-label classification head-on.

How a tile becomes a label, by hand. Single-label EuroSAT uses the same softmax machinery as ordinary classification. Suppose for one tile the model outputs three relevant logits: Forest = 4.0, Pasture = 2.5, HerbaceousVegetation = 2.0 (treat the other seven as ≈ 0). Softmax exponentiates and normalizes: e4.0 ≈ 54.60, e2.5 ≈ 12.18, e2.0 ≈ 7.39, sum ≈ 74.17. So P(Forest) = 54.60 / 74.17 ≈ 0.736, P(Pasture) ≈ 12.18 / 74.17 ≈ 0.164, P(Herb.Veg) ≈ 7.39 / 74.17 ≈ 0.100. Top-1 = Forest at 74%. Hold onto that softmax habit — in Chapter 6 we will deliberately throw it away.

The widget below lays out the ten EuroSAT classes as a small gallery of stylized overhead tiles. Tap any class to see a one-line definition and the kind of texture a satellite records for it. Notice how visually close some are — AnnualCrop, Pasture, and PermanentCrop are all "green-ish farmland," separated more by texture and season than by color. That closeness is the difficulty we'll quantify in Chapter 4.

The ten EuroSAT land covers — a tile gallery

Tap a tile to read its definition. Notice how several "green" agricultural classes look alike from above — the visual overlap between them is the source of the confusions we'll meet later.

Tap a tile to read its land-cover definition.
Common mistake: assuming all three datasets measure the same thing, so a model good on one is good on all. They differ on three axes that change the problem: sensor (Sentinel-2 multispectral vs aerial RGB), resolution (10 m vs sub-metre ground sampling), and label structure (single-label vs multi-label). A model that aces single-label EuroSAT can be useless on multi-label BigEarthNet because its very output layer (softmax) is built on the wrong assumption. Always check the sensor, the resolution, and the label structure before you trust a benchmark number.
For one EuroSAT tile a model outputs logits: River = 3.0, SeaLake = 2.0, Highway = 1.0 (others ≈ 0). Using softmax over just these three (e3≈20.1, e2≈7.39, e1≈2.72), what is P(River)?

Chapter 2: The Multispectral Input — Beyond RGB

Here is the heart of what makes remote sensing different from ordinary vision: the input is not a 3-channel RGB image but a multispectral stack of many channels, each measuring a different slice of the light spectrum. "Multispectral" means the sensor splits incoming light into several named bands — intervals of wavelength — and records a separate grayscale image for each. Sentinel-2 records 13 bands, spanning visible light, near-infrared, and shortwave infrared.

Why bother? Because different surfaces reflect different wavelengths in characteristic ways, and the most useful differences live outside the visible range. The signature example is the near-infrared (NIR) band, light just beyond red that your eyes can't detect. Healthy, chlorophyll-rich vegetation reflects NIR extremely strongly — far more than it reflects green — while reflecting red light weakly because chlorophyll absorbs red for photosynthesis. So in NIR, a thriving crop field is almost glowing, while bare soil, water, and concrete stay dim. The vegetation/non-vegetation question, ambiguous in RGB, becomes nearly trivial in NIR.

Think of the input as a channel stack: a tensor of shape (bands, height, width). For a 64×64 Sentinel-2 tile with all bands, that's roughly (13, 64, 64) — thirteen stacked grayscale images, perfectly aligned, one per wavelength. (In practice the bands come at different native resolutions — 10 m, 20 m, 60 m ground sampling — and are resampled to a common grid.) Where an RGB photo gives a network three numbers per pixel, a multispectral tile gives it thirteen, and the extra ten are where much of the discriminative power lives.

Rather than feed all thirteen raw bands to a network and hope, remote-sensing practice leans on spectral indices: simple arithmetic combinations of two bands that isolate a physical property. The most famous is the NDVI, the Normalized Difference Vegetation Index, a single number per pixel that measures "how vegetated is this." Its formula combines the NIR and red bands:

NDVI = (NIR − RED) / (NIR + RED)

Read the structure. The numerator (NIR − RED) is large and positive exactly when NIR is high and red is low — the chlorophyll signature. The denominator (NIR + RED) normalizes by overall brightness, so a sunlit field and a shaded field of the same vegetation give similar NDVI — the index measures type, not brightness. NDVI ranges from −1 to +1: dense healthy vegetation lands around 0.6–0.9, bare soil near 0.1–0.2, and water goes negative because water reflects almost no NIR.

NDVI, worked by hand for three pixels. Suppose reflectances are scaled to [0,1].
Healthy forest pixel: NIR = 0.50, RED = 0.05. NDVI = (0.50 − 0.05) / (0.50 + 0.05) = 0.45 / 0.55 = 0.818. Strongly vegetated.
Bare soil pixel: NIR = 0.30, RED = 0.25. NDVI = (0.30 − 0.25) / (0.30 + 0.25) = 0.05 / 0.55 = 0.091. Barely vegetated.
Open water pixel: NIR = 0.04, RED = 0.10. NDVI = (0.04 − 0.10) / (0.04 + 0.10) = −0.06 / 0.14 = −0.429. Negative → not vegetation, likely water.
One formula, three readings — vegetation, soil, water — that you simply cannot get from RGB alone.

The widget makes NDVI tactile. Drag the NIR and red sliders and watch the resulting NDVI value and its color band. Push NIR up with red low and you climb toward dense vegetation; pull NIR below red and the index goes negative into the water regime. This single derived channel often separates EuroSAT's classes better than any raw band, and it is the substrate for one of our Code Labs.

Build an NDVI value — drag NIR and red

Set the near-infrared and red reflectance for one pixel. NDVI = (NIR−RED)/(NIR+RED) updates live, with the regime it falls into. Drive NIR high / red low for vegetation; NIR below red for water.

NIR reflectance0.50
RED reflectance0.05
Misconception: "more bands is always strictly better, so always feed all 13." Not free. Extra bands add channels (more parameters, more compute), and several bands are highly correlated, so the marginal information per band falls off. Worse, some bands are noisy or atmospherically contaminated (Sentinel-2's band 10 is a cirrus-cloud band, near-useless for land cover). The skilled move is to use the informative bands and well-chosen indices (NDVI, NDWI for water, NDBI for built-up) rather than dumping all 13 in raw. Channel choice is an engineering decision, not a default.
A pixel reads NIR = 0.6, RED = 0.1. What is its NDVI, and what does the sign tell you?

Chapter 3: The CNN Baseline

Now we build a classifier. The remote-sensing baseline is the same workhorse as ordinary scene recognition — a convolutional neural network — with one surgical change at the input. Let us trace the data flow with real shapes, because the shapes show exactly where multispectral enters.

In ordinary vision the input is (3, 224, 224): three RGB channels. Here the input is the multispectral stack, say (13, 64, 64): thirteen bands over a 64×64 tile. The only structural difference is the first convolutional layer. A standard CNN's first conv expects 3 input channels; we widen it to accept 13 (or however many bands we use). Concretely, the first layer's weight tensor changes from shape (out, 3, k, k) to (out, 13, k, k) — each output filter now mixes thirteen input bands instead of three. Everything downstream is unchanged.

From there the network behaves like any CNN. Blocks of convolutions shrink the spatial size and grow the channel count, until you hold a feature map of shape, say, (256, 4, 4): 256 learned feature channels, each a tiny 4×4 spatial grid. Each channel fires where some learned pattern appears — "bright NIR texture here," "regular grid of building roofs there," "smooth dark water over here."

Then the move that matters for tiles: global average pooling (GAP). We collapse each spatial grid to a single number by averaging it. The (256, 4, 4) map becomes a (256,) vector. This vector says, for each of the 256 patterns, "how much of this pattern is present anywhere in the tile," discarding where. For overhead land cover this is exactly right: a forest tile is a forest tile whether the densest trees are top-left or bottom-right. Location within the tile is mostly irrelevant; presence is everything.

Finally a fully-connected layer maps the (256,) vector to K logits (one per land-cover class), and softmax turns logits into probabilities. That is the whole single-label classifier. Let us watch one toy example end to end.

Global average pooling, by hand. Take one feature channel, a 3×3 grid (we use 3×3 instead of 4×4 to keep the arithmetic visible):
[[0.8, 0.9, 0.7], [0.1, 0.85, 0.6], [0.2, 0.75, 0.5]]
GAP = average of all nine = (0.8+0.9+0.7+0.1+0.85+0.6+0.2+0.75+0.5) / 9 = 5.4 / 9 = 0.600. One channel, one number. Do this for all 256 channels and you have the 256-dim tile descriptor. The "0.600" means "this pattern is strongly present, on average, across the tile" — location discarded on purpose, presence retained.

The simulation below runs this pipeline live. Click cells in the feature grid to toggle which "patterns" are firing (think: NIR-bright cells, built-up cells), and watch GAP collapse the grid to a value, a tiny linear layer produce logits over four land covers, and softmax produce probabilities. Paint a "forest signature" or a "residential signature" and see what it classifies as.

CNN pipeline: feature map → GAP → logits → softmax

Click cells in the feature grid to toggle which "patterns" are firing. Watch the global-average-pooled value, the logits over four land covers, and the final probabilities update. This is the whole CNN classifier in one picture.

Here is the same pipeline as code — first the multispectral first layer from scratch, then the library version, so you see they do the same thing:

python
import numpy as np

# tile: (C_in, H, W) multispectral stack, e.g. (13, 64, 64)
# the ONLY change vs an RGB model is C_in = 13 instead of 3
def global_average_pool(feature_map):
    return feature_map.mean(axis=(1, 2))   # (C,) one number per channel — location discarded

def softmax(z):
    z = z - z.max()                      # subtract max for numerical stability
    e = np.exp(z)
    return e / e.sum()

# W_fc: (K, C) classifier weights; b_fc: (K,) biases; K = number of land covers
def classify_tile(feature_map, W_fc, b_fc):
    v      = global_average_pool(feature_map)   # (C,) the tile descriptor
    logits = W_fc @ v + b_fc                     # (K,) raw class scores
    probs  = softmax(logits)                     # (K,) sum to 1 (single-label)
    return probs.argmax(), probs                 # top-1 index, full distribution
python
# Library version: take a standard ResNet and widen its first conv to 13 bands
import torch.nn as nn, torchvision.models as models
net = models.resnet18(num_classes=10)        # 10 EuroSAT classes
# swap the 3-channel stem for a 13-channel one — the whole multispectral trick:
net.conv1 = nn.Conv2d(13, 64, kernel_size=7, stride=2, padding=3, bias=False)
# resnet internally does conv blocks -> AdaptiveAvgPool2d(1) [== GAP] -> Linear
logits = net(tile_batch)                  # (B, 10)
probs  = logits.softmax(dim=1)            # same softmax we wrote by hand

See the correspondence: nn.Conv2d(13, …) is the multispectral input change, AdaptiveAvgPool2d(1) is our global_average_pool, nn.Linear is our W_fc @ v + b_fc. A plain ResNet-18 with a 13-band stem, trained from scratch, reaches well over 95% accuracy on EuroSAT — the task is genuinely learnable once you let the model see the spectral bands.

Misconception: "to use 13 bands I need a special exotic architecture." No — you change exactly one number: the input-channel count of the first convolution. The rest of a battle-tested CNN transfers directly. The subtle cost is that you can no longer naively reuse ImageNet-pretrained weights for that first layer (ImageNet is 3-channel), so the stem is trained fresh or its RGB weights are repeated/averaged into the extra bands. That single-layer mismatch — not the architecture — is the real friction of going multispectral.
To adapt a standard RGB CNN to classify 13-band Sentinel-2 tiles, what is the minimal structural change, and what does global average pooling at the end discard?

Chapter 4: Why It's Genuinely Hard

EuroSAT looks easy — 95%+ accuracy — but that number is a trap, because it comes from small, clean, single-label tiles. Real deployment is humbling, and three distinct forces make remote-sensing scene classification much harder than the toy benchmark suggests. Naming each one tells you what to engineer around.

Force 1: geographic intra-class variance. A "Residential" tile in suburban Germany — red-tile roofs, gardens, narrow streets — looks nothing like a "Residential" tile in Phoenix — flat tan roofs, pools, wide grids — or in Lagos, or rural India. The same class spans wildly different appearances across continents, climates, and building styles. A model trained on European tiles can collapse on African ones, not because it's weak but because it never saw that variety. The class concept is abstract; the pixels are parochial.

Force 2: resolution and ground sample distance. The key quantity is GSD, the Ground Sample Distance — how many metres of real ground each pixel covers. Sentinel-2's best bands are 10 m GSD: one pixel is a 10×10 m patch. At that scale, individual houses are a pixel or two and small features vanish into texture. Aerial imagery at sub-metre GSD shows individual cars. A model and a class definition that work at 10 m can be meaningless at 0.5 m and vice versa — "industrial" is a texture at coarse GSD and a collection of recognizable buildings at fine GSD. Resolution silently changes what each class even is.

Force 3: the multi-label reality. The single biggest gap between EuroSAT and the real world: a one-kilometre tile almost never contains one land cover. It contains forest and pasture and a road and some water. Forcing a single label is not just lossy — it makes the labels themselves inconsistent, because annotators must pick "the dominant one," and dominance is subjective. This is why BigEarthNet went multi-label, and it's the subject of Chapter 6.

The key insight that reframes the task: the gap between "95% on EuroSAT" and "this fails in the field" is mostly a distribution and label-structure gap, not a model gap. Geographic shift means your test distribution differs from training; GSD shift means your sensor differs; multi-label means your output format is wrong. None of these is fixed by a bigger network alone — they're fixed by representative data, matched resolution, and the right output layer. A model is only as general as the geography, the GSD, and the label structure it was trained under.

Watch the geographic-shift force directly. The widget shows a classifier's confidence as you "fly" it from its training region (Europe) to increasingly different regions. As the geographic distance grows, the same "Residential" tiles look less and less like training data, and accuracy slides — the classic distribution-shift collapse. This is the number that benchmark accuracy hides.

Geographic generalization — accuracy falls as you leave the training region

Drag the model away from its training geography. In-region accuracy is high; cross-continent accuracy collapses as the same classes take on unfamiliar appearances. This is why "95% on EuroSAT" doesn't transport.

Distance from training region 0.10

Let us put a number on the multi-label problem so it isn't just a feeling. Suppose a real tile genuinely contains, by area, 50% Forest, 30% Pasture, 15% Water, 5% Highway. A single-label dataset forces the annotator to write "Forest." Your softmax model, trained on such labels, learns to output "Forest" too — and is marked correct. But it has been actively trained to suppress Pasture, Water, and Highway, even though all three are truly present. The single-label format doesn't just lose information; it teaches the model to deny reality. Multi-label fixes this by scoring each cover independently, which we build in Chapter 6.

Misconception: "EuroSAT is 95%, so remote-sensing scene classification is basically solved." It is solved on small clean single-label European tiles at a fixed resolution — a narrow slice. Move the camera to another continent, change the GSD, or demand multi-label outputs on real one-kilometre tiles, and accuracy drops sharply. Benchmark saturation is not the same as deployment readiness; the hard part lives precisely in the geographic, resolution, and label-structure shifts that the easy benchmark holds fixed.
A model gets 96% on EuroSAT (trained and tested on European Sentinel-2 tiles) but 64% when applied to tiles from a different continent. The best single explanation is:

Chapter 5: Geospatial Foundation Models

Here is the problem the field bumped into: labelling satellite tiles is expensive (you need experts, or ground truth), so labelled data is scarce — but unlabelled satellite imagery is essentially infinite. Petabytes of Sentinel-2 pixels stream down every week, free, with no labels. The obvious move: learn from the unlabelled flood first, then fine-tune on the small labelled set. That move is self-supervised pretraining, and it produced the geospatial foundation models that now define the state of the art.

The dominant recipe is the masked autoencoder (MAE). The idea is gorgeous in its simplicity: take a tile, hide a large fraction of it (mask out, say, 70% of the patches), feed the network only the visible part, and train it to reconstruct the hidden pixels. No labels needed — the supervision is the image itself. To fill in a masked patch correctly, the network is forced to learn what land covers look like, how they're spatially arranged, and how the spectral bands relate. Those learned representations then transfer beautifully to classification.

Three geospatial foundation models are worth knowing by name, each adapting MAE to a different axis of satellite data:

ModelCore ideaWhat it adds beyond plain MAE
SatMAEMAE on satellite imageryMasks across spectral bands and across time, not just space — learns from the multispectral, multi-temporal structure
SpectralGPTMAE built for the spectral dimensionTreats the spectrum as a first-class axis: masks 3D spectral-spatial cubes so the model learns band relationships explicitly
PrithviNASA/IBM geospatial MAEPretrained on large-scale Sentinel/HLS data; a temporal ViT MAE released as an open foundation model for Earth observation

What makes these geospatial rather than generic MAEs is that they mask along axes a normal image model doesn't have. A photo MAE masks spatial patches. SatMAE additionally masks entire spectral bands (predict the NIR band from the others) and entire time steps (predict July's tile from May's). SpectralGPT goes further, treating the spectrum as a dimension to be masked and reconstructed in 3D cubes. Prithvi, from NASA and IBM, is a temporal Vision Transformer MAE pretrained on massive Sentinel/Harmonized-Landsat-Sentinel data and released openly. The unifying theme: exploit the structure satellites give you for free — many bands, repeated passes — as the pretraining signal.

Why masking 70% works, made concrete. Reconstructing a masked patch is only possible if you understand the surrounding context. Mask the centre of a forest tile and the network can only fill it correctly by having internalized "forest = dense, NIR-bright, fine green texture." Mask part of a river and it must know rivers are smooth, dark, NIR-absorbing ribbons. The harder you make reconstruction (more masking, masking across bands/time), the richer the representation it forces — up to a point. The masked-prediction task is a pretext: nobody cares about the reconstructed pixels; we care about the representation the network had to build to produce them.

Once pretrained, a geospatial foundation model is used through the same three modes as any foundation model — and the choice is a real engineering decision driven by how many labels you have:

ModeWhat you trainWhen to use it
Linear probeA small linear head on frozen featuresFew labels (<~10K); want strong results cheaply & fast
Fine-tuneThe whole backboneLots of labels; chasing the last few points of accuracy
Few-shotTiny head on a handful of examplesA brand-new region/class with almost no labels

The widget below sketches the payoff that makes pretraining worth the trouble: how accuracy scales with the number of labelled tiles, for a model trained from scratch versus a pretrained foundation model that's only fine-tuned. The pretrained curve starts far higher and stays ahead — especially in the low-label regime, which is exactly where real deployments live (you rarely have a million labels for your specific region). Drag the label budget and watch the gap.

Pretraining pays off most when labels are scarce

Drag the labelled-tile budget. The from-scratch model needs many labels to get good; the pretrained foundation model is strong even with few labels. The gap at the left (few labels) is the whole argument for self-supervised pretraining.

Labelled tiles (log scale) 0.25
Misconception: "a geospatial foundation model is just an ImageNet model fine-tuned on satellites." No — the whole point is that it's pretrained on satellite data with satellite-specific masking (spectral and temporal), so it already understands 13 bands and multi-date sequences. An ImageNet model only knows 3 RGB channels of ground-level photos; it has never seen NIR or a one-kilometre overhead tile. Starting from a domain-matched foundation model is why a few hundred labels can now beat a from-scratch CNN trained on tens of thousands.
A masked autoencoder for satellite imagery hides 70% of a tile and trains the network to reconstruct it. Why does this produce useful features without any labels?

Chapter 6: Multi-Label — A Tile Is Many Covers

This is the chapter the whole lesson has been building toward, because it's where the machinery genuinely changes. We've said a real one-kilometre tile contains several land covers at once. Now we make the model honor that, and it requires abandoning the most reflexive habit in classification: softmax.

Recall what softmax does: it forces the class probabilities to sum to 1. That is a statement — "exactly one class is the answer; raising one probability must lower the others." For a tile that is genuinely Forest and Pasture and Water, that constraint is a lie. If Forest's probability goes up, softmax mechanically pushes Water's down, even though both are truly present. Softmax models competition between classes. Multi-label land cover is not a competition.

The fix is to score each land cover independently with a sigmoid. A sigmoid squashes a single logit into [0,1] on its own, with no coupling to the other classes. So instead of one softmax over K classes, you run K separate sigmoids — each answering "is this cover present? yes/no" — and the outputs need not sum to 1. A tile can be 0.9 Forest, 0.8 Pasture, 0.7 Water simultaneously. The training loss changes to match: binary cross-entropy applied per class (each cover is its own little present/absent classifier), replacing the single cross-entropy over a softmax.

sigmoid(z) = 1 / (1 + e−z)  —  applied to each class logit, independently

Read the contrast carefully. Softmax: P(class k) = ezk / Σj ezj — coupled, sums to 1, "pick one." Sigmoid: P(class k present) = 1 / (1 + e−zk) — independent, each in [0,1], "each present or not." This single swap is the entire multi-label shift.

Sigmoid vs softmax on the same logits, by hand. Suppose three covers have logits Forest = 2.0, Pasture = 1.0, Water = 0.5.
Softmax (forces sum to 1): e2.0≈7.39, e1.0≈2.72, e0.5≈1.65, sum≈11.76. P(Forest)≈0.629, P(Pasture)≈0.231, P(Water)≈0.140 — raising Forest forced the others down; sum = 1.
Sigmoid (each independent): σ(2.0)=1/(1+e−2.0)=1/(1+0.135)=0.881; σ(1.0)=1/(1+0.368)=0.731; σ(0.5)=1/(1+0.607)=0.622. Sum≈2.23, not 1 — all three can be "present" at once. Same logits, two completely different stories about reality.

With sigmoids, prediction has one more knob: a per-class threshold. Each cover is "predicted present" if its sigmoid output exceeds a threshold — commonly 0.5, but tunable per class. This matters because covers differ in prevalence: a rare cover (say "Beaches") may need a lower threshold to be caught at all, while a common one ("Pastures") can afford a higher one. Tuning thresholds per class is how you trade off catching every instance (recall) against avoiding false alarms (precision) for each land cover separately — impossible under a single softmax argmax.

The widget below shows it live. You set three logits and toggle between softmax and sigmoid. Under softmax, the three bars are chained — pushing one up drags the others down, and exactly one "wins." Under sigmoid, each bar moves on its own, and you draw a threshold line that decides the predicted set. This is the clearest way to feel why multi-label needs sigmoid.

Softmax (pick one) vs sigmoid (each present or not)

Set three cover logits and toggle the output mode. Softmax couples the bars and sums to 1 (one winner). Sigmoid frees each bar; the threshold line picks the predicted SET. Watch the same logits tell two different stories.

Forest logit2.0
Pasture logit1.0
Water logit0.5
Threshold (sigmoid only)0.50
Misconception: "I'll just take the top-3 softmax classes to get multiple labels." Tempting but wrong. Softmax's probabilities are coupled — they were trained to compete — so the 2nd and 3rd entries are artificially suppressed by the winner and don't mean "present." Worse, "top-3" forces exactly three labels whether the tile has one cover or five. True multi-label needs independent sigmoid scores plus per-class thresholds so the number of predicted covers can be anything from zero to all. The output layer, not a post-hoc trick, is what makes it multi-label.
A BigEarthNet tile genuinely contains Coniferous forest, Pastures, and Water bodies. Which output design correctly captures this, and why is softmax wrong?

Chapter 7: ShowcaseA Live Multispectral Tile Classifier

Everything converges here. This is a working multi-label land-cover classifier you can drive and, more importantly, break. You set the tile's spectral character with sliders — near-infrared brightness, redness, water-darkness, the built-up texture, and an overall haze/noise knob — and the model computes a logit for each of five covers, then sigmoids them into independent presence probabilities. A threshold line decides the predicted set. The bars are the live per-cover probabilities; the panel shows which covers cross the threshold.

The five covers — Forest, Pasture, Water, Residential, Highway — each respond to the spectral sliders the way real surfaces do. Forest loves high NIR and a vegetation index; Water loves low NIR and darkness; Residential loves the built-up texture; Highway loves a thin built-up streak with low vegetation. No single slider decides a cover — the combination of spectral cues does, exactly as Chapters 2 and 3 argued.

Three experiments, each illustrating a chapter:

Make a clean forest (high NIR, low red, low water-dark, low built-up). Watch Forest's bar dominate and cross the threshold alone — the easy, separable case from Chapter 3.
Now raise built-up texture and keep NIR high. Watch Forest and Residential both cross the threshold — you've manufactured the multi-label reality of Chapter 6, live: a tile that is genuinely two things at once, which softmax could never express.
Crank the haze/noise slider. This blurs the spectral signal the way clouds, shadow, or a hard tile would. Confident covers drift toward the threshold and the predicted set becomes unstable — you're watching the geographic/atmospheric difficulty of Chapter 4 erode the decision in real time.

Live tile classifier — set the spectral cues, read the multi-label set, break it with haze

Set the spectral sliders, then push haze up to watch a clean call collapse. The right panel shows the predicted SET (every cover above the threshold) the way a multi-label system reads it — not a single winner.

NIR brightness (vegetation)0.80
Redness0.10
Water darkness (low NIR)0.05
Built-up texture0.05
Threshold0.50
Haze / noise (tile difficulty)0.00
What you just proved to yourself: a remote-sensing classifier is multispectral features → per-cover logits → sigmoid → threshold into a set, exactly as in Chapters 3 and 6 — and its predictions are bounded not only by the model but by how cleanly the spectral cues separate the covers. When two covers genuinely co-occur (forest + residential), the multi-label output expresses both; when haze blurs the spectrum, the set wobbles. That is the whole story of Chapters 2–6 in one interactive panel.

If you removed this simulation, would you lose understanding? Yes — reading "multi-label means several covers can be present" is abstract until you watch Forest and Residential both light up by sliding one texture knob, with no winner forced. The napkin drawing of this whole lesson is exactly this: spectral cues in, an independent probability per cover out, a threshold drawing the predicted set, haze pulling it toward chaos.

Chapter 8: Evaluation — Scoring Multi-Label Honestly

You understand the models; here is how to judge them without being fooled — and multi-label evaluation has its own culture, because plain accuracy quietly lies. Three ideas cover most of it: why accuracy fails, mean average precision (mAP), and geographic generalization.

Why "accuracy" breaks for multi-label. With single-label tiles you compute accuracy = fraction predicted exactly right. But with 19 independent covers, you could score "accuracy" as fraction of the 19 yes/no decisions you got right — and that number is misleadingly high, because most covers are absent on any given tile. If a tile has 3 of 19 covers and your model predicts "all absent," it's wrong on only 3 of 19 = 84% "accurate" while having found nothing. The class imbalance (mostly-absent labels) makes accuracy a useless flatterer. We need a metric that focuses on getting the present covers right.

Mean Average Precision (mAP). The standard multi-label metric. For each class separately, you rank all tiles by that class's sigmoid score, then compute Average Precision (AP) — the area under that class's precision-recall curve, which rewards putting the truly-present tiles at the top of the ranking. Then you average AP across all classes to get mAP. Because it's computed per class and averaged, mAP treats a rare cover as seriously as a common one, and because it's threshold-free (it scores the whole ranking), it isn't fooled by a single bad threshold choice. BigEarthNet leaderboards report mAP precisely for these reasons.

Average Precision, worked by hand for one class. Rank 5 tiles by the model's "Water" score; the truth (present=✓) in rank order is: ✓, ✓, ✗, ✓, ✗. Precision is computed at each correct hit, as (correct-so-far / rank-so-far):
• rank 1, hit ✓: precision = 1/1 = 1.00
• rank 2, hit ✓: precision = 2/2 = 1.00
• rank 3, miss: (not counted)
• rank 4, hit ✓: precision = 3/4 = 0.75
AP = average of the precisions at the hits = (1.00 + 1.00 + 0.75) / 3 = 0.917. A perfect ranking (✓✓✓✗✗) would give AP = 1.00. Average this AP across all classes → mAP.

The same metric in code, computed the way a benchmark script does — first the per-class AP from scratch, then the one-liner:

python
import numpy as np

# scores: (N,) model sigmoid output for one class; truth: (N,) 1 if present else 0
def average_precision(scores, truth):
    order = np.argsort(-scores)            # rank tiles best-first by this class's score
    t = truth[order]
    hits = np.cumsum(t)                    # correct-so-far at each rank
    ranks = np.arange(1, len(t) + 1)     # 1,2,3,...
    precision_at_rank = hits / ranks       # precision at every position
    return (precision_at_rank * t).sum() / max(t.sum(), 1)  # average precision AT the hits

# mAP = mean of per-class AP over all K classes
def mean_ap(scores_KxN, truth_KxN):
    return np.mean([average_precision(scores_KxN[k], truth_KxN[k]) for k in range(len(scores_KxN))])

# sklearn one-liner equivalent (micro/macro averaged):
#   from sklearn.metrics import average_precision_score
#   mAP = average_precision_score(truth_NxK, scores_NxK, average='macro')

Notice the ranking, not a threshold, drives AP — that's why mAP is robust to a poorly chosen cutoff. Geographic generalization is the silent killer, again. Just as in Chapter 4, the honest question is not "what's the mAP on the held-out split?" but "what's the mAP on tiles from a region the model never trained on?" A model can post 90% mAP on a random European split and far less on an African or Asian split. Serious remote-sensing evaluation reports cross-region or geographically-disjoint splits, where training and test tiles come from different continents, because that's what deployment actually demands. An in-distribution number is an optimistic ceiling.

The interactive panel below is a small multi-label scoreboard. Each row is a tile; each column a cover; green = truly present, and you toggle the model's predictions to see precision, recall, and per-class AP respond. Make the model predict "all absent" and watch accuracy stay high while recall collapses — the exact trap that makes accuracy useless here.

Multi-label scoreboard — why accuracy lies and recall matters

Tap cells to toggle the model's predictions (blue outline). Green = truly present. Watch precision, recall, and AP update. Try predicting nothing: "accuracy" stays high (most covers are absent) while recall → 0 — that's the trap mAP avoids.

Misconception: "high accuracy means the multi-label model is good." Almost meaningless here. Because most covers are absent on most tiles, a model predicting "nothing present" can score very high accuracy while finding zero land cover — useless. Always report mAP (which scores the ranking of present covers per class) and inspect per-class AP for the rare covers, and always evaluate on a geographically-disjoint split. Single-number accuracy on an in-region split is the most flattering and least informative number you can quote.
For one class, tiles ranked by the model's score have truth (in rank order): ✓, ✗, ✓, ✓, ✗. Computing precision at each hit, what is the Average Precision?

Chapter 9: Connections & Where It Lives

Remote-sensing scene classification is a hub, not an island. The exact pattern you built — read a whole tile, summarize it into a descriptor, score a fixed taxonomy, and accept that the world is multi-label — recurs across vision, audio, and 3D sensing. Naming those connections turns one lesson into a mental map.

The scene-classification family. This lesson is one sibling of a larger family that all ask "what is this whole signal?" Ordinary Visual Scene Classification names "kitchen" or "beach" in a ground-level photo — same holistic-descriptor machinery, different viewpoint. Acoustic Scene Classification asks "is this audio a park, a metro, an airport?" — whole-signal classification over a spectrogram. RGB-D scene classification adds a depth channel that sharply helps indoor layout. Each is "this lesson, new sensor": the descriptor-then-classify skeleton is shared; what changes is the input modality and what "a class" means.

The multispectral input is the distinctive twist here, and it connects to the broader idea of feeding non-RGB channels through a CNN — the same first-layer-widening trick you'd use for depth, thermal, or hyperspectral. The masked-autoencoder pretraining of SatMAE/SpectralGPT/Prithvi is the satellite cousin of the self-supervised pretraining you'd recognize from Vision Transformers and masked-image modelling generally. And the zero-shot branch of foundation models runs through Contrastive CLIP: remote-sensing CLIP variants (like RemoteCLIP) let you classify a tile by writing "a satellite photo of a forest" — no labels, editable taxonomy.

ApproachInputLabel structureBest when
Multispectral CNN (ResNet, widened stem)13-band tileSingle-label (softmax)Clean datasets like EuroSAT; strong fast baseline (95%+)
Multi-label CNN (sigmoid + BCE)13-band tileMulti-label (per-class sigmoid)Realistic tiles like BigEarthNet with co-occurring covers
Geospatial foundation model (SatMAE, SpectralGPT, Prithvi)Multispectral + temporalEither, via fine-tune/probeScarce labels; want SOTA and cross-region transfer
Remote-sensing CLIP (zero-shot)Tile + text promptsOpen / editableNo labels; new or changing taxonomy; quick prototype
The one idea to carry out the door: remote-sensing scene classification is ordinary scene classification with three twists — an invisible-band input (multispectral, NIR, NDVI), a bird's-eye viewpoint that destroys object cues and demands holistic reading, and a multi-label reality that swaps softmax for sigmoid. Master those three twists, and you can read the land from above the way the planet is actually mapped.

Cheat sheet

ThingWhat to remember
TaskLabel an overhead tile by land use / land cover from a fixed taxonomy
DatasetsEuroSAT (10, single-label), NWPU-RESISC45 (45, aerial RGB), BigEarthNet (19/43, multi-label)
InputMultispectral: Sentinel-2 has 13 bands incl. NIR; NDVI = (NIR−RED)/(NIR+RED)
CNN baselineWiden first conv to 13 channels → conv blocks → GAP → linear → softmax
Why hardGeographic intra-class variance + resolution/GSD shift + multi-label tiles
Foundation modelsSatMAE / SpectralGPT / Prithvi: masked autoencoding over spectral + temporal data, no labels
Multi-labelA tile is many covers → per-class sigmoid + BCE + per-class threshold, NOT softmax
EvaluationReport mAP (per-class AP averaged), not accuracy; use geographically-disjoint splits

"We shall not cease from exploration, and the end of all our exploring will be to arrive where we started and know the place for the first time." — T. S. Eliot. Remote sensing is the discipline of knowing a place — from a viewpoint no human ever had.