The first foundation model purpose-built for the spectral cube. Instead of treating a satellite tile like an RGB photo, SpectralGPT masks and reconstructs 3D tokens that span both space and wavelength — learning the joint spatial-spectral structure of a million Sentinel-2 scenes with self-supervision alone.
You are an analyst at a forestry agency. A Sentinel-2 satellite just flew over a province and handed you a stack of images of the same patch of ground — but not three images for red, green, and blue. Twelve images, one per spectral band: deep blue, green, red, four red-edge bands that sit on the slope between visible and infrared, a near-infrared band, two short-wave infrared bands. Your job is to flag which pixels are stressed crops, which are healthy forest, which are flooded.
Here is the thing the RGB world never prepared you for: the signal you care about lives across the bands, not within any single one. Vegetation looks dark in red and blazingly bright in near-infrared — that contrast (the basis of the famous NDVI index) is invisible if you only ever look at one channel at a time. Two pixels can have identical brightness in every visible band and be completely different materials once you read their red-edge and short-wave-infrared response. The shape of the spectrum — how reflectance rises and falls as you walk across wavelength — is the fingerprint of the material on the ground.
So you go looking for a pretrained model to give you good features, the way ImageNet-pretrained ResNets transformed ordinary computer vision. And you find a problem. Every "remote-sensing foundation model" before SpectralGPT did one of two things, both lossy:
Both failures share a root cause: they treat the spectral axis as an afterthought. A 2D vision model partitions an image into spatial patches and lets attention discover spatial structure. But it has no analogous machinery for the wavelength axis. Bands are either dropped, averaged, or flattened into a feature vector where their sequential structure (band i is adjacent to band i+1) is lost. The model can memorise "this combination of numbers means forest," but it cannot learn the general principle that the smooth rise from red into near-infrared is chlorophyll.
Three materials — vegetation, water, bare soil — that overlap heavily in the visible bands but separate cleanly in the infrared. Drag the slider to pick which band you inspect. Notice how, looking at one band at a time, the three classes are often indistinguishable; only the full spectral shape separates them.
SpectralGPT's bet is simple to state and hard to execute: treat the spectral cube as a true 3D object and learn it self-supervised. Borrow the masked-autoencoder recipe that worked so well for images — hide most of the input, ask the model to reconstruct it — but redefine "the input" as 3D tokens that each carry a small patch of space and a small slice of bands. Mask 90% of these tokens. Force the network to reconstruct the missing space-and-wavelength chunks, with a loss that explicitly rewards getting the spectral sequence right. Do this on a million Sentinel-2 scenes and you get a backbone that, fine-tuned, beats every prior model on scene classification, multi-label tagging, and pixel-level change detection.
| Approach | Spectral axis treated as… | What is lost |
|---|---|---|
| RGB backbone, 3 bands | Discarded (9 of 12 bands dropped) | All infrared / red-edge evidence |
| 2D ViT, bands = channels | Unordered feature stack | Spectral adjacency & sequence |
| Per-band 2D models, late fusion | Independent images | Spatial-spectral coupling |
| SpectralGPT (3D tokens) | A masked, reconstructed dimension | Nothing — it is modelled directly |
Before any architecture, we need to be precise about the thing being modelled. A Sentinel-2 tile, after SpectralGPT's preprocessing, is a tensor of shape [H, W, D] = [96, 96, 12] in the first training stage and [128, 128, 12] in the second. H and W are spatial pixels; D = 12 is the number of spectral bands kept (Sentinel-2 has 13 bands; the cirrus band B10 is dropped because it carries no surface information).
Read the indices carefully. The pixel at spatial location (r, c) is not a scalar and not an RGB triple — it is a 12-dimensional spectral vector xr,c = [xr,c,1, xr,c,2, ..., xr,c,12]. Walk down that vector and you are walking up the electromagnetic spectrum, from ~490 nm (blue) to ~2190 nm (short-wave infrared). That ordered walk is the spectral signature.
A good representation of this cube must respect two kinds of locality at once:
The paper's central design claim is that you cannot get the third box (coupling) by handling the first two separately. If you patch spatially and then run an RNN over bands, or vice versa, you bottleneck the interaction. The fix is to make the atomic unit of computation already 3D: a token that is simultaneously a small spatial patch and a small spectral slice. Then a single stack of self-attention layers can model spatial, spectral, and coupled structure with one mechanism.
Pick a material and read off its 12-band signature. The reflectance curve is what the model must learn to reconstruct and complete. Notice vegetation's "red-edge" — the steep climb from band 4 (red) into bands 5–8 (red-edge / NIR) — and water's near-zero infrared.
Let us count, because the numbers drive every later design decision. One stage-1 cube is 96 × 96 × 12 = 110,592 scalar reflectance values. A million such cubes is over 1011 values — far too much to label, which is exactly why self-supervision (no labels needed) is the only viable route. The pretraining set is fMoW-S2 (712,874 images) followed by BigEarthNet-S2 (354,196 images), about 1.07 million Sentinel-2 cubes total, none of them requiring a human annotation for the pretraining objective.
The first real machinery. A 2D ViT cuts an image into non-overlapping spatial patches; SpectralGPT cuts the cube into non-overlapping 3D tensor tokens of size p × p × k pixels — a small square of space (p = 8) carrying a thin stack of bands (k = 3). Every token is itself a little cube.
Take the stage-1 cube X ∈ ℝ96×96×12. Slice it with a stride of 8 in each spatial direction and 3 along bands. The number of tokens along each axis is:
So a single 96×96×12 cube becomes a sequence of 576 3D tokens. For the stage-2 cube (128×128×12) the same token size gives 16 × 16 × 4 = 1024 tokens. The spectral axis is split into nD = 4 spectral groups: bands {1,2,3}, {4,5,6}, {7,8,9}, {10,11,12}. This is the crucial difference from a 2D ViT — there, the band axis would be nD = 1 (all bands crammed into one channel-stack). SpectralGPT keeps four distinct, ordered spectral positions, so attention can route information along the wavelength axis.
Each 8×8×3 token contains 8·8·3 = 192 raw reflectance values. A shared linear projection (implemented as a 3D convolution with kernel = stride = token size, the 3D analogue of a ViT patch-embed) maps that flattened 192-vector to a model embedding of dimension dmodel (768 for ViT-Base). Concretely, with the shared projection matrix Es:
The tensor-shape pipeline for one stage-1 cube, end to end through the embedder:
| Stage | Shape | Meaning |
|---|---|---|
| Raw cube | [96, 96, 12] | H × W × bands |
| 3D patchify (stride 8,8,3) | [12, 12, 4, 8, 8, 3] | token grid × token contents |
| Flatten token grid | [576, 192] | 576 tokens, each a 192-vector |
| Linear embed Es | [576, 768] | 576 tokens in model space |
| + positional embeddings | [576, 768] | spatial pos + spectral pos added |
A subtle but essential detail: SpectralGPT adds two positional embeddings to each token. One encodes the spatial location (which of the 12×12 grid cells), the other encodes the spectral position (which of the 4 band-groups). Because attention is permutation-invariant, without the spectral positional embedding the model could not tell band-group {1,2,3} from band-group {10,11,12} — it would lose the very ordering that makes the data spectral. The spatial embedding does the same job a 2D ViT's does; the spectral embedding is what 2D models lack.
A 96×96×12 cube cut into 8×8×3 tokens. Use the sliders to change spatial patch size p and spectral slice k; watch the token count N = (96/p)² × (12/k) update. Smaller tokens (the paper's choice) mean more, finer tokens — more fine-grained spatial-spectral detail preserved into deeper layers.
Token grid index (r=1, c=1, g=2), zero-based, with p=8, k=3, picks the raw region rows 8..15, columns 8..15, bands 6..8 (the second-to-last spectral group). That is one 8×8×3 sub-cube = 192 values. Mask this token and the model must reconstruct those 192 numbers using only the visible neighbours — both spatial (the surrounding tokens at the same band-group) and spectral (the same spatial patch at other band-groups). The spectral neighbours are the ones an RGB MAE never had.
Now the self-supervised heart. SpectralGPT follows the masked-autoencoder (MAE) recipe: corrupt the input by hiding most of it, then train the network to fill in what was hidden. The twist is what gets hidden. In image MAE you mask spatial patches; in SpectralGPT you mask 3D tokens, each one a chunk of space and bands. Hiding a 3D token hides an 8×8 spatial region at three specific wavelengths simultaneously.
Formally, a random binary mask M is drawn over the token grid:
where ρ is the masking ratio. The tokens with M = 1 are visible (xvis); the tokens with M = 0 are masked (xmask) and never shown to the encoder. SpectralGPT sets ρ = 0.90: ninety percent of tokens are dropped before the encoder ever runs.
Out of N = 576 tokens, the encoder sees only (1 − 0.90) · 576 = 57.6 ≈ 58 visible tokens, and must enable the decoder to reconstruct the remaining ~518. That is a brutal ratio — far harsher than the 75% used by image MAE, where 25% of patches (about 49 of 196) are kept. Why can spectral data tolerate, and even benefit from, hiding so much more?
Because the token grid is 3D, "random masking" is genuinely 3D: a masked token removes a specific spatial patch at a specific band-group. This is strictly more informative than 2D masking, where you would remove an entire spatial column through all bands at once. Compare:
| Masking scheme | What a masked unit hides | Reconstruction must use… |
|---|---|---|
| 2D spatial mask (image MAE) | A spatial patch through all bands | Only spatial neighbours |
| 3D token mask (SpectralGPT) | A spatial patch at one band-group | Spatial and spectral neighbours — the same patch at other band-groups is still visible |
This is the mechanism that injects the spectral signal into the learning objective. When you hide the {7,8,9} slice of a spatial patch but leave its {1,2,3} and {10,11,12} slices visible, the only way to reconstruct the missing infrared is to learn how a pixel's visible/SWIR response constrains its NIR response — i.e., to learn the shape of the spectral curve. A 2D mask, hiding all bands of a patch at once, never poses this spectral-completion question.
The 12×12×4 token grid, drawn as 4 stacked spectral slices. Drag the ratio ρ and re-roll the mask. Watch how many tokens survive — at ρ = 0.90, only ~58 of 576 are visible (lit). Note that masking is per-(patch, band-group): a patch can be hidden in one slice and visible in another, which is what poses the spectral-completion task.
SpectralGPT inherits MAE's asymmetric encoder-decoder design, and the asymmetry is the whole efficiency trick. A heavy encoder processes only the visible tokens; a light decoder sees everything and does the reconstruction. After pretraining the decoder is thrown away — only the encoder transfers to downstream tasks.
The encoder is a vanilla ViT (Base = 12 layers / 768-dim / ~86M params; Large = 24 layers; Huge = 32 layers). It receives only the ~58 visible tokens, each already embedded to 768 dims with its spatial and spectral positional embeddings. Because self-attention is O(N2) in token count, running the encoder on 58 tokens instead of 576 cuts attention cost by roughly (58/576)2 ≈ 1% of the full-sequence cost. This is why MAE-style pretraining is cheap enough to run at the million-image scale: the expensive part never touches the masked tokens.
Now reconstruction. The decoder is deliberately narrower and shallower than the encoder — the SpectralGPT default is 4 transformer blocks at a smaller width. The decoder input is the full grid of 576 positions, assembled as:
The single learned mask token is the elegant part: the model does not need 518 different "I don't know" vectors. It uses one shared placeholder and relies entirely on the positional embeddings (spatial + spectral) to tell each masked slot where in space and wavelength it sits. The decoder's job is then "given where you are and what your visible neighbours encoded, predict your 192 numbers." Because every masked slot can attend to every visible latent across the whole grid, the reconstruction integrates long-range spatial and spectral evidence in one shot.
| Choice | Why |
|---|---|
| Encoder sees only visible tokens | O(N²) attention ⇒ running on 58 not 576 tokens makes pretraining ~100× cheaper |
| Decoder is shallow / narrow | Reconstruction is an easier task than representation; a heavy decoder wastes compute and can let the encoder offload work it should be doing |
| Single shared mask token | Position is supplied by positional embeddings, so one learned placeholder suffices for all masked slots |
| Decoder discarded after pretraining | Only the encoder's features transfer; the decoder exists solely to define the reconstruction loss during pretraining |
Step through the pipeline: tokenize → mask 90% → encode only visible → splice in mask tokens → decode the full grid → reconstruct. Watch the token counts and tensor shapes change at each stage. The encoder bar shrinks to ~10%; the decoder bar expands back to 100%.
This is SpectralGPT's signature contribution and where the spectral physics enters the objective. A vanilla MAE has one loss: predict the masked pixels. SpectralGPT uses two reconstruction targets summed together — a token-level term and a spectral-wise term — so the model is rewarded not just for getting pixels right, but for getting the spectral sequence right.
Both terms are mean-squared errors, but computed over different groupings of the reconstruction. Let x be the (normalized) ground-truth values and x̂ the decoder's predictions. We derive each term.
The standard MAE term — average squared error over the masked tokens. With m masked tokens:
This says: for each hidden 8×8×3 token, the predicted 192 numbers should match the true 192 numbers. It is the spatial-spectral "fill in the blank" loss. Computed only on masked tokens (the visible ones are trivially known), exactly as in image MAE.
Here is the new idea. Fix a spatial location (r, c). Walking down the spectral axis at that location gives a sequence of nD = 4 spectral-group values: [xr,c,1, xr,c,2, xr,c,3, xr,c,4] (each entry a band-group). The spectral-wise loss asks that this whole spectral profile at each spatial location be reconstructed coherently:
where sj is the spectral sequence at the j-th spatial location and n is the number of such spatial locations. The difference from ℒtoken is the grouping: ℒtoken averages error per spatial-spectral chunk; ℒspectral averages error per spectral profile, tying together all band-groups at a fixed (r, c). This explicitly penalises a reconstruction that gets each band roughly right but produces a spectrally inconsistent curve (e.g., a kink that no real material would have).
One more design decision with a measured payoff. Before computing the loss, the targets are normalized (scaled into [0,1]) rather than used raw. The paper tests three target choices — raw values, [0,1] normalization, and per-patch standardization (mean 0, std 1) — and finds normalization gives the best transfer, beating raw targets by about 0.66 mAP points downstream. The reason mirrors the √dk story in attention: well-scaled targets keep the MSE gradient well-conditioned across bands whose raw reflectance magnitudes differ a lot, so no single bright band dominates the loss.
Suppose at one spatial location the true (normalized) spectral profile is s = [0.20, 0.30, 0.80, 0.75] (low visible, high NIR — a vegetation-like curve) and the decoder predicts ŝ = [0.20, 0.30, 0.50, 0.75]. The token term might be small (three of four entries are exact). But the spectral term sees that the third entry — the NIR peak — is badly off: (0.80 − 0.50)2 = 0.09, dominating the profile error and signalling that the reconstructed curve has lost its red-edge. Squared error on the profile makes that single physically-critical mistake expensive, which is precisely the point.
python # SpectralGPT multi-target reconstruction loss (schematic) import torch import torch.nn.functional as F def spectralgpt_loss(x, x_hat, mask, lam=0.5): """ x, x_hat : [B, n_tokens, 192] normalized true / predicted tokens mask : [B, n_tokens] 1 = masked (compute loss), 0 = visible """ # --- Term 1: token-level MSE on masked tokens only --- per_tok = ((x - x_hat) ** 2).mean(dim=-1) # [B, n_tokens] L_token = (per_tok * mask).sum() / mask.sum() # --- Term 2: spectral-wise MSE over the band-group profile --- # reshape so the spectral (band-group) axis is grouped per spatial loc xs = x.reshape(x.size(0), -1, N_D, 192) # [B, n_spatial, n_D, 192] xh = x_hat.reshape(x.size(0), -1, N_D, 192) L_spec = ((xs - xh) ** 2).mean() # error along the spectral profile return L_token + lam * L_spec
A foundation model is only as good as the data it sees, and remote-sensing data comes in many sizes, resolutions, dates, and regions. SpectralGPT's answer is progressive training: pretrain in two stages, starting on a large but lower-quality corpus and continuing on a smaller, higher-quality, more varied one. The model "warms up" on quantity and "refines" on quality and diversity.
| Stage | Dataset | Images | Cube size | Role |
|---|---|---|---|---|
| 1 | fMoW-S2 | 712,874 | 96 × 96 × 12 | Scale: learn broad spatial-spectral priors |
| 2 | BigEarthNet-S2 | 354,196 | 128 × 128 × 12 | Quality + variety: larger tiles, time series, more regions |
| Total pretraining | 1,067,070 | 96 → 128 | One million Sentinel-2 cubes, no labels | |
Two reasons, both about easing the optimisation. First, the cube size grows from 96 to 128 between stages. Because the token size (8×8×3) is fixed, the same architecture handles both — a 96-cube is 576 tokens, a 128-cube is 1024 tokens, and the ViT simply processes a longer sequence. Starting on the shorter sequence lets the model lock in local spatial-spectral structure before being asked to integrate over a larger field of view. Second, fMoW-S2 is large but comparatively lower-quality; BigEarthNet-S2 adds temporal (time-series) and geographic diversity. Learning easy-and-plentiful first, then hard-and-varied, is a curriculum that the paper reports improves final transfer.
SpectralGPT ships three sizes, scaling the ViT backbone exactly as image ViTs do. The ">600M params" headline refers to the Huge variant; the family spans roughly:
| Variant | Backbone | Layers | ~Params |
|---|---|---|---|
| SpectralGPT | ViT-Base | 12 | ~100M |
| SpectralGPT | ViT-Large | 24 | ~300M |
| SpectralGPT-Huge | ViT-Huge | 32 | ~600M+ |
The variant trained with the full two-stage progressive schedule is denoted SpectralGPT++ and is the one that posts the headline numbers. The "++" is the progressive-training boost on top of the base pretrained model.
Stage 1 (fMoW-S2, 96-cube, 576 tokens) then Stage 2 (BigEarthNet-S2, 128-cube, 1024 tokens). The token size is fixed, so growing the cube simply lengthens the sequence. Click to step through the curriculum and watch the token count and data character change.
A foundation model lives or dies on transfer. SpectralGPT is pretrained once with no labels, then the encoder is fine-tuned on a battery of downstream tasks. The headline: it sets the state of the art across scene classification, multi-label tagging, semantic segmentation, and change detection on multispectral benchmarks.
| Task | Benchmark | Metric | SpectralGPT++ | Prior best (e.g. SatMAE) |
|---|---|---|---|---|
| Single-label scene classification | EuroSAT | Overall accuracy | ~99.21% | ~99.09% |
| Multi-label classification | BigEarthNet | macro / micro mAP | ~88.2% / ~87.5% | Lower across the board |
| Few-shot (10% labels) | BigEarthNet | mAP | Beats prior full-data models | — |
| Segmentation / change detection | Multiple RS sets | mIoU / F1 | SOTA | — |
Read the EuroSAT line carefully: the gain over SatMAE is small in absolute terms (~0.1 points) because EuroSAT is nearly saturated — almost everything scores above 99%. The more telling result is BigEarthNet, a harder multi-label task with 19+ co-occurring land-cover classes, where the margin is larger, and especially the few-shot result: SpectralGPT fine-tuned on only 10% of the labels can match or beat prior models that used all the labels. That data efficiency is the real payoff of strong self-supervised pretraining — the encoder already "knows" the spectral world, so it needs few labels to specialise.
The paper isolates each design choice. The findings, in order of impact:
A schematic of the masking-ratio sweep: downstream transfer (higher is better) as a function of pretraining masking ratio ρ, for spectral cubes vs. ordinary RGB. Drag the marker. The spectral curve peaks near 90% (redundancy lets you hide more); the RGB curve peaks near 75%. The peaks differ because the data redundancy differs.
Every ablation points the same direction: the spectral axis was the missing ingredient. The two largest levers — the 90% masking ratio and the small 3D token — both exist only because the model treats wavelength as a real, redundant, ordered dimension. The multi-target loss and normalization are refinements on top. SpectralGPT's lesson for the field is that you do not get a great multispectral model by porting an RGB recipe; you get it by redesigning tokenization, masking, and loss around the physics of the spectrum.
python # Fine-tuning SpectralGPT for a downstream task (schematic) import torch.nn as nn # 1. Load the pretrained ENCODER only (decoder is discarded) encoder = load_spectralgpt_encoder("spectralgpt-base.pt") # 2. Attach a small task head (e.g. linear classifier for EuroSAT) class SceneClassifier(nn.Module): def __init__(self, encoder, n_classes=10): super().__init__() self.encoder = encoder # pretrained ViT, sees ALL tokens now self.head = nn.Linear(768, n_classes) def forward(self, cube): # cube: [B, H, W, 12] tokens = patchify_3d(cube) # [B, N, 192], NO masking at fine-tune feats = self.encoder(tokens) # [B, N, 768] return self.head(feats.mean(1)) # global-pool -> logits # 3. Fine-tune with few labels — pretraining did the heavy lifting
This is the showcase — SpectralGPT's full mechanism in one interactive loop. You drive a small spectral cube through the entire self-supervised pipeline: choose a material so the cube has a real spectral signature, set the masking ratio, re-roll the random 3D mask, and watch the encoder-decoder reconstruct the hidden tokens. Then read off both loss terms. Everything you learned in Chapters 1–7 is on screen at once.
Left: the true cube as 4 stacked spectral slices. Middle: the masked input the encoder actually sees (dark = hidden). Right: the decoder's reconstruction. Below: the per-cell squared error and the two loss terms. Raise ρ to make the task harder and watch reconstruction quality and loss respond. Switch material to change the spectral curve being completed.
Three things to notice as you play. First, raising ρ thins the visible tokens and makes the reconstruction visibly worse where holes cluster — the harder the task, the more the model must rely on global structure. Second, the squared-error map lights up most on the infrared slices for vegetation, because the NIR peak is the most informative and therefore the most expensive to miss — exactly what the spectral loss is designed to penalise. Third, water (near-zero infrared, almost flat curve) is easy to reconstruct, while vegetation (a sharp red-edge) is hard; the loss reflects the information content of the material's spectrum.
SpectralGPT is a milestone, not an endpoint. Holding it up against its own design reveals the open problems, and placing it in the lineage of self-supervised vision shows where its ideas came from and where they go next.
| Limitation | Why it bites |
|---|---|
| Sentinel-2 specific | Pretrained on 12-band Sentinel-2. Transfer to hyperspectral sensors (hundreds of bands) or sensors with different band sets needs adaptation — the spectral positional scheme assumes the trained band layout. |
| O(N²) attention | Larger cubes mean more tokens (1024 at 128px) and quadratic attention cost. Very high-resolution or many-band cubes strain memory — the same scaling wall every ViT hits. |
| Reconstruction ≠ semantics | MAE optimises pixel reconstruction, a proxy for representation quality. A perfectly reconstructed cube is not guaranteed to yield the best semantic features; contrastive or hybrid objectives may complement it. |
| Atmospheric / temporal nuisances | Clouds, haze, and seasonal change are spectral nuisances the model must learn to ignore; progressive training helps but does not fully solve domain shift across regions and dates. |
| Single modality | Uses only optical multispectral. Fusing SAR, LiDAR, or weather would enrich the representation but the 3D-token recipe must be extended to heterogeneous modalities. |
| Concept | SpectralGPT's answer |
|---|---|
| Input | Spectral cube [H, W, 12] (Sentinel-2, B10 dropped) |
| Token | 3D tensor token, p×p×k = 8×8×3 pixels (192 values) |
| Token count | (96/8)²×(12/3) = 12×12×4 = 576 (96-cube); 1024 (128-cube) |
| Positional | Two embeddings: spatial position + spectral position |
| Masking | Random 3D mask, ρ = 90% (vs 75% for RGB MAE) |
| Encoder | Vanilla ViT (Base/Large/Huge), sees only ~58 visible tokens |
| Decoder | Shallow/narrow (~4 blocks), sees full grid, predicts 192-vec per token, discarded after pretraining |
| Loss | ℒ = ℒtoken + λℒspectral (MSE on normalized targets) |
| Pretraining | Progressive: fMoW-S2 (96) → BigEarthNet-S2 (128), ~1.07M cubes, no labels |
| Params | ~100M / ~300M / ~600M+ (Base / Large / Huge) |
| Headline | EuroSAT ~99.21%, BigEarthNet SOTA, strong 10%-label few-shot |
Deepen the building blocks this paper assumes: