Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

GIS & Geospatial Data (QGIS, GeoPandas, PostGIS)  Module 10 – Spatial analysis and statistics

Machine learning with spatial data

Build land-cover features, validate classifiers with spatial blocks, not random folds, read accuracy class by class and estimate areas without pixel counts.

  • Advanced
  • 20 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 7 of 20 minutes free
Free: A pixel is described by its bands, its neighbours and its place · Random folds test memory, spatial blocks test the map. With a pass: 3 in-depth parts and 3 interview answers, about 13 minutes.

What you will learn

  • Build features from bands, neighbourhoods and distances for a land-cover classifier
  • Explain why random cross-validation overstates accuracy for spatial data, with a measured example
  • Validate with spatial blocks and report a confusion matrix with producer's and user's accuracy
  • Estimate class areas from a classified map with a reference sample instead of pixel counts

Before you start

What you’ll learn · Before you start

What you will learn

  • Build features from bands, neighbourhoods and distances for a land-cover classifier
  • Explain why random cross-validation overstates accuracy for spatial data, with a measured example
  • Validate with spatial blocks and report a confusion matrix with producer's and user's accuracy
  • Estimate class areas from a classified map with a reference sample instead of pixel counts

Before you start

On this page

A classifier that knows nothing about a pixel except where it is scores 0.891 in random cross-validation on this lesson’s land-cover scene, and 0.561 when whole blocks of the scene are held out, barely above the 0.494 of always answering “crop”. The first number measures memory; the second, what the map is worth on ground the model has not seen.

This lesson builds features for a land-cover classifier, shows why spatial data need spatial validation, and names what a credible map reports: accuracy class by class, and areas estimated from a reference sample.

A pixel is described by its bands, its neighbours and its place

The scene is made up so that the truth is known everywhere: 90 by 60 pixels of 20 m, patches of crop, forest and built-up land, and a river. Each pixel’s reflectance mixes its class’s values, a shift shared by its whole patch, a light haze and noise. 1,200 reference pixels play the field survey:

A land-cover map of patches of crop, forest and built-up land with a river across it, divided by dashed lines into 24 blocks.The scene, and its 300 m blocks90 by 60 pixels of 20 m; 1.8 km by 1.2 kmcropforestbuilt-upwatera block of 300 m, held out whole

The lesson's land-cover scene and its 300 m blocks

Text description of the diagram

The scene is 1.8 km wide and 1.2 km tall, 90 by 60 pixels of 20 m, with north at the top. A river winds across it from west to east, a little above the middle, drawn dark blue. The rest is made of irregular patches: crop, the most common class, is white; forest is light grey; built-up land is dark grey. A patch is many pixels of one class, so neighbouring pixels usually share their class.

Dashed lines divide the scene into 24 square blocks of 300 m, 6 across and 4 down. Spatial cross-validation puts each block whole into one of five folds, so a test pixel's neighbours in its own block are never in the training data. Most blocks hold two or three of the four classes.

scene.py: the made-up scene and its 1,200 reference pixels Python · scene/scene.py
"""A made-up land-cover scene of 90 by 60 pixels of 20 m (1.8 km by 1.2 km), from a seeded generator: patches of crop,
forest and built-up land, and a river. Each pixel has red, near-infrared and shortwave-infrared reflectance: the
class's own values, a shift shared by its whole patch (soil, crop variety, roof material), a gentle haze across the
scene, and noise. Then 1,200 reference pixels whose true class is known, as from a field survey."""
import math
import random

COLS, ROWS, PIXEL = 90, 60, 20
CLASSES = ["water", "crop", "forest", "built-up"]
MEAN = {  # reflectance in the red, near-infrared and shortwave-infrared bands
    "water": (0.04, 0.03, 0.02),
    "crop": (0.06, 0.34, 0.20),
    "forest": (0.04, 0.27, 0.13),
    "built-up": (0.15, 0.22, 0.24),
}


