Machine Learning
SVM
Python
Satellite Imagery
Image Classification

How to train an SVM classifier on a satellite image using Python

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

Support Vector Machines can work very well for satellite image classification when training data is limited and feature spaces are moderate. The key is converting raster pixels and labels into a clean tabular dataset, then applying consistent preprocessing. This guide demonstrates a full workflow in Python using rasterio and scikit-learn.

Prepare Features and Labels From Raster Data

For supervised training, you need predictor bands and labeled pixels. A common setup is one multiband image for features and one single-band raster for class labels.

python
1import numpy as np
2import rasterio
3
4FEATURE_RASTER = "features.tif"   # multiband, shape bands x rows x cols
5LABEL_RASTER = "labels.tif"       # single band, class ids, 0 means unlabeled
6
7with rasterio.open(FEATURE_RASTER) as src_x:
8    bands = src_x.read()  # shape: (n_bands, h, w)
9    profile = src_x.profile
10
11with rasterio.open(LABEL_RASTER) as src_y:
12    labels = src_y.read(1)  # shape: (h, w)
13
14n_bands, h, w = bands.shape
15
16# Flatten bands to feature matrix
17X_all = bands.reshape(n_bands, -1).T  # shape: (h*w, n_bands)
18y_all = labels.reshape(-1)
19
20# Keep labeled pixels only
21mask = y_all > 0
22X = X_all[mask]
23y = y_all[mask]
24
25print("Samples:", X.shape[0], "Features:", X.shape[1], "Classes:", np.unique(y))

If class imbalance is strong, stratified splitting and class weighting are critical for stable results.

Train an SVM With Scaled Features

SVM performance is sensitive to feature scale, so use a pipeline with StandardScaler. The code below performs a train and validation split and reports classification metrics.

python
1from sklearn.model_selection import train_test_split
2from sklearn.pipeline import Pipeline
3from sklearn.preprocessing import StandardScaler
4from sklearn.svm import SVC
5from sklearn.metrics import classification_report, confusion_matrix
6
7X_train, X_test, y_train, y_test = train_test_split(
8    X, y, test_size=0.2, random_state=42, stratify=y
9)
10
11model = Pipeline([
12    ("scaler", StandardScaler()),
13    ("svm", SVC(
14        kernel="rbf",
15        C=10.0,
16        gamma="scale",
17        class_weight="balanced"
18    ))
19])
20
21model.fit(X_train, y_train)
22pred = model.predict(X_test)
23
24print(confusion_matrix(y_test, pred))
25print(classification_report(y_test, pred, digits=4))

class_weight="balanced" helps when some land-cover classes have far fewer training pixels than others.

Tune Hyperparameters With Cross-Validation

For production use, tune C and gamma using cross-validation. Keep search space realistic to avoid costly experiments with little gain.

python
1from sklearn.model_selection import GridSearchCV, StratifiedKFold
2
3param_grid = {
4    "svm__C": [1, 10, 30],
5    "svm__gamma": [0.001, 0.01, "scale"],
6    "svm__kernel": ["rbf"]
7}
8
9cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
10search = GridSearchCV(model, param_grid, scoring="f1_macro", cv=cv, n_jobs=-1)
11search.fit(X_train, y_train)
12
13print("Best params:", search.best_params_)
14print("Best CV score:", round(search.best_score_, 4))
15
16best_model = search.best_estimator_

Choose your scoring metric based on project goals. Macro F1 is often better than accuracy for imbalanced satellite classes.

Predict the Full Image and Save Classified Raster

After training, classify all pixels and write a new raster map with class ids.

python
1import numpy as np
2import rasterio
3
4# X_all was built earlier from full image
5full_pred = best_model.predict(X_all).astype(np.uint8)
6classified = full_pred.reshape(h, w)
7
8out_profile = profile.copy()
9out_profile.update(count=1, dtype=rasterio.uint8)
10
11with rasterio.open("classified_map.tif", "w", **out_profile) as dst:
12    dst.write(classified, 1)
13
14print("Saved classified_map.tif")

If unlabeled areas should remain background, add a mask step before final export to restore original no-data semantics.

Data Quality and Feature Engineering Notes

SVM can perform strongly with spectral bands alone, but adding derived indices often improves class separability. Typical additions include normalized difference vegetation index and texture features computed over local windows.

Also ensure alignment between feature and label rasters. Different projections, resolutions, or extents can silently corrupt training labels. Always verify spatial metadata before flattening arrays.

Common Pitfalls

  • Training on misaligned feature and label rasters, which produces misleading metrics.
  • Skipping scaling before SVM, causing unstable decision boundaries.
  • Evaluating only overall accuracy while minority classes fail.
  • Predicting full images without managing no-data regions explicitly.
  • Running large hyperparameter grids without stratified validation splits.

Summary

  • Convert multiband raster data into a clean feature matrix with labeled pixels.
  • Use a scaling plus SVM pipeline for stable training behavior.
  • Tune C and gamma with cross-validation and class-aware metrics.
  • Reconstruct full-image predictions and write typed GeoTIFF output.
  • Validate raster alignment and class balance to avoid silent modeling errors.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.