GeoMaster 地理空间机器学习实战指南:遥感影像分类、语义分割与可解释 AI 全流程
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
本文是开源科学 Agent 技能库 scientific-agent-skills 中 GeoMaster 技能(skills/geomaster/SKILL.md)的核心参考文档之一,系统讲解面向遥感与空间分析的机器学习和深度学习应用。读者将掌握从传统机器学习(随机森林、SVM)到深度学习(CNN、U-Net、孪生网络)、图神经网络(GNN)再到可解释 AI(SHAP、Grad-CAM)的完整技术栈,以及如何结合 Rasterio、GeoPandas、PyTorch、TorchGeo、PyTorch Geometric 等库,将理论落地为可复现的土地覆盖分类、变化检测与空间预测方案。
一、环境准备与依赖安装
在动手之前,先按照 GeoMaster 技能主文档 SKILL.md 的安装说明准备好 Python 环境。GeoMaster 覆盖遥感、GIS、空间分析与地球观测机器学习等 70+ 主题,涉及栅格、矢量、点云三类核心数据(Vector: GeoJSON/Shapefile/GeoPackage;Raster: GeoTIFF/NetCDF/COG;Point Cloud: LAS/LAZ)。本文涉及的机器学习相关依赖可按如下方式安装:
# 核心 Python 栈(建议 conda) conda install -c conda-forge gdal rasterio fiona shapely pyproj geopandas # 遥感与机器学习 uv pip install rsgislib torchgeo earthengine-api uv pip install scikit-learn xgboost torch-geometric其中torchgeo提供面向地理空间的 PyTorch 数据集与预训练模型,torch-geometric用于图神经网络,scikit-learn承载传统机器学习算法。更多遥感数据获取与预处理细节可参考 remote-sensing.md。
二、传统机器学习:土地覆盖分类的两条经典路线
2.1 随机森林(Random Forest)用于影像分类
随机森林是遥感影像分类的经典基线算法,其核心思想是用矢量训练样本(通常是人工标注的多边形)通过栅格化(rasterize)从多波段影像中抽取逐像素训练数据,再训练集成树模型。GeoMaster 给出了完整可运行流程:
from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report, confusion_matrix import rasterio from rasterio.features import rasterize import geopandas as gpd import numpy as np import pandas as pd def train_random_forest_classifier(raster_path, training_gdf): """Train Random Forest for image classification.""" # Load imagery with rasterio.open(raster_path) as src: image = src.read() profile = src.profile transform = src.transform # Extract training data X, y = [], [] for _, row in training_gdf.iterrows(): mask = rasterize( [(row.geometry, 1)], out_shape=(profile['height'], profile['width']), transform=transform, fill=0, dtype=np.uint8 ) pixels = image[:, mask > 0].T X.extend(pixels) y.extend([row['class_id']] * len(pixels)) X = np.array(X) y = np.array(y) # Split data X_train, X_val, y_train, y_val = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # Train model rf = RandomForestClassifier( n_estimators=100, max_depth=20, min_samples_split=10, min_samples_leaf=4, class_weight='balanced', n_jobs=-1, random_state=42 ) rf.fit(X_train, y_train) # Validate y_pred = rf.predict(X_val) print("Classification Report:") print(classification_report(y_val, y_pred)) # Feature importance feature_names = [f'Band_{i}' for i in range(X.shape[1])] importances = pd.DataFrame({ 'feature': feature_names, 'importance': rf.feature_importances_ }).sort_values('importance', ascending=False) print("\nFeature Importance:") print(importances) return rf # Classify full image def classify_image(model, image_path, output_path): with rasterio.open(image_path) as src: image = src.read() profile = src.profile image_reshaped = image.reshape(image.shape[0], -1).T prediction = model.predict(image_reshaped) prediction = prediction.reshape(image.shape[1], image.shape[2]) profile.update(dtype=rasterio.uint8, count=1) with rasterio.open(output_path, 'w', **profile) as dst: dst.write(prediction.astype(rasterio.uint8), 1)关键参数解读:
- 训练样本抽取:
rasterize将训练多边形的几何边界在影像的height × width网格与transform坐标系下栅格化,mask > 0处的像元即为该类别样本。这一"多边形标注 → 逐像素样本"的模式是遥感监督分类的标准做法。 stratify=y:按类别比例分层划分训练/验证集,避免小类别在划分时丢失,对类别不平衡的遥感数据尤为重要。class_weight='balanced':为样本少的类别自动加权,缓解土地覆盖类型之间面积差异悬殊带来的偏置。n_jobs=-1:使用全部 CPU 核心并行训练,GeoMaster 的性能建议(见 SKILL.md)中同样强调用n_jobs=-1加速大范围预测。- 特征重要性:
rf.feature_importances_直接输出各波段对分类的贡献,可用于判断 Sentinel-2 的哪个波段(如 NIR/SWIR)对当前地物区分最有效。
推理阶段将整幅影像展平为(H*W, bands)后一次性预测,再还原为二维分类图并以uint8单波段 GeoTIFF 写出。
2.2 支持向量机(SVM)
SVM 在小样本、高维光谱数据上表现出色。由于 RBF 核依赖距离度量,特征标准化(StandardScaler)是前提:
from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler def svm_classifier(X_train, y_train): """SVM classifier for remote sensing.""" # Scale features scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) # Train SVM svm = SVC( kernel='rbf', C=100, gamma='scale', class_weight='balanced', probability=True ) svm.fit(X_train_scaled, y_train) return svm, scaler # Multi-class classification def multiclass_svm(X_train, y_train): from sklearn.multiclass import OneVsRestClassifier scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) svm_ovr = OneVsRestClassifier( SVC(kernel='rbf', C=10, probability=True), n_jobs=-1 ) svm_ovr.fit(X_train_scaled, y_train) return svm_ovr, scaler实现要点:
C=100与gamma='scale':较大的正则化参数 C 意味着更严格拟合训练数据;gamma='scale'让核宽度自动按特征数自适应,避免手动调参。probability=True:启用 Platt 缩放以输出类别概率,便于后续做置信度阈值过滤或与随机森林的概率输出对比。- 多分类策略:
OneVsRestClassifier将多分类分解为一对多二分类问题,配合n_jobs=-1并行训练多个二分类器。注意使用时需先对训练集调用scaler.fit_transform,对预测数据同样先scaler.transform,避免数据泄漏。
三、深度学习:从卷积网络到分割与变化检测
3.1 TorchGeo + CNN 图像分类
对于 Sentinel-2 这类多光谱影像,卷积神经网络(CNN)能自动学习光谱-空间联合特征。示例中的LandCoverCNN采用编码器-解码器结构,in_channels=12恰好对应 Sentinel-2 的 12 个波段(配合 remote-sensing.md 中 Sentinel-2 的波段划分理解):编码器逐步下采样提取高层特征,解码器用转置卷积恢复到输入分辨率,实现逐像元分类(语义分割式输出)。
import torch import torch.nn as nn import torchgeo.datasets as datasets import torchgeo.models as models from torch.utils.data import DataLoader # Define CNN class LandCoverCNN(nn.Module): def __init__(self, in_channels=12, num_classes=10): super().__init__() self.encoder = nn.Sequential( nn.Conv2d(in_channels, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding=1), nn.BatchNorm2d(128), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(128, 256, 3, padding=1), nn.BatchNorm2d(256), nn.ReLU(), nn.MaxPool2d(2), ) self.decoder = nn.Sequential( nn.ConvTranspose2d(256, 128, 2, stride=2), nn.BatchNorm2d(128), nn.ReLU(), nn.ConvTranspose2d(128, 64, 2, stride=2), nn.BatchNorm2d(64), nn.ReLU(), nn.ConvTranspose2d(64, num_classes, 2, stride=2), ) def forward(self, x): x = self.encoder(x) x = self.decoder(x) return x # Training def train_model(train_loader, val_loader, num_epochs=50): device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = LandCoverCNN().to(device) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) for epoch in range(num_epochs): model.train() train_loss = 0 for images, labels in train_loader: images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() train_loss += loss.item() # Validation model.eval() val_loss = 0 with torch.no_grad(): for images, labels in val_loader: images, labels = images.to(device), labels.to(device) outputs = model(images) loss = criterion(outputs, labels) val_loss += loss.item() print(f'Epoch {epoch+1}/{num_epochs}, Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}') return model训练管线要点:
- 设备自适应:
cuda if available else cpu自动选择 GPU;训练循环中每轮迭代执行zero_grad → forward → loss → backward → step五步。 - CrossEntropyLoss + Adam:多分类任务的默认组合,
lr=0.001是 Adam 的常用初始学习率。 model.eval()+torch.no_grad():验证阶段关闭 dropout/BN 的统计更新并禁用梯度计算,避免内存浪费。- 配合 TorchGeo 的
datasets与models模块,可以直接加载 Sentinel-2/Landsat 等标准数据集构造DataLoader。大数据场景下的 GPU 训练加速(如pin_memory=True、混合精度autocast)可参考 big-data.md。
3.2 U-Net 语义分割
当需要逐像元的精细地物边界(如水体、建筑轮廓)时,U-Net 通过"编码器 + 瓶颈 + 解码器 + 跳跃连接"结构保留空间细节:编码器逐级池化扩大感受野,解码器逐级上采样,同时将编码器对应层的特征通过torch.cat拼接回来,让分割结果同时具备语义与细节信息。
class UNet(nn.Module): def __init__(self, in_channels=4, num_classes=5): super().__init__() # Encoder self.enc1 = self.conv_block(in_channels, 64) self.enc2 = self.conv_block(64, 128) self.enc3 = self.conv_block(128, 256) self.enc4 = self.conv_block(256, 512) # Bottleneck self.bottleneck = self.conv_block(512, 1024) # Decoder self.up1 = nn.ConvTranspose2d(1024, 512, 2, stride=2) self.dec1 = self.conv_block(1024, 512) self.up2 = nn.ConvTranspose2d(512, 256, 2, stride=2) self.dec2 = self.conv_block(512, 256) self.up3 = nn.ConvTranspose2d(256, 128, 2, stride=2) self.dec3 = self.conv_block(256, 128) self.up4 = nn.ConvTranspose2d(128, 64, 2, stride=2) self.dec4 = self.conv_block(128, 64) # Final layer self.final = nn.Conv2d(64, num_classes, 1) def conv_block(self, in_ch, out_ch): return nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True), nn.Conv2d(out_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True) ) def forward(self, x): # Encoder e1 = self.enc1(x) e2 = self.enc2(F.max_pool2d(e1, 2)) e3 = self.enc3(F.max_pool2d(e2, 2)) e4 = self.enc4(F.max_pool2d(e3, 2)) # Bottleneck b = self.bottleneck(F.max_pool2d(e4, 2)) # Decoder with skip connections d1 = self.dec1(torch.cat([self.up1(b), e4], dim=1)) d2 = self.dec2(torch.cat([self.up2(d1), e3], dim=1)) d3 = self.dec3(torch.cat([self.up3(d2), e2], dim=1)) d4 = self.dec4(torch.cat([self.up4(d3), e1], dim=1)) return self.final(d4)结构拆解:
- 双卷积块
conv_block:每个阶段包含两层3×3卷积 +BatchNorm+ReLU,是 U-Net 的基本构件。 - 通道数变化:64 → 128 → 256 → 512 → 1024 逐级翻倍;解码器转置卷积每次将空间尺寸翻倍、通道减半。
- 跳跃连接:
torch.cat([self.up1(b), e4], dim=1)将上采样特征与同尺度编码特征沿通道维拼接(因此dec1的输入通道为 512+512=1024),有效缓解深层网络中的梯度消失与细节丢失。in_channels=4与num_classes=5可按实际数据(如四波段影像、五类地物)调整。 - 注意该网络输出与输入等分辨率,配合
CrossEntropyLoss即可端到端训练分割模型,训练循环可复用 3.1 节中的train_model框架。
3.3 孪生网络(Siamese Network)变化检测
变化检测需要同时考察同一地理位置不同时相的影像。孪生网络的核心思想是权值共享:两个时相的影像通过同一个特征提取器得到特征f1、f2,计算其绝对差diff = |f1 - f2|,再将三者拼接送入分类头,输出"变化 / 未变化"的二分类结果。
class SiameseNetwork(nn.Module): """Siamese network for change detection.""" def __init__(self): super().__init__() self.feature_extractor = nn.Sequential( nn.Conv2d(3, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding=1), nn.BatchNorm2d(128), nn.ReLU(), ) self.classifier = nn.Sequential( nn.Conv2d(256, 128, 3, padding=1), nn.ReLU(), nn.Conv2d(128, 64, 3, padding=1), nn.ReLU(), nn.Conv2d(64, 2, 1), # Binary: change / no change ) def forward(self, x1, x2): f1 = self.feature_extractor(x1) f2 = self.feature_extractor(x2) # Concatenate features diff = torch.abs(f1 - f2) combined = torch.cat([f1, f2, diff], dim=1) return self.classifier(combined)设计要点:
- 共享的特征提取器保证了两个时相的特征位于同一语义空间,差异才具有可比性;
diff通道直接编码"哪里变了、变得多剧烈"。 - 分类器输入通道为 256(f1 128 + f2 128 + diff 128),输出 2 通道对应变化/未变化,训练时可用
CrossEntropyLoss,配合argmax(dim=1)得到变化掩膜。 - 这类方法天然适用于洪涝前后、城市扩张、森林砍伐等场景,可与 GeoMaster 的洪涝制图工作流(见 code-examples.md)串联使用。
四、图神经网络(GNN):用 k-NN 图建模空间邻域
传统 CNN 处理的是规则栅格,而采样点、路网、地块边界等不规则空间对象更适合用图建模。GeoMaster 给出了从"点数据构造 k-NN 空间图"到"GCN 空间预测"的完整流程:
import torch from torch_geometric.data import Data from torch_geometric.nn import GCNConv # Create spatial graph def create_spatial_graph(points_gdf, k_neighbors=5): """Create graph from point data using k-NN.""" from sklearn.neighbors import NearestNeighbors coords = np.array([[p.x, p.y] for p in points_gdf.geometry]) # Find k-nearest neighbors nbrs = NearestNeighbors(n_neighbors=k_neighbors).fit(coords) distances, indices = nbrs.kneighbors(coords) # Create edge index edge_index = [] for i, neighbors in enumerate(indices): for j in neighbors: edge_index.append([i, j]) edge_index = torch.tensor(edge_index, dtype=torch.long).t().contiguous() # Node features features = points_gdf.drop('geometry', axis=1).values x = torch.tensor(features, dtype=torch.float) return Data(x=x, edge_index=edge_index) # GCN for spatial prediction class SpatialGCN(torch.nn.Module): def __init__(self, num_features, hidden_channels=64): super().__init__() self.conv1 = GCNConv(num_features, hidden_channels) self.conv2 = GCNConv(hidden_channels, hidden_channels) self.conv3 = GCNConv(hidden_channels, 1) def forward(self, data): x, edge_index = data.x, data.edge_index x = self.conv1(x, edge_index).relu() x = F.dropout(x, p=0.5, training=self.training) x = self.conv2(x, edge_index).relu() x = self.conv3(x, edge_index) return x原理剖析:
- k-NN 构图:
NearestNeighbors(n_neighbors=k)为每个点找到距离最近的 k 个邻居,构成有向边edge_index(2×E的张量)。地理空间中的"邻近"由此显式编码,GCN 的每一层卷积即聚合邻居节点的特征。 - 节点特征:
points_gdf.drop('geometry', axis=1)将矢量属性表中的非几何列作为节点特征x。 - 三层 GCN:
num_features → 64 → 64 → 1,逐层聚合 1、2、3 跳邻域信息,输出回归值(如某种空间插值/预测目标)。中间层加F.dropout(p=0.5)抑制过拟合。 - 该模式同样可以扩展到边特征(如欧氏距离、道路长度)与更深的图卷积变体,是空间统计中 Moran's I、半变异函数等传统空间自相关分析的深度学习方法替代,相关传统方法参考 code-examples.md 中的热点分析、克里金插值示例。
五、可解释 AI(XAI):让地学模型"可被信任"
地球系统科学对模型可解释性要求极高——审稿人、决策者和领域专家都需要知道"模型为什么这么判"。GeoMaster 提供两种主流方案。
5.1 SHAP:解释任意模型的特征贡献
SHAP(SHapley Additive exPlanations)基于博弈论中的 Shapley 值,为每个样本的每个特征分配一个贡献值,正负号表示该特征对预测的推动方向:
import shap import numpy as np def explain_model(model, X, feature_names): """Explain model predictions using SHAP.""" # Create explainer explainer = shap.Explainer(model, X) # Calculate SHAP values shap_values = explainer(X) # Summary plot shap.summary_plot(shap_values, X, feature_names=feature_names) # Dependence plot for important features for i in range(X.shape[1]): shap.dependence_plot(i, shap_values, X, feature_names=feature_names) return shap_values # Spatial SHAP (accounting for spatial autocorrelation) def spatial_shap(model, X, coordinates): """Spatial explanation considering neighborhood effects.""" # Compute SHAP values explainer = shap.Explainer(model, X) shap_values = explainer(X) # Spatial aggregation shap_spatial = {} for i, coord in enumerate(coordinates): # Find neighbors neighbors = find_neighbors(coord, coordinates, radius=1000) # Aggregate SHAP values for neighborhood neighbor_shap = shap_values.values[neighbors] shap_spatial[i] = np.mean(neighbor_shap, axis=0) return shap_spatial关键点:
shap.summary_plot绘制特征重要性排序图(颜色表示特征值高低),shap.dependence_plot绘制单特征与 SHAP 值的关系曲线,可识别非线性效应(如 NDVI 对地物判别的饱和区间)。spatial_shap是 GeoMaster 针对地理数据特点的进阶扩展:地学数据存在空间自相关(邻近位置高度相似),孤立地解释单个样本意义有限。该函数以固定半径(示例为 1000 米)聚合邻域内样本的 SHAP 值取平均,得到"区域级"的解释,其中find_neighbors是需按实际数据结构实现的邻居查询函数(可用sklearn.neighbors.BallTree或scipy.spatial.cKDTree)。
5.2 Grad-CAM:CNN 的注意力热图
对于深度学习模型,Grad-CAM 用目标类别对特征图的梯度加权生成"哪里激活了模型判断"的类激活热图:
import cv2 import torch import torch.nn.functional as F def generate_attention_map(model, image_tensor, target_layer): """Generate attention map using Grad-CAM.""" # Forward pass model.eval() output = model(image_tensor) # Backward pass model.zero_grad() output[0, torch.argmax(output)].backward() # Get gradients gradients = model.get_gradient(target_layer) # Global average pooling weights = torch.mean(gradients, axis=(2, 3), keepdim=True) # Weighted combination of activation maps activations = model.get_activation(target_layer) attention = torch.sum(weights * activations, axis=1, keepdim=True) # ReLU and normalize attention = F.relu(attention) attention = F.interpolate(attention, size=image_tensor.shape[2:], mode='bilinear', align_corners=False) attention = (attention - attention.min()) / (attention.max() - attention.min()) return attention.squeeze().cpu().numpy()实现说明:
- 算法流程为:前向传播取得分最高的类别 → 对该类别反向传播得梯度 → 梯度全局平均池化得通道权重 → 权重与特征图加权求和 → ReLU 截断负值 → 双线性插值上采样到原图尺寸 → 归一化到
[0,1]。 - 示例中的
model.get_gradient(target_layer)与model.get_activation(target_layer)并非 PyTorch 内置方法,需要在模型上通过register_forward_hook(捕获激活)与register_full_backward_hook(捕获梯度)自行注册钩子实现,这是 Grad-CAM 的标准实现细节。 - 生成的注意力热图可与原始遥感影像叠加(
cv2.addWeighted),直观展示模型是依据水体、植被还是裸土区域作出判断,是模型审计与论文可视化的常用手段。
六、深入探索
本文内容源自 GeoMaster 技能的机器学习参考文档,完整的 500+ 代码示例可按需继续阅读仓库内的配套资料:
- GeoMaster 技能主文档:安装、快速上手、NDVI 计算、STAC/COG 云原生工作流与性能优化建议;
- code-examples.md:涵盖 Python/R/Julia/JavaScript 的完整分类、分割、插值、空间统计示例;
- remote-sensing.md:Sentinel-2/Landsat/SAR/高光谱数据的获取与预处理(云掩膜、大气校正、全色锐化);
- big-data.md:Dask 分布式处理、GPU 加速(CuPy、RAPIDS、混合精度训练)与高效数据格式(COG、Zarr、Parquet)。
七、最佳实践小结
结合 GeoMaster 整体技能的最佳实践(见 SKILL.md),机器学习建模阶段应始终遵循:
- 统一坐标系(CRS):训练样本与影像必须处于同一 CRS,栅格化前务必确认
src.transform与矢量 CRS 一致; - 样本质量控制:用
gdf.is_valid过滤无效几何,处理缺失几何,确保训练多边形与影像严格配准; - 类别不平衡处理:优先使用
stratify划分与class_weight='balanced',必要时对样本量少的类别做过采样; - 光学影像先去云:对 Sentinel-2 使用
SCL场景分类层或 QA60 位掩码去除云与卷云(参考 remote-sensing.md); - 可解释性先行:对地学决策类任务,模型上线前用 SHAP 与 Grad-CAM 完成特征与空间层面的审计;
- 记录数据血缘:记录卫星、轨道、日期、预处理版本,保证研究可复现。
从随机森林到 GCN、从黑盒预测到可解释审计,GeoMaster 提供的这套机器学习方法论覆盖了地理空间科学从数据到决策的完整链路,可直接应用于土地覆盖制图、变化检测、灾害监测与生态建模等任务。
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考