def river_y(x):
    """The river's centre line, in pixels, at column x."""
    return 34 + 9 * math.sin(x / 14)


def build(seed=21):
    """(classes, bands, patch) for every pixel, row by row; bands are (red, nir, swir)."""
    rng = random.Random(seed)
    centres = [(rng.uniform(0, COLS), rng.uniform(0, ROWS), rng.choices(CLASSES[1:], weights=(5, 3, 2))[0]) for _ in range(45)]
    shift = [tuple(rng.gauss(0, 0.025) for _ in range(3)) for _ in centres]
    classes, bands, patch = [], [], []
    for r in range(ROWS):
        for c in range(COLS):
            if abs(r - river_y(c)) < 2.2:
                k, p = "water", -1
            else:
                p = min(range(len(centres)), key=lambda i: (c - centres[i][0]) ** 2 + (r - centres[i][1]) ** 2)
                k = centres[p][2]
            haze = 0.02 * c / COLS
            offset = shift[p] if p >= 0 else (0, 0, 0)
            pixel = tuple(max(0.0, MEAN[k][b] + offset[b] + haze + rng.gauss(0, 0.02)) for b in range(3))
            classes.append(k)
            bands.append(pixel)
            patch.append(p)
    return classes, bands, patch


def reference(n=1_200, seed=5):
    """Pixel numbers of n reference pixels drawn at random."""
    return random.Random(seed).sample(range(COLS * ROWS), n)

A classifier sees each pixel as a row of features, and they come in four kinds:

Kind In this lesson What it tells the model
Bands red, near-infrared, shortwave infrared what the surface reflects
Index NDVI (normalised difference vegetation index): (NIR − red) ÷ (NIR + red), from −1 to 1: negative over water and rising with green leaves, which absorb red light and reflect near-infrared strongly. More in “Map algebra, NDVI and reclassification” from red and near-infrared how green it is, as in map algebra
Neighbourhood mean NDVI of the 3 × 3 pixels around it what surrounds it
Place distance to the river; x and y where it is

Place features are the dangerous kind: they let a model learn where each class lay in the training area, which no new area shares. The classifier here is k-nearest neighbours: A classifier that labels a sample with the majority class of the k training samples nearest to it in feature space, after the features are put on a common scale., which labels a pixel with the majority class of the five training pixels nearest in feature space, after every feature is scaled to the same spread:

features.py: features, k-nearest neighbours and two kinds of folds Python · scene/features.py
"""Features of the scene's pixels, a k-nearest-neighbour classifier, and two ways to split reference pixels into five
folds: at random, and by spatial blocks, so that a whole block is either trained on or tested."""
import heapq
import math
import random

from scene import COLS, PIXEL, ROWS, river_y


def ndvi(band):
    red, nir, _ = band
    return (nir - red) / (nir + red) if nir + red > 0 else 0.0


def table(bands):
    """For every pixel: its bands, its NDVI, the mean NDVI of its 3 x 3 neighbourhood, its distance to the river in
    metres, and its x and y in metres."""
    v = [ndvi(b) for b in bands]
    rows = []
    for i, b in enumerate(bands):
        r, c = divmod(i, COLS)
        near = [v[(r + dr) * COLS + c + dc] for dr in (-1, 0, 1) for dc in (-1, 0, 1) if 0 <= r + dr < ROWS and 0 <= c + dc < COLS]
        rows.append({"red": b[0], "nir": b[1], "swir": b[2], "ndvi": v[i], "ndvi 3x3": sum(near) / len(near),
                     "river m": abs(r - river_y(c)) * PIXEL, "x": c * PIXEL, "y": r * PIXEL})
    return rows


