Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Pixel-Level Regression

Introduction

Learning Objectives

Understanding Pixel Regression

Classification vs. Regression

Applications

Regression Architectures

Adapting Segmentation Models for Regression

Loss Functions for Regression

Output Activation and Scaling

Case Study: NDVI Prediction from Landsat Imagery

Environment Setup

import geoai
from sklearn.model_selection import train_test_split

Download Data

train_raster = geoai.download_file(
    "https://data.source.coop/opengeos/geoai/tn_landsat_2022.tif"
)
train_target = geoai.download_file(
    "https://data.source.coop/opengeos/geoai/tn_ndvi_2022.tif"
)
test_raster = geoai.download_file(
    "https://data.source.coop/opengeos/geoai/tn_landsat_2023.tif"
)

Inspect the Data

import rasterio

with rasterio.open(train_raster) as src:
    in_channels = src.count
    print(f"Input shape: {src.height} x {src.width}, {src.count} bands")
    print(f"Input CRS: {src.crs}")
    print(f"Input resolution: {src.res}")

with rasterio.open(train_target) as src:
    print(f"Target shape: {src.height} x {src.width}, {src.count} band(s)")
    target_data = src.read(1)
    print(f"Target value range: [{target_data.min():.2f}, {target_data.max():.2f}]")

Create Training Tiles

image_paths, target_paths = geoai.create_regression_tiles(
    input_raster=train_raster,
    target_raster=train_target,
    output_dir="ndvi_tiles",
    tile_size=256,
    stride=128,
    target_band=1,
    min_valid_ratio=0.9,
    target_min=-1.0,
    target_max=1.0,
)
print(f"Created {len(image_paths)} tiles")

Split Data

train_imgs, val_imgs, train_tgts, val_tgts = train_test_split(
    image_paths, target_paths, test_size=0.2, random_state=42
)
print(f"Training: {len(train_imgs)}, Validation: {len(val_imgs)}")

Train the Model

model = geoai.train_pixel_regressor(
    train_image_paths=train_imgs,
    train_target_paths=train_tgts,
    val_image_paths=val_imgs,
    val_target_paths=val_tgts,
    encoder_name="resnet34",
    architecture="unet",
    in_channels=in_channels,
    output_dir="ndvi_model",
    batch_size=8,
    num_epochs=100,
    learning_rate=1e-3,
    num_workers=0,
    loss_type="mse",
    patience=20,
    devices=1,
    verbose=False,
)

Monitor Training History

fig, history_df = geoai.plot_training_history(
    log_dir="ndvi_model",
    metrics=["loss", "r2"],
)

Run Inference on Training Area

geoai.predict_raster(
    model=model,
    input_raster=train_raster,
    output_raster="ndvi_model/predicted_ndvi_2022.tif",
    tile_size=256,
    overlap=64,
    batch_size=8,
    clip_range=(-1.0, 1.0),
)

Evaluate Results

fig, metrics = geoai.plot_regression_comparison(
    true_raster=train_target,
    pred_raster="ndvi_model/predicted_ndvi_2022.tif",
    title="NDVI Prediction Results",
    cmap="RdYlGn",
    vmin=-0.2,
    vmax=0.8,
    valid_range=(-1.0, 1.0),
)
fig, metrics = geoai.plot_scatter(
    true_raster=train_target,
    pred_raster="ndvi_model/predicted_ndvi_2022.tif",
    sample_size=50000,
    valid_range=(-1.0, 1.0),
    fit_line=True,
)

Predict on New Data (2023)

geoai.predict_raster(
    model=model,
    input_raster=test_raster,
    output_raster="ndvi_model/predicted_ndvi_2023.tif",
    tile_size=256,
    overlap=64,
    batch_size=8,
    clip_range=(-1.0, 1.0),
)
geoai.visualize_prediction(
    input_raster=test_raster,
    pred_raster="ndvi_model/predicted_ndvi_2023.tif",
    cmap="RdYlGn",
    vmin=-0.2,
    vmax=0.8,
)

Evaluation Metrics

Key Takeaways

Exercises

Exercise 1: Comparing Loss Functions

Exercise 2: Encoder Architecture Comparison

Exercise 3: The Effect of Tile Size and Stride

Exercise 4: Residual Analysis and Error Mapping