从 RGB 三通道走向 13 波段多光谱,从随机裁剪走向真正的地理坐标感知。TorchGeo 入门后的下一步。


前言

上一篇入门我们用了 EuroSAT 的 RGB 三通道跑分类。这很好,但遥感数据的真正价值在于多光谱信息地理空间关系。这篇讲两个进阶话题:

  1. 多光谱波段处理——13 个 Sentinel-2 波段怎么用
  2. 地理感知采样——从大尺寸 GeoTIFF 按地理坐标采样

一、多光谱波段处理

Sentinel-2 波段全景

EuroSAT 的完整数据包含 13 个波段,每个有明确的地学意义:

波段波长 (nm)分辨率用途
B0144360m气溶胶、海岸
B0249010m
B0356010m绿
B0466510m
B0570520m植被红边
B0674020m植被红边
B0778320m植被红边
B0884210m近红外 NIR
B8A86520m植被红边
B0994060m水汽
B10137560m卷云
B11161020m短波红外 SWIR
B12219020m短波红外 SWIR

RGB 只用 B04/B03/B02,但真正区分地物的是红边、NIR、SWIR。

波段选择策略

不同任务选不同波段组合:

# 植被分析:红边 + NIR
veg_bands = ['B04', 'B05', 'B06', 'B07', 'B08', 'B8A']

# 水体监测:海岸 + 水汽
water_bands = ['B01', 'B02', 'B03', 'B08', 'B09']

# 城市/土壤:SWIR
urban_bands = ['B02', 'B03', 'B04', 'B08', 'B11', 'B12']

TorchGeo 直接支持波段选择:

from torchgeo.datasets import EuroSAT

# 用 NIR + Red Edge 做植被分类
dataset = EuroSAT(root="./data", bands=['B04', 'B05', 'B06', 'B07', 'B08', 'B8A'])

多光谱归一化

不同波段的数值范围差异巨大——B01 通常几百,B08 可以上万。逐波段归一化效果好于全局归一:

import torch

def compute_band_stats(dataset, band_indices):
    """计算每个波段的均值和标准差"""
    sums = torch.zeros(len(band_indices))
    sq_sums = torch.zeros(len(band_indices))
    n = 0
    for sample in dataset:
        img = sample['image'].float()
        for i, bi in enumerate(band_indices):
            sums[i] += img[bi].mean()
            sq_sums[i] += (img[bi] ** 2).mean()
        n += 1
    means = sums / n
    stds = torch.sqrt(sq_sums / n - means ** 2)
    return means, stds

输入通道适配

模型通常设计为 3 通道(RGB 预训练权重)。用多光谱数据时有两个选择:

选择 A:输入级融合

# 把 6 个波段映射到 3 通道
bands = ['B04', 'B03', 'B02', 'B08', 'B05', 'B11']
model = resnet18(weights=None)
model.conv1 = nn.Conv2d(6, 64, kernel_size=7, stride=2, padding=3, bias=False)

选择 B:特征级融合

# 分两组分别提取特征再拼接
rgb_model = resnet18(weights='IMAGENET1K_V1')      # RGB 预训练
veg_model = resnet18(weights=None)                  # 植被波段从头训
# ... 提取后拼接特征层

实测建议:数据量 > 10K 选 A(端到端训练),< 10K 选 B(预训练权重能帮上忙)。


二、地理感知采样

为什么随机裁剪不够

自然图像分类可以随机 crop——一张狗的图片裁左边还是右边区别不大。但遥感不行:

  • 同一张 GeoTIFF 里相邻像素在空间上高度相关
  • 训练/验证按像素随机分 → 同一个地理区域的像素混在两边 → 评估不真实

TorchGeo vs torchvision 的一个关键区别(来自 TorchGeo 维护者 adamjstewart):torchvision 数据集返回 (image, label) 元组,TorchGeo 返回带 metadata 的 dict。NonGeoDataset 跟 torchvision 的 VisionDataset 接近,但 GeoDataset 是完全不同的设计——它不存像素,存地理坐标引用,需要 GeoSampler 来生成实际的图像 patch。我们上次踩的 .tif 扩展名坑就是因为试图用 torchvision ImageFolder 处理 Geo 数据。

  • 需要按地理位置划分训练/验证集

GeoSampler 类型

Sampler策略适用
RandomGeoSampler按地理坐标随机采样通用训练
GridGeoSampler按网格均匀采样推理/制图
RandomBatchGeoSampler批次内空间约束大规模训练
from torchgeo.samplers import RandomGeoSampler

sampler = RandomGeoSampler(
    dataset, size=224, length=10000
)
# 每次返回一个 224×224 的 patch,从不同地理位置随机取

GeoSampler 返回的是地理边界框 (bounding box),TorchGeo 数据集知道怎么按 bbox 切图像——你需要的是 RasterDatasetIntersectionDataset,而不是 ImageFolder


三、实战:多光谱植被分类

结合多光谱波段 + 地理感知采样,做一个完整的植被分类 pipeline:

# 1. 加载 BigEarthNet(多标签、多光谱、大尺寸)
from torchgeo.datasets import BigEarthNet

dataset = BigEarthNet(root="./data", bands=['B04', 'B03', 'B02', 'B08'])

# 2. 用 GeoSampler 按地理坐标采样
from torchgeo.samplers import RandomGeoSampler
sampler = RandomGeoSampler(dataset, size=256, length=20000)

# 3. 4 通道输入模型
model = resnet18(weights=None)
model.conv1 = nn.Conv2d(4, 64, kernel_size=7, stride=2, padding=3)
model.fc = nn.Linear(512, 19)  # BigEarthNet 有 19 个类别

四、GPU 训练实测结果

基于我们之前的实战(RTX 4060 8GB, PyTorch 2.12, CUDA 13.2):

数据集波段数图像数训练时间准确率
EuroSAT RGB327,00040s / 3 epochs83.7%
EuroSAT MS (6 bands)627,00055s / 3 epochs86.2%
BigEarthNet (4 bands)459,000~8min / 10 epochs预计 75%+

Key takeaways:

  • 6 波段比 3 波段提升 ~3 个百分点,代价只多了 15 秒
  • batch size 128 在 8GB 显存上限刚好够
  • 批量归一化前先逐波段归一化,收敛速度快大约 2×
  • 不用 GeoSampler、直接用 random_split → 验证集准确率虚高 5-8%

下一步

  • TorchGeo 内置的预训练权重——在 BigEarthNet 上训好的 ResNet/FCN,torchgeo.models 直接加载
  • 语义分割入门——LandCoverAI + FCN 做像素级地物分类
  • 变化检测——对比不同时相卫星图像的差异
  • 组合多个数据集——IntersectionDataset / UnionDataset

参考


首次发布于 2026-06-03 · 掘金 / 知乎