def scaled(rows, names):
    """The chosen features of each row, each scaled to mean 0 and standard deviation 1 over the rows."""
    cols = [[row[n] for row in rows] for n in names]
    stats = [(sum(col) / len(col), math.sqrt(sum((v - sum(col) / len(col)) ** 2 for v in col) / len(col)) or 1.0) for col in cols]
    return [tuple((row[n] - m) / s for n, (m, s) in zip(names, stats)) for row in rows]


def knn(train, labels, test, k=5):
    """The majority class of the k nearest training rows of each test row (ties: the class first in alphabetical order)."""
    pairs = list(zip(train, labels))
    out = []
    for x in test:
        votes = {}
        for _, label in heapq.nsmallest(k, ((math.dist(x, t), lab) for t, lab in pairs)):
            votes[label] = votes.get(label, 0) + 1
        out.append(max(sorted(votes), key=lambda lab: votes[lab]))
    return out


def random_folds(n, k=5, seed=1):
    order = list(range(n))
    random.Random(seed).shuffle(order)
    return [order[f::k] for f in range(k)]


def block_folds(pixels, k=5, size=15, seed=1):
    """Folds of whole blocks of size x size pixels: every block goes to one fold, the blocks dealt out at random."""
    blocks = {}
    for j, p in enumerate(pixels):
        r, c = divmod(p, COLS)
        blocks.setdefault((c // size, r // size), []).append(j)
    keys = sorted(blocks)
    random.Random(seed).shuffle(keys)
    folds = [[] for _ in range(k)]
    for n, key in enumerate(keys):
        folds[n % k] += blocks[key]
    return folds


def cross_validate(x, y, folds, k=5):
    """The predicted class of every sample, each from a model trained on the other folds."""
    predicted = [None] * len(y)
    for fold in folds:
        held = set(fold)
        train = [j for j in range(len(y)) if j not in held]
        for j, p in zip(fold, knn([x[t] for t in train], [y[t] for t in train], [x[j] for j in fold], k)):
            predicted[j] = p
    return predicted

Checkpoint

Check: Features from bands, neighbours and place

  1. Question 1 of 2 Which two bands does NDVI combine?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Red and near-infrared

    NDVI, from the map-algebra lesson, contrasts the near-infrared that vegetation reflects with the red it absorbs, so crop and forest score high and water and built-up land low.

  2. Question 2 of 2 Which of these features tell a model where a pixel is, rather than what it looks like?

    Choose every answer that is right.

    Show the answer: Question 2 of 2

    Answer:

    • The distance from the pixel to the river
    • The pixel's x and y

    Coordinates and distances to fixed features both encode location. They can be useful, but they let a model learn where classes lie in the training area, which does not carry over to a new area.

Random folds test memory, spatial blocks test the map

Random five-fold cross-validation holds out a random fifth of the reference pixels in turn. But nearby pixels share their class, so each held-out pixel still has neighbours of its own class in training, and a model can copy them. spatial block cross-validation: Cross-validation that holds out whole blocks of space at a time, so that the samples tested are never next to samples the model was trained on. Blocks should be wider than the distance over which places stay alike. holds out whole blocks instead. Step through it:

Interactive · stage by stage Cross-validation by spatial blocks, step by step Step through how whole blocks become folds, and why a model that knows only where a pixel is fails the test.
Cross-validation by spatial blocks, step by step, step 6 of 6. Showing Cut the scene into blocks, Deal blocks into 5 folds, Train on 4 folds, Predict the 5th fold, Score every prediction (0.891 random, 0.561 blocks). In focus: Score every prediction.the next foldCut the scene into blocks24 blocks of 300 mDeal blocks into 5 foldsa whole block per foldTrain on 4 foldsabout 960 reference pixelsPredict the 5th foldits blocks never seenScore every prediction0.891 random, 0.561 blocks

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Blocks. The scene is cut into 24 square blocks of 300 m. Each of the 1,200 reference pixels belongs to one block.
  2. Folds. The blocks are shuffled with a fixed seed and dealt into five folds, so every block, with all its pixels, sits in exactly one fold.
  3. Training. A model is trained on the reference pixels of four folds. The fifth fold's blocks, and every pixel in them, stay out of it.
  4. Testing. The model predicts the fifth fold. A test pixel's nearest neighbours in its own patch are mostly in its own block, so the model cannot copy their class.
  5. Every fold. The same is done with each fold held out in turn, so every reference pixel is predicted once by a model that never saw its block.
  6. The score. A model that knows only x and y scored 0.891 with random folds, which leave a pixel's neighbours in training, and 0.561 with blocks, barely above the 0.494 of always answering crop.

A study of structured data found block cross-validation “nearly universally more appropriate than random cross-validation” when the goal is to predict new data (Roberts and others, Ecography). In scikit-learn, give each sample its block’s id and split with GroupKFold, which never lets one group sit in both the training and the test set of a split (scikit-learn). The program tests the classifier that knows only x and y:

A classifier that knows only where a pixel is Python · scene/where_only.py
"""A classifier that knows only where a pixel is: k-nearest neighbours on x and y alone, tested by random five-fold
cross-validation and by five folds of spatial blocks 300 m wide. The pixels' bands are not used at all."""
from features import block_folds, cross_validate, random_folds, scaled, table
from scene import CLASSES, build, reference

classes, bands, _ = build()
rows = table(bands)
pixels = reference()
x = scaled([rows[p] for p in pixels], ["x", "y"])
y = [classes[p] for p in pixels]
print(f"{len(pixels):,} reference pixels: " + ", ".join(f"{c} {y.count(c)}" for c in CLASSES))
print(f"  always the commonest class, crop: accuracy {y.count('crop') / len(y):.3f}")
for name, folds in (("random folds", random_folds(len(y))), ("blocks of 300 m", block_folds(pixels))):
    predicted = cross_validate(x, y, folds)
    print(f"  {name:<16} accuracy {sum(p == t for p, t in zip(predicted, y)) / len(y):.3f}")

Output

1,200 reference pixels: water 103, crop 593, forest 277, built-up 227
  always the commonest class, crop: accuracy 0.494
  random folds     accuracy 0.891
  blocks of 300 m  accuracy 0.561

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 where_only.py

Random folds gave it 0.891; blocks of 300 m, 0.561. Three more choices decide whether a map’s numbers can be trusted. The block size comes from how far apart pixels must be before their classes are no more alike than chance; two variants hold out one sample with a buffer of that width around it, or a whole region, as if it had no reference data. A confusion matrix: A table of a classifier's results that counts, for every pair of classes, the samples predicted as one class whose true class is the other. Its diagonal holds the right answers., which counts every pair of predicted and true class, reports each class’s producer's accuracy: The share of the samples truly in a class that a map or classifier labels as that class: how much of the real class it finds., the share of its true pixels the map finds, and its user's accuracy: The share of the samples a map or classifier labels as a class that truly belong to it: how often the label is right when a user reads it., the share of the pixels mapped as it that are right. And class areas come from a reference sample, because counting a map’s pixels inherits its errors. Real products publish such checks: ESA’s global 10 m WorldCover maps report overall accuracies of 74.4 % and 76.7 % from independent validation (ESA WorldCover).

Checkpoint

Check: Random folds against spatial blocks

  1. Question 1 of 2 A model that knows only x and y scored 0.891 with random folds and 0.561 with spatial blocks. By how much did random folds overstate its accuracy?

    Type a number.

    Show the answer: Question 1 of 2

    Answer: 0.33 accuracy (anything from 0.3295 to 0.3305 counts)

    0.891 − 0.561 = 0.330, a third of all the pixels. Random folds left each test pixel's neighbours in training, and neighbours share their class; blocks removed them.

  2. Question 2 of 2 Always answering crop, the commonest class, scores 0.494. What does the block score of 0.561 for x and y alone say?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Away from its training pixels, place alone predicts little more than guessing the commonest class

    A score means little without a baseline. Against the 0.494 of always guessing crop, place alone adds almost nothing once the test pixels are kept apart from the training pixels.

Spatial validation measured: six feature sets, block sizes and the range Pro: opens with a Pro, Premium or Ultimate pass

Random folds do more than inflate a score: they choose the wrong features. This part tests six feature sets both ways, measures how far apart pixels must be to stop sharing their class, and watches accuracy fall as the blocks grow.

In this part:

  • Six feature sets, two kinds of folds
  • How far apart pixels must be: the range of the classes
  • Accuracy as the blocks grow
  • Building the folds in practice

2 runnable examples (Python) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

The confusion matrix, worked: producer's and user's accuracy Pro: opens with a Pro, Premium or Ultimate pass

One overall accuracy hides which classes the map gets wrong. This part builds the confusion matrix of the bands-and-NDVI classifier from its spatial blocks and reads it class by class.

In this part:

  • The confusion matrix of the spatial blocks, worked
  • Reading it down and across
  • What the matrix says to do next
  • What a credible accuracy report holds

1 runnable example (Python) · about 3 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Class areas adjusted for the map's errors, with intervals Pro: opens with a Pro, Premium or Ultimate pass

A map’s pixel counts are areas with the map’s errors built in. This part maps the whole scene, draws an independent sample in each mapped class, and estimates every class’s area with a confidence interval.

In this part:

  • Pixel counts against areas adjusted for the map’s errors
  • How the estimator works
  • One sample is one draw

1 runnable example (Python) · about 4 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Practise: blocks and accuracy

Write the two validation steps of this lesson: folds of whole spatial blocks, and producer’s and user’s accuracy for every class. The sample tests check that a block never splits across folds, that the same seed deals the same folds, and that a class the model never predicts gets no user’s accuracy:

Exercise · Medium · Python

Folds of spatial blocks, and accuracy class by class

Write two functions in validate.py.

block_folds(points, size, k=5, seed=1) takes points, a list of (x, y) positions in metres, and returns k folds, each a list of indices into points. A point belongs to the block (int(x // size), int(y // size)). Every block goes whole into one fold: sort the block keys, shuffle them with random.Random(seed).shuffle, and deal them out in turn, the first block to fold 0, the second to fold 1 and so on, adding each block's indices to its fold in the order the points come.

accuracies(predicted, truth) takes two lists of class names of the same length and returns a dictionary with an entry for every class that appears in either list, in alphabetical order: a pair (producer's accuracy, user's accuracy). The producer's accuracy of a class is the share of its true cases predicted as that class; the user's accuracy, the share of its predictions that are right. Round each to 3 decimals, and give None for a share whose denominator is 0.

Starter code · validate.py

import random


def block_folds(points, size, k=5, seed=1):
    """k folds of point indices, each block of size x size metres whole in one fold (see the prompt)."""
    return [[] for _ in range(k)]


def accuracies(predicted, truth):
    """{class: (producer's accuracy, user's accuracy)} (see the prompt)."""
    return {}
The sample tests · test_validate.py
from validate import accuracies, block_folds

POINTS = [(10, 10), (20, 30), (150, 20), (160, 40), (310, 310), (20, 260), (260, 20), (390, 390)]


def test_every_point_once():
    """puts every point in exactly one of the k folds"""
    folds = block_folds(POINTS, 100, k=3)
    assert len(folds) == 3
    assert sorted(i for fold in folds for i in fold) == list(range(len(POINTS)))


def test_a_block_stays_together():
    """keeps the points of one block in one fold"""
    folds = block_folds(POINTS, 100, k=3)
    fold_of = {i: n for n, fold in enumerate(folds) for i in fold}
    assert fold_of[0] == fold_of[1]  # both in the block (0, 0)
    assert fold_of[2] == fold_of[3]  # both in (1, 0)
    assert fold_of[4] == fold_of[7]  # both in (3, 3)


def test_the_blocks_are_dealt_from_a_seeded_shuffle():
    """deals the shuffled blocks in turn, so the same seed gives the same folds"""
    assert block_folds(POINTS, 100, k=3, seed=1) == [[2, 3, 0, 1], [6, 5], [4, 7]]
    assert block_folds(POINTS, 100, k=3, seed=2) == [[2, 3, 4, 7], [5, 0, 1], [6]]


def test_accuracies_by_class():
    """gives each class its producer's and user's accuracy"""
    result = accuracies(["A", "A", "B", "B", "C", "C"], ["A", "A", "A", "B", "B", "C"])
    assert result == {"A": (0.667, 1.0), "B": (0.5, 0.5), "C": (1.0, 0.5)}


def test_forest_and_crop():
    """reads producer's accuracy down the true class and user's accuracy across the predicted one"""
    truth = ["forest"] * 40 + ["crop"] * 60
    predicted = ["forest"] * 30 + ["crop"] * 10 + ["forest"] * 6 + ["crop"] * 54
    assert accuracies(predicted, truth) == {"crop": (0.9, 0.844), "forest": (0.75, 0.833)}


def test_a_class_never_predicted():
    """gives None for the user's accuracy of a class the model never predicts"""
    assert accuracies(["A", "A"], ["A", "B"]) == {"A": (1.0, 0.5), "B": (0.0, None)}
A hint

Collect the blocks in a dictionary, blocks.setdefault(key, []).append(i), then shuffle sorted(blocks) and use n % k to choose each block's fold. For a class c, the right answers are the positions where both lists say c; divide by truth.count(c) for the producer's accuracy and by predicted.count(c) for the user's.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Then test yourself on features and on spatial validation; the session opens with the idea you have answered least well:

Practice that adapts to you

Practise this lesson

6 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 The mean NDVI of a pixel's 3 × 3 neighbourhood is which kind of feature?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: A neighbourhood feature, which describes the pixel's surroundings

    The lesson's features come in four kinds: the pixel's own bands, an index computed from them, its neighbourhood, and its place, as a distance or as coordinates.

  2. Question 2 of 6 Before k-nearest neighbours, every feature is scaled to mean 0 and standard deviation 1. What goes wrong without it?

    Choose one answer.

    Show the answer: Question 2 of 6

    Answer: Features in large units, such as metres to the river, swamp reflectances between 0 and 1 in the distances

    Scaling gives every feature the same spread, so that distances in feature space weigh them alike. It is the same reason the suitability lesson scaled its factors before weighting them.

  3. Question 3 of 6 A model trained on one district uses x and y as features and will be applied to a district 50 km away. What happens to those features there?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: They take values the model never saw, so its predictions rest on nothing it learned

    Place features describe the training area. In a new area they either extrapolate or mislead, so keep them out of models meant for other areas, or test them with blocks that keep whole regions apart.

  4. Question 4 of 6 Why does random cross-validation overstate accuracy on spatial data?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: Test pixels have neighbours of the same class in training, so the model can copy them

    Spatial autocorrelation breaks the independence that random splits assume. scikit-learn's guide gives group-wise splitting as the remedy whenever samples come in dependent groups; for spatial data the groups are blocks of space.

  5. Question 5 of 6 In scikit-learn, how do you make five folds of spatial blocks?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: Give every sample its block id as its group, and split with GroupKFold

    GroupKFold never puts the same group in both the training and the test set of a split. With block ids as groups, every fold holds out whole blocks.

  6. Question 6 of 6 Which of these evaluations has the same leak as random folds on pixels?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: Testing a crop-yield model on random plots from the same farms it was trained on

    Any grouping that makes samples alike, a farm, a patient, a sensor, a block of space, leaks through a random split. Split by the group the model will meet new.

Interview questions

Warm-up (fresher): Why does random cross-validation overstate accuracy on spatial data?

Nearby places are alike, so a randomly chosen test pixel usually has training pixels of its own class a few metres away, and a model can score well by copying them. Random folds then measure how well the model recalls the training area, not how it will do on ground it has not seen. In the lesson’s scene, a classifier that knew only each pixel’s x and y scored 0.891 with random folds and 0.561 with blocks of 300 m held out whole, against 0.494 for always answering crop. Validate with spatial blocks at least as wide as the distance over which places stay alike.

Three longer questions test this lesson against a field team, a teammate’s features and a carbon report; the module’s bank has the first step and the follow-ups of each:

  1. Mid to senior

    Your land-cover classifier scores 96 % in ten-fold cross-validation, but a field team finds it often wrong in a district it was not trained on. What went wrong, and how do you validate it properly?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A teammate adds each pixel's x and y as features, and the cross-validated accuracy of a land-cover classifier rises from 93 % to 99 %. Should the model keep them? How would you decide?

    Model answer, Pro. First step and follow-ups

  3. Mid to senior

    A classified map shows 48.2 ha of forest in a project area, and the client wants the forest area for a carbon report. What do you report, and what do you need to produce it?

    Model answer, Pro. First step and follow-ups

Flashcards · 6 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. Which four kinds of features describe a pixel to a land-cover classifier?

    Bands such as red, near-infrared and shortwave infrared; an index such as NDVI; its neighbourhood, such as the mean NDVI of the 3 × 3 pixels around it; and its place, such as the distance to the river or x and y.

  2. Why are place features such as x and y dangerous in a land-cover classifier?

    They let the model learn where each class lay in the training area, which no new area shares. Given x and y alone, a classifier reached 0.891 under random folds but only 0.561 under spatial blocks, barely above the 0.494 of always answering crop.

  3. Why does random cross-validation overstate a spatial model's accuracy, and what replaces it?

    Nearby pixels share their class, so each held-out pixel keeps neighbours of its class in training, and the model can copy them. Spatial block cross-validation holds whole blocks out; in scikit-learn, give each sample its block's id and split with GroupKFold.

  4. How large should spatial cross-validation blocks be?

    No narrower than the distance beyond which two pixels' classes are no more alike than chance would make them. Variants hold out one sample with a buffer of that width around it, or a whole region as if it had no reference data.

  5. What are producer's accuracy and user's accuracy?

    Two readings of a confusion matrix, which counts every pair of predicted and true class: producer's accuracy is the share of a class's true pixels that the map finds, and user's accuracy the share of the pixels mapped as that class that are right.

  6. Why estimate class areas from a reference sample rather than by counting a map's pixels?

    A pixel count inherits every error of the map. Credible products publish independent checks as well: ESA's global 10 m WorldCover maps report overall accuracies of 74.4 % and 76.7 %.

Key takeaways

  • Features describe a pixel by its bands, indices such as NDVI, its neighbourhood and its place, and place features let a model learn the training area itself.
  • Random cross-validation leaves each test pixel’s neighbours in training; spatial blocks hold whole areas out, and x and y alone fell from 0.891 to 0.561 under them.
  • A credible map reports producer’s and user’s accuracy per class and estimates areas from a reference sample.

Terms in this lesson

confusion matrix
A table of a classifier's results that counts, for every pair of classes, the samples predicted as one class whose true class is the other. Its diagonal holds the right answers.
k-nearest neighbours
A classifier that labels a sample with the majority class of the k training samples nearest to it in feature space, after the features are put on a common scale.
producer's accuracy
The share of the samples truly in a class that a map or classifier labels as that class: how much of the real class it finds.
spatial block cross-validation
Cross-validation that holds out whole blocks of space at a time, so that the samples tested are never next to samples the model was trained on. Blocks should be wider than the distance over which places stay alike.
user's accuracy
The share of the samples a map or classifier labels as a class that truly belong to it: how often the label is right when a user reads it.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.