系列回顾:
(一)CNN 基础:高光谱图像分类可视化全流程
(二)HybridNet(CNN+Transformer):提升全局感受野
(三)GCN 入门实战:基于光谱 KNN 的图卷积分类与全图预测
(四)空间–光谱联合构图的 GCN:RBF 边权 + 自环 + 早停,得到更稳更自然的全图分类结果
(五)GAT & 构图消融 + 分块全图预测:更稳更快的高光谱图分类(PyTorch Geometric 实战)
合集链接:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkwMTE0MjI4NQ==&action=getalbum&album_id=4007114522736459789#wechat_redirect
本篇(六):聚焦“数据泄露”,采用仅训练集像素拟合 StandardScaler+PCA,并在全图预测中共享同一变换空间;模型选用轻量化 MobileNetV2 的深度可分离卷积,在显存友好的坐标批推理下实现全图预测。
0. 前言:PCA 与高光谱分类中的“数据泄露”
- 什么是泄露? 训练阶段直接/间接使用了测试数据统计信息(均值、方差、主成分方向等)。
- 怎么产生? 在整图上
fit标准化与 PCA,然后再切训练/测试或直接做全图分类。 - 为什么常见? 历史习惯、样本少时稳定性考虑、对比研究图省事、实现方便。
- 影响大吗? 小数据集上常为 0.1%~1% 的 OA 差异;但在类分布差异大或训练样本极少时,差距可达数个百分点。真实部署场景绝不允许整图
fit。
本文做法:先分层抽样得到训练/测试索引 → 仅用训练像素
fit标准化与 PCA → 用该变换对整图 transform → 训练与预测均在同一(训练集拟合得到的)特征空间,从源头避免泄露。
1. 任务要点
- 严格无泄露预处理:只用训练像素拟合
StandardScaler+PCA;全图在同一变换空间中变换。 - 轻量模型:用 MobileNetV2 的深度可分离卷积(3 段)+ GAP + FC。
- 全图预测显存友好:按坐标批收集 patch → 堆成 batch → 前向推理。
- 评估:
classification_report、混淆矩阵、OA;可视化支持 Windows 阻塞显示。
2. 方法详解
2.1 严格无泄露的 PCA 流程
- 先划分后拟合:对有标签像素做分层抽样得到训练/测试索引;仅训练像素拟合
StandardScaler和PCA。 - 全图共享空间:将整图
(H×W×Bands)用训练集拟合的变换进行标准化与降维,得到(H×W×PCA_DIM)。 - 提取 patch:在 PCA 空间内按坐标提取
(PATCH_SIZE×PATCH_SIZE×PCA_DIM)的 patch 作为输入。
这样做的关键:测试像素从未参与统计,评估更可信。
2.2 轻量化 MobileNetV2(HSI 版)
- Depthwise Separable Conv:逐通道 3×3 深度卷积 + 1×1 点卷积,大幅降参与算力需求。
- 网络骨干:3 段深度可分离卷积 → GAP(自适应全局平均池化)→ 全连接输出。
- 输入通道:这里输入为 PCA 后的通道数(例如 30),以二维 patch 形式输入(
C×H×W)。
2.3 全图预测策略(坐标批)
- 坐标遍历:生成所有像素坐标。
- 反射填充:边界像素也能提取完整 patch。
- 批量收集:按 batch_size 组装 patch → 前向 → 填回
pred_map。 - 显存稳定:避免一次性张量过大导致溢出。
3. 代码逐段 + 解释
下面先按逻辑分段展示与解释;最末提供“一键可跑脚本(整合版)”,复制后仅需修改数据路径即可运行。
3.1 全局与可视化设置
import os, time, numpy as np, scipy.io as sio
import torch, torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
from sklearn.model_selection import train_test_split
import matplotlib
# Windows 下 TkAgg 更稳;Linux/服务器用 Agg(无显示)
if os.name == 'nt':
matplotlib.use('TkAgg')
else:
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import seaborn as sns
matplotlib.rcParams['font.family'] = 'SimHei'
matplotlib.rcParams['axes.unicode_minus'] = False
plt.rcParams['figure.dpi'] = 120
sns.set_theme(context="notebook", style="whitegrid", font="SimHei")
torch.backends.cudnn.benchmark = True
3.2 随机种子
def set_seeds(seed=42):
import random
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
固定随机性,保证复现。
3.3 轻量化网络
class DepthwiseSeparableConv(nn.Module):
def __init__(self, in_ch, out_ch, stride=1):
super().__init__()
self.depthwise = nn.Conv2d(in_ch, in_ch, 3, stride=stride, padding=1, groups=in_ch, bias=False)
self.pointwise = nn.Conv2d(in_ch, out_ch, 1, bias=False)
self.bn = nn.BatchNorm2d(out_ch)
self.act = nn.ReLU6(inplace=True)
def forward(self, x):
x = self.depthwise(x)
x = self.pointwise(x)
x = self.bn(x)
return self.act(x)
class MobileNetV2_HSI(nn.Module):
def __init__(self, in_ch, num_classes, width_mult=1.0):
super().__init__()
c1, c2, c3 = int(32 * width_mult), int(64 * width_mult), int(128 * width_mult)
self.layer1 = DepthwiseSeparableConv(in_ch, c1)
self.layer2 = DepthwiseSeparableConv(c1, c2)
self.layer3 = DepthwiseSeparableConv(c2, c3)
self.gap = nn.AdaptiveAvgPool2d

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



