Lesson 4.1.1: Structuring hyperspectral data for model training
Lesson 4.1.1: Structuring hyperspectral data for model training
Learning Objectives
- Explain the importance of organising hyperspectral data for machine learning workflows.
- Identify common data formats and structures used in hyperspectral preprocessing.
- Apply best practices for normalising, resampling and splitting datasets for training, validation and testing.
- Diagnose and mitigate common pitfalls such as spectral leakage, class imbalance and overfitting due to high dimensionality.
Key Terms
| Spectral band | A narrow range of wavelengths captured by a hyperspectral sensor, representing a single channel of data. |
|---|---|
| Data cube | A three‑dimensional array where two axes represent spatial coordinates and the third axis represents spectral bands. |
| Band normalisation | Scaling spectral values to a common range (e.g., 0–1) to reduce bias between bands. |
| Class imbalance | A situation where some spectral classes are under‑represented, leading to biased model training. |
| Hold‑out split | Partitioning data into disjoint sets for training, validation, and testing to evaluate generalisation. |
flowchart TD
A[Raw hyperspectral images] --> B[Pre‑processing]
B --> C[Calibration & Noise reduction]
B --> D[Band selection / Dimensionality reduction]
C --> E[Normalisation]
D --> E
E --> F[Data labelling]
F --> G[Train‑test split]
G --> H[Model training]
H --> I[Evaluation & Validation]
Additional Example: Spectral Normalisation and Augmentation
In many pipelines, normalising each band to a common scale and applying simple augmentations can improve model robustness. Below is a quick example that demonstrates per‑band min‑max scaling followed by random spectral shifts.
import numpy as np
import rasterio
import random
from pathlib import Path
Normalise each band to [0,1] using min-max per band
def minmax_normalise(arr):
mins = arr.min(axis=(1,2), keepdims=True)
maxs = arr.max(axis=(1,2), keepdims=True)
return (arr - mins) / (maxs - mins + 1e-8)
Randomly shift spectral bands to simulate illumination changes
def spectral_shift(arr, shift_range=(-0.05, 0.05)):
shift = np.random.uniform(*shift_range, size=(arr.shape[0], 1, 1))
return arr + shift
def augment_scene(scene_path, out_path):
with rasterio.open(scene_path) as src:
data = src.read() # shape (bands, H, W)
data = minmax_normalise(data)
data = spectral_shift(data)
meta = src.meta.copy()
meta.update(dtype=rasterio.float32)
with rasterio.open(out_path, 'w', **meta) as dst:
dst.write(data.astype(rasterio.float32))
if __name__ == "__main__":
scene = Path("hyperspectral_project/raw/sensorA/scene_001/scene_001.tif")
out = Path("hyperspectral_project/processed/normalised") / "scene_001_norm.tif"
out.parent.mkdir(parents=True, exist_ok=True)
augment_scene(scene, out)
This snippet can be plugged into the preprocess.py workflow to generate a second set of tiles that are normalised and augmented for training.
Comparing Organisation Approaches
| Approach | Semantic Clarity | Computational Efficiency | Reproducibility | Typical Use‑case |
|---|---|---|---|---|
| Flat file system (single folder per scene) | Low – no clear sensor or class grouping | Medium – all data loaded at once | Low – hard to track preprocessing steps | Quick prototypes, limited data |
| Hierarchical sensor/scene structure (as shown) | High – explicit sensor and scene metadata | High – allows selective loading and tiling | High – version control and scripts isolated | Full‑scale research projects |
| Containerised pipeline with fixed image archives | Medium – metadata stored in container config | Very high – pre‑packaged binaries optimise I/O | Very high – containers capture exact environment | Industrial deployment, reproducible builds |
After exploring how to structure hyperspectral data for AI training, think about the specific challenges you might face when organising your own datasets. Consider the impact of spectral resolution, spatial alignment, and metadata consistency. Write a brief reflection (2–4 sentences) on how you would address these issues in a real‑world project.
Outline a step‑by‑step plan for augmenting your hyperspectral dataset prior to training. Include at least three augmentation techniques (e.g., spectral shifts, noise injection, spatial cropping) and justify why each is suitable for hyperspectral data. Describe how you would integrate these steps into an automated preprocessing script.
Implementation/Code: Preprocessing Pipelines
When preparing hyperspectral data for machine‑learning models, the preprocessing stage is arguably the most critical. The raw measurements arrive as large, multi‑dimensional arrays that are rarely ready for ingestion by a neural network. Normalisation, resampling, and efficient batching must be orchestrated so that each pipeline stage respects the physical meaning of the data while keeping memory usage under control. Below we walk through a typical workflow in Python, emphasising the rationale behind each step and providing annotated code snippets that you can adapt to your own projects.
1. Normalising the Spectral Cubes
Hyperspectral sensors record radiance or reflectance values that span many orders of magnitude. Models trained on such raw values often converge slowly or become numerically unstable. A common remedy is to apply per‑band normalisation, where each spectral channel is centred and scaled independently. This preserves inter‑band relationships while reducing dynamic range.
We illustrate a simple yet powerful approach using scikit‑learn's StandardScaler. The example assumes the data is stored in a numpy array of shape (N, H, W, B), where N is the number of samples, H and W are spatial dimensions, and B is the spectral dimension.
import numpy as np
from sklearn.preprocessing import StandardScaler
Dummy hyperspectral dataset: 100 samples, 64x64 pixels, 200 spectral bands
X = np.random.rand(100, 64, 64, 200).astype(np.float32)
Reshape to (NHW, B) so each pixel becomes a sample for scaling
pixels = X.reshape(-1, X.shape[-1])
Initialise the scaler
scaler = StandardScaler()
Fit on the entire dataset (or on a validation set if you prefer)
scaler.fit(pixels)
Apply the transformation
X_norm = scaler.transform(pixels)
Reshape back to the original shape
X_norm = X_norm.reshape(X.shape)
Line‑by‑line explanation:
import numpy as npandfrom sklearn.preprocessing import StandardScalerbring in the required libraries.- The random array
Xmimics a real hyperspectral stack; in practice you would load your data from a.tiffor.matfile. pixels = X.reshape(-1, X.shape[-1])flattens the spatial dimensions. Each row now represents a single pixel’s spectral signature.- We instantiate
StandardScaler(), which will compute the mean and standard deviation for each spectral band. scaler.fit(pixels)calculates these statistics across all pixels in the dataset.scaler.transform(pixels)centres and scales every band to unit variance.- Finally,
reshaperestores the four‑dimensional shape, ready for downstream processing.
Alternative strategies include min‑max scaling, logarithmic compression, or even spectral‑band‑wise clipping to a specified percentile range. The choice depends on sensor characteristics and the model’s sensitivity to outliers.
# Alternative: Min‑Max scaling to [0, 1] per band
from sklearn.preprocessing import MinMaxScaler
minmax_scaler = MinMaxScaler()
pixels_minmax = minmax_scaler.fit_transform(pixels)
X_minmax = pixels_minmax.reshape(X.shape)
| Approach | Pros | Cons | Typical Use‑Case |
|---|---|---|---|
| StandardScaler (z‑score) | Preserves mean/variance, robust to outliers if band‑wise | Can amplify noise if bands have very small variance | Deep learning, where zero‑mean input favours optimiser stability |
| Min‑Max Scaler | Simpler, bounds data to [0, 1] | Sensitive to extreme values; may compress useful dynamic range | Models that expect inputs in a fixed range (e.g., sigmoid activations) |
| Log Compression | Reduces large dynamic range, emphasises subtle differences | Requires all values >0; can distort linear relationships | Radiometric data where log‑scale better reflects physical processes |
2. Resampling to a Common Grid
Hyperspectral data acquired from different platforms often exhibit varying spectral resolutions. To train a single model, you need all inputs on a shared wavelength grid. Resampling can be performed via interpolation or spectral binning. Below we demonstrate cubic spline interpolation using scipy.interpolate.
from scipy.interpolate import interp1d
Original wavelengths (nm), e.g., 400–1000 nm sampled every 3 nm
orig_wl = np.arange(400, 1001, 3)
Desired common grid (e.g., 400–1000 nm every 5 nm)
common_wl = np.arange(400, 1001, 5)
Function to resample a single spectral cube
def resample_cube(cube, orig_wl, common_wl):
# cube shape: (H, W, B)
H, W, B = cube.shape
# Create an array to hold resampled data
resampled = np.empty((H, W, len(common_wl)), dtype=cube.dtype)
# Resample each pixel individually
for i in range(H):
for j in range(W):
f = interp1d(orig_wl, cube[i, j, :], kind='cubic')
resampled[i, j, :] = f(common_wl)
return resampled
Example usage on a single sample
sample_cube = X_norm[0] # shape (64, 64, 200)
sample_resampled = resample_cube(sample_cube, orig_wl, common_wl)
This routine loops over every pixel, constructs a cubic spline interpolator, and evaluates it on the new grid. For large datasets, vectorised or GPU‑accelerated resampling (e.g., torch.nn.functional.interpolate with custom kernels) are preferable.
3. Constructing Data Loaders with PyTorch
Deep learning frameworks typically expect data to be streamed in batches. PyTorch’s DataLoader class, coupled with a custom Dataset, provides a flexible way to feed normalised, resampled cubes into a neural network. Below we define a straightforward dataset that encapsulates the three preprocessing steps.
import torch
from torch.utils.data import Dataset, DataLoader
class HyperspectralDataset(Dataset):
def __init__(self, cubes, labels, scaler, orig_wl, common_wl):
""
cubes: list or array of shape (N, H, W, B)
labels: array of shape (N,)
scaler: fitted StandardScaler instance
orig_wl, common_wl: wavelength arrays for resampling
""
self.cubes = cubes
self.labels = labels
self.scaler = scaler
self.orig_wl = orig_wl
self.common_wl = common_wl
def __len__(self):
return len(self.cubes)
def __getitem__(self, idx):
# Normalise
cube = self.cubes[idx]
pixels = cube.reshape(-1, cube.shape[-1])
cube_norm = self.scaler.transform(pixels).reshape(cube.shape)
# Resample
cube_resampled = resample_cube(cube_norm, self.orig_wl, self.common_wl)
# Convert to torch tensor
tensor = torch.from_numpy(cube_resampled).permute(2, 0, 1) # (B, H, W)
label = torch.tensor(self.labels[idx], dtype=torch.long)
return tensor, label
Instantiate dataset
dataset = HyperspectralDataset(X_norm, np.random.randint(0, 5, size=100),
scaler, orig_wl, common_wl)
Create DataLoader
loader = DataLoader(dataset, batch_size=8, shuffle=True, num_workers=4)
Key points:
__getitem__normalises each cube on the fly, ensuring that the scaler’s parameters remain fixed and that no data leakage occurs between training and validation splits.- Resampling is performed per sample; if the target grid is the same for all samples, you could pre‑compute this step to speed up training.
- Reordering the dimensions to
(B, H, W)matches the expectation of convolutional layers that treat the spectral axis as channels. - Using
num_workers=4offloads data loading to separate processes, keeping the GPU busy.
4. Benchmarking Memory Footprint
Preprocessing can be memory‑intensive, especially when working with gigabyte‑scale datasets. It is prudent to monitor peak memory usage and optimise accordingly. The following table summarises typical memory consumption for a batch of 8 samples, each 64×64×200, under the pipeline above.
| Stage | Peak RAM (MB) |
|---|---|
| Raw data (float32) | 8 × 64 × 64 × 200 × 4 ≈ 2 097 MB |
| Normalised data (in‑place) | ≈ 2 097 MB |
| Resampled data (common grid 5 nm) | ≈ 2 097 MB × (200/120) ≈ 3 500 MB |
| Torch tensors (B,H,W) | ≈ 3 500 MB |
Strategies to reduce memory:
- In‑place operations: normalise directly on the original array.
- Half‑precision: cast tensors to
torch.float16if the model tolerates it. - On‑the‑fly resampling: perform interpolation only for the batch being processed, discarding the intermediate result after conversion to tensor.
- Chunking the dataset: split the dataset into smaller files and load them sequentially.
5. Integrating with a Convolutional Neural Network
Once the data is in a DataLoader, you can feed it into a CNN. A minimal architecture for hyperspectral classification might look like this:
import torch.nn as nn
import torch.nn.functional as F
class SimpleHSNet(nn.Module):
def __init__(self, in_channels, num_classes):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(2)
self.fc1 = nn.Linear(64 16 16, 128)
self.fc2 = nn.Linear(128, num_classes)
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(x)
x = F.relu(self.conv2(x))
x = self.pool(x)
x = x.view(x.size(0), -1)
x = F.relu(self.fc1(x))
return self.fc2(x)
Here, in_channels equals the number of spectral bands after resampling (len(common_wl)). The pooling layers reduce spatial dimensions, allowing the fully connected layers to operate on a manageable feature vector.
6. Practical Tips and Common Pitfalls
- Sensor‑specific corrections: before normalisation, apply radiometric calibration, dark‑current subtraction, and atmospheric correction if available.
- Batch‑wise vs dataset‑wide scaling: fitting the scaler on the entire training set preserves global statistics but may leak information into validation folds if not carefully split.
- Interpolation artefacts: cubic interpolation can introduce ringing near sharp spectral features; consider windowed sinc or linear interpolation for smoother results.
- GPU memory vs CPU memory: keep the preprocessing on the CPU until tensors are ready; otherwise you risk saturating GPU memory with intermediate buffers.
- Reproducibility: set random seeds for all libraries (
np.random.seed,torch.manual_seed, etc.) to guarantee deterministic behaviour.
By following the steps outlined above, you create a robust preprocessing pipeline that normalises, resamples, and batches hyperspectral data efficiently. This foundation enables the subsequent training of deep learning models that can leverage the rich spectral information inherent in your measurements.
- Which of the following steps is NOT typically part of a hyperspectral data preprocessing pipeline?
- A) Noise reduction
- B) Spectral resampling
- C) Spatial interpolation of missing pixels
- D) Random data shuffling for training
- True or False: Normalising each spectral band to zero mean and unit variance is essential before feeding data into a neural network.
- Choose the correct statement about data augmentation for hyperspectral images:
- A) Flipping and rotating the image preserve spectral integrity.
- B) Adding Gaussian noise to the spectral dimension can improve model robustness.
- C) Colour space conversion is irrelevant for hyperspectral data.
- D) All of the above.
Create a Python function that takes a hyperspectral cube (numpy array of shape (H, W, B)) and applies the following augmentations: random horizontal flip, random vertical flip, and random spectral band shuffling. Return the augmented cube. Provide a short explanation of why each augmentation might help a model generalise.
9. Alternative Normalisation Strategy: Per‑Pixel Min‑Max Scaling
Some applications benefit from normalising each pixel independently so that every spectrum spans the full dynamic range. This can be useful when the absolute reflectance values are less informative than the relative spectral shape.
# Per‑pixel min‑max scaling to range [0, 1]
pixel_min = samples.min(axis=1, keepdims=True)
pixel_max = samples.max(axis=1, keepdims=True)
samples_minmax = (samples - pixel_min) / (pixel_max - pixel_min + 1e-8)
print("First pixel before scaling:", samples[0, :5])
print("First pixel after scaling:", samples_minmax[0, :5])
| Aspect | Per‑Band Standardisation | Per‑Pixel Min‑Max |
|---|---|---|
| Goal | Equalise variance across bands | Equalise dynamic range per pixel |
| Effect on Spectral Shape | Preserved, but amplitude scaled | Preserved, but each spectrum stretched/compressed |
| Computational Cost | O(Nbands × Nsamples) | O(Nbands × Nsamples) but requires per‑pixel min/max |
| Typical Use‑Case | Regression, classification with absolute reflectance comparison | Texture analysis, edge detection, where relative band ratios matter |
| Risk of Noise Amplification | Low if applied globally | Higher if a band contains only noise leading to division by small range |
| Implementation Note | Use StandardScaler from scikit‑learn |
Manual NumPy operations as shown above |
Imagine you are preparing a new hyperspectral dataset for a classification model. Based on the concepts covered in this lesson, outline a detailed plan for splitting the data into training, validation, and test sets. Include your choice of split ratios, any stratification or randomisation techniques, and how you would handle class imbalance. Then, explain how you would verify that the split preserves the spectral and spatial characteristics of the original dataset.
Data Preparation Checklist
Before embarking on model training, confirm that your dataset satisfies the following criteria. A quick audit against these items can save hours of debugging later.
• All scenes are labelled consistently, with a clear mapping from class names to numeric identifiers.
• Spectral metadata (wavelengths, sensor type, acquisition date) is stored in a machine‑readable format such as JSON or CSV and is accessible to all preprocessing scripts.
• The raw data has been verified for integrity (checksum, file size) and is free of corrupted tiles that could crash loaders.
• The chosen hold‑out split preserves spatial contiguity where necessary, avoiding leakage of neighbouring pixels between training and test sets.
Workflow Overview
The end‑to‑end pipeline can be summarised in three high‑level stages, each encapsulating a set of scripts and artefacts. Visualising the flow helps identify bottlenecks and ensures reproducibility across teams.
1. Data Ingestion – Load raw files, validate integrity, and store a canonical copy in a version‑controlled repository (e.g., Git LFS or a dedicated data store).
2. Pre‑processing – Apply calibration, normalisation, resampling, and augmentation in a deterministic, reproducible script. Output should be a set of normalised, resampled tiles ready for model consumption.
3. Training & Evaluation – Use the prepared tiles to train a model, monitor training dynamics, and generate evaluation artefacts (confusion matrices, ROC curves). Store checkpoints and final metrics in a structured experiment log.
Pitfalls: Common Issues and Fixes
When organising hyperspectral imagery for supervised model training, a handful of routine missteps can erode model performance more quickly than any hyper‑parameter tweak. Below we catalogue the most frequent errors—spectral leakage, class imbalance, and over‑fitting—followed by concrete strategies to mitigate each. The discussion is deliberately technical, aimed at practitioners who already understand the basics of spectral pre‑processing and machine learning pipelines.
1. Spectral Leakage: The Invisible Noise Source
Spectral leakage occurs when the discrete Fourier transform (DFT) of a signal is contaminated by energy from neighbouring wavelengths. In hyperspectral data this is often a consequence of inadequate windowing or of mixing pixels that have been coarsely resampled. Leakage can manifest as subtle smears in the reflectance curve, leading to mis‑classification in models that rely on fine spectral features.
Typical symptoms:
- Unexpected spikes in the spectral signature of a class that do not match ground truth.
- Poor separability in Principal Component Analysis (PCA) or Linear Discriminant Analysis (LDA) plots.
- High variance in cross‑validation scores across neighbouring bands.
Fixes:
- Apply a Hamming or Hann window before computing the DFT if you use frequency‑domain augmentation.
- Use spectral band selection to exclude bands that are known to be noisy (e.g., water absorption bands).
- When resampling, adopt cubic convolution or Lanczos interpolation rather than nearest‑neighbour to preserve spectral continuity.
Illustration of a simple windowing step in Python:
import numpy as np
from scipy.signal import windows
Assume `spectrum` is a 1‑D numpy array of reflectance values
def apply_hamming_window(spectrum):
""
Multiply the input spectrum by a Hamming window to reduce spectral leakage.
""
# 1. Generate a Hamming window of the same length as the spectrum
window = windows.hamming(len(spectrum))
# 2. Element‑wise multiplication preserves the overall amplitude
return spectrum * window
Example usage:
spectrum = np.array([...]) # Replace with real data
cleaned_spectrum = apply_hamming_window(spectrum)
Line‑by‑line explanation:
import numpy as npimports the numerical library.from scipy.signal import windowsbrings in the window functions.- Function
apply_hamming_windowtakes a 1‑D array. - Inside the function,
windows.hamming(len(spectrum))creates a Hamming window matching the spectrum length. - The window is multiplied element‑wise with the spectrum, attenuating edge effects.
- The cleaned spectrum is returned for downstream processing.
2. Class Imbalance: When Rare Signals Get Overlooked
Hyperspectral datasets frequently exhibit skewed class distributions—e.g., a sparse canopy class versus a vast background. If left uncorrected, models will bias toward the majority class, yielding deceptively high overall accuracy but poor recall for minority classes.
Common indicators:
- Accuracy > 90 % but F1‑score for minority class < 0.3.
- Confusion matrix dominated by diagonal entries for the majority class.
- Training loss plateauing while validation loss diverges.
Mitigation tactics:
- Resampling: either oversample minority classes (e.g., SMOTE) or undersample the majority class.
- Class‑weighted loss functions: assign higher penalties to minority errors.
- Use stratified splits during cross‑validation to preserve class ratios.
- Implement focal loss for deep learning models to focus learning on hard examples.
Python example of a class‑weighted Random Forest:
from sklearn.ensemble import RandomForestClassifier
from sklearn.utils.class_weight import compute_class_weight
import numpy as np
X_train: feature matrix, y_train: class labels
class_weights = compute_class_weight(
class_weight='balanced',
classes=np.unique(y_train),
y=y_train
)
weight_dict = dict(zip(np.unique(y_train), class_weights))
rf = RandomForestClassifier(
n_estimators=200,
class_weight=weight_dict,
random_state=42
)
rf.fit(X_train, y_train)
Explanation:
- Import the RandomForestClassifier and a util to compute class weights.
- Calculate weights inversely proportional to class frequencies.
- Map each class to its weight.
- Instantiate the classifier with
class_weight=weight_dict, ensuring minority classes exert greater influence. - Fit the model on the training data.
3. Over‑Fitting: The Model Learns Noise, Not Signal
Over‑fitting is the classic pitfall where a model captures idiosyncratic patterns in the training set that do not generalise. In hyperspectral work, high dimensionality combined with limited samples can exacerbate this.
Symptoms:
- Training accuracy close to 100 % while validation accuracy lags significantly.
- Large standard deviation in validation metrics across folds.
- Model predictions fluctuate wildly when presented with spectrally similar but unseen samples.
Prevention strategies:
- Dimensionality reduction: PCA, t‑SNE, or autoencoders to summarise spectra.
- Regularisation: L1/L2 penalties in linear models, dropout in neural nets.
- Use early stopping based on validation loss.
- Apply cross‑validation with a high number of folds to obtain a robust estimate of generalisation.
- Limit model complexity: fewer layers/neurons or trees in ensemble methods.
Code snippet for PCA‑based feature reduction followed by a Gradient Boosting model:
from sklearn.decomposition import PCA
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
pipeline = Pipeline([
('scaler', StandardScaler()), # Zero‑mean, unit‑variance
('pca', PCA(n_components=0.95)), # Retain 95 % variance
('gb', GradientBoostingClassifier(
n_estimators=300,
learning_rate=0.05,
max_depth=5,
random_state=123
))
])
pipeline.fit(X_train, y_train)
Line‑by‑line:
- Import PCA, GradientBoostingClassifier, Pipeline, and StandardScaler.
- Instantiate a pipeline that first standardises data.
- Apply PCA, automatically selecting enough components to explain 95 % of variance.
- Fit a Gradient Boosting model with a moderate learning rate and depth.
- Train the entire pipeline on the training set.
4. Neglecting Spatial Context: Treating Pixels as Isolated
Many novices treat each pixel as an independent sample, discarding the valuable spatial relationships inherent in imagery. This can lead to high intra‑class variance and over‑fitting.
Remedies:
- Incorporate spatial‑spectral fusion by concatenating local patches or using 3‑D convolutions.
- Use Markov Random Fields or conditional random fields to post‑process predictions.
- Apply morphological opening/closing to smooth prediction maps.
5. Inadequate Validation Design
Validation protocols that ignore the spatial autocorrelation inherent in remote sensing data can inflate performance estimates. Classic random splits may inadvertently place adjacent pixels in training and test sets, leading to overly optimistic results.
Best practice:
- Implement stratified block cross‑validation where each fold is a spatially contiguous block.
- Use a leave‑one‑scene-out strategy when multiple scenes are available.
- Report metrics per class and per scene to expose hidden biases.
6. Feature Scaling Missteps
In hyperspectral work, reflectance values are typically already normalised (0–1). However, when adding derived indices (NDVI, SAVI, etc.) or external covariates (DEM, soil maps), inconsistent scaling can distort model learning.
Solution:
- Apply robust scaling (median and IQR) to outlier‑prone features.
- When mixing data types, standardise all features to zero mean and unit variance.
- Document scaling choices in the metadata for reproducibility.
7. Forgetting to Monitor Training Dynamics
Without visualising loss curves or per‑epoch metrics, subtle over‑fitting may go unnoticed.
Recommendation:
- Plot training and validation loss after each epoch.
- Track additional metrics such as confusion matrix evolution.
- Use TensorBoard or similar tools for deep learning workflows.
Summative Checklist
| Issue | Detection | Remedy |
|---|---|---|
| Spectral leakage | Spurious band spikes | Windowing, band selection |
| Class imbalance | Low minority F1 | Resampling, weighted loss |
| Over‑fitting | Train/val gap | PCA, regularisation, early stopping |
| Spatial neglect | High intra‑class variance | Spatial‑spectral fusion, MRF |
| Validation bias | Inflated metrics | Block CV, leave‑one‑scene‑out |
| Feature scaling | Divergent feature ranges | Robust/standard scaling |


Comments are closed.