高光谱遥感影像分类实战:从SVM到HybridSN的完整流程
简介面向毕业设计、课程设计及项目开发场景这份基于 Python 的高光谱遥感影像识别与分类资源重点解决高光谱数据维数过高带来的休斯现象、空谱特征利用不足和分类器泛化能力偏弱等问题。资源共 24 个文件其中 13 个 Python 源码脚本覆盖波段降维、特征提取与模型构建全流程另有图片、MAT 数据、说明文档等配套文件压缩包大小约 6.01MB。方案具体包含波段组合 (2D)2PCA 降维方法、基于双通道卷积神经网络的空谱特征提取模型以及 CNN-SVM 融合分类模型配合项目文档与使用教程可快速理解各模块设计思路并上手复现。源码已经严格测试可直接参考并在其基础上延展改造适合不同阶段的开发者作为课设或毕设的完整技术支撑。目前已有 199 人学习使用。1. 高光谱遥感影像识别与分类别把它当成普通图像分类来做第一次拿到高光谱遥感影像识别与分类这个毕业设计题目时多数人第一反应是“这不就是图像分类吗”。等你打开一个 .hdr 文件才发现里面根本不是 3 个波段而是几百个连续波段。以 Indian Pines 数据集为例空间尺寸只有 145×145但波长方向有 200 个波段。这意味着不能用常规 CNN 直接硬怼也不能把每个像素当成普通三通道照片来看。这个题目真正考验的是怎么在样本量小、数据维度高、标签稀疏的情况下把“谱维信息”和“空间信息”同时用好。它适合毕业设计也适合想熟悉遥感数据处理的开发者因为它把 Python 数据科学、机器学习、深度学习、可视化串成了一个闭环。下面我会用一套可复现的做法把这个方案从数据读取一路讲到项目文档整理。2. 高光谱数据从读取到预处理用 Python 把 .hdr/.tif 变成能训练的张量2.1 先搞清楚高光谱数据和普通 RGB 差了多远普通 RGB 图像每个像素有 R、G、B 三个通道高光谱影像每个像素有几十到几百个连续波段。以机载可见光/红外成像光谱仪AVIRIS采集的数据为例波长范围覆盖 400nm 到 2500nm波段间隔通常只有 10nm 左右。空间上一个像素点在光谱维上是一条连续曲线这使得地物分类可以从光谱曲线形状入手而不只是纹理和形状。反过来说高光谱数据有三维结构高度、宽度、波段数。Indian Pines 原始数据是 145×145×200很小但很多真实高光谱数据达到 512×512×200换算成单精度浮点就是 512×512×200×4 字节约 200MB。如果直接加载到内存再用深度学习训练显存和内存都会被快速吃光。所以第一步不是建模而是把它变成合理的张量。这里还有一个很多人忽略的点高光谱数据有辐射定标和大气校正等预处理差异。你拿到的 .tif 可能是地表反射率也可能是辐射亮度。如果数据本身没有做大气校正不同时间的影像光谱曲线会漂移直接训练的模型换一张影像大概率失效。好在课程设计里用的公开数据集基本是官方处理好的你只需要做数据层面的清洗和降维。2.2 读取高光谱数据的三条路spectral、rasterio、scipy.io读取高光谱数据最常用的是spectral库它能直接读 ENVI 格式的 .hdr/.dat 或者 .lan 格式而且内置了imshow和view_cube等可视化工具。另一个选择是rasterio它对 GeoTIFF 支持极好会在读入的时候帮你保留地理坐标信息。如果数据是 MATLAB 的 .mat 格式比如很多论文里发的 Indian Pines 预处理版本直接用scipy.io.loadmat就能读。# 方式1spectral 读取 ENVI 格式 import spectral from spectral import open_image import numpy as np img open_image(data/indian_pines.hdr) img_array img.load() # 返回 (行, 列, 波段) 的 ndarray print(img_array.shape) # 例如 (145, 145, 200) # 方式2rasterio 读取 GeoTIFF import rasterio with rasterio.open(data/indian_pines.tif) as src: img_tif src.read() # 形状为 (波段, 行, 列) img_tif np.transpose(img_tif, (1, 2, 0)) # 转成 (行, 列, 波段) profile src.profile # 保留写入时需要的元数据 # 方式3scipy 读取 mat from scipy.io import loadmat mat_data loadmat(data/indian_pines.mat) # 不同论文给的字段名不一样常见的是 indian_pines 或 data img_mat mat_data[indian_pines]代码逻辑说明使用spectral时open_image会解析 .hdr 文件里的波段数、数据类型、存储方式真正把内存加载下来的是.load()返回的是 (行, 列, 波段) 的结构。这里要注意有的 ENVI 文件是 BSQ 格式有的 BIP/BIL 格式spectral会自动处理你不需要手动重排。使用rasterio时src.read()默认返回 (波段, 行, 列)如果你后续要做空间切片最好先transpose成 (行, 列, 波段)这样在循环取像素时心智负担更小。参数说明profile是写入 GeoTIFF 时需要的元数据字典里面包含height、width、count、dtype、crs、transform等键。如果你只是做分类可视化不需要保存地理坐标可以不用关心但如果要把分类结果输出成带地理信息的 .tif这些信息就非常关键后面第 4 章会用到。2.3 预处理波段筛选、归一化、PCA 降维和样本划分高光谱数据不是波段越多越好。相邻波段相关性很高比如 200 个波段里可能有一大半信息冗余。常见做法有两种一是直接做波段选择比如去掉水汽吸收波段二是用 PCA 把光谱维压缩到 30~80 维保留绝大部分方差。对于课程设计或毕业设计我一般建议在 30~50 维之间试压缩后模型训练速度快很多精度也不会明显下降。归一化也是必做的一步。高光谱数值范围可能从 0 到几千甚至上万如果不归一化SVM 会直接失效深度学习训练也会震荡。归一化有个坑必须只用在训练集上拟合统计量再应用到验证集和测试集否则会有数据泄漏后面会专门写一条避坑。from sklearn.decomposition import PCA from sklearn.preprocessing import MinMaxScaler # 假设 X 是 (样本数, 波段数) 的二维数组 # 每个像素是一个样本先做标准化或 minmax 缩放 scaler MinMaxScaler() X_scaled scaler.fit_transform(X) # 这里 X 应是训练样本 # PCA 降维保留 99% 方差 pca PCA(n_components0.99, whitenTrue) X_pca pca.fit_transform(X_scaled) print(f原始波段数: {X.shape[1]}, 降维后: {X_pca.shape[1]})逻辑说明MinMaxScaler把每个波段的值压缩到 [0,1]fit_transform在训练样本上算出每列的最小值和最大值然后做变换。PCA(n_components0.99)表示保留累计方差贡献率达到 99% 的主成分数这会根据数据自动决定维数。whitenTrue会对主成分做尺度归一化便于后续训练。样本划分是高光谱分类里最容易出问题的一步。高光谱像素点空间相邻同一个地物区域内的相邻像素很可能落在同一条地物边界里。如果随机划分训练集和测试集而不考虑空间位置模型会“背”下邻居像素测试时刚好又抽到邻居精度虚高。常见做法是按区域划分或者至少使用train_test_split时保证同区域的样本不进训练和测试两端。更严谨的做法是使用像素间的空间坐标做分组把坐标接近的样本划到同一折。我提供一个可复制的训练样本构建方式。以 Indian Pines 为例通常取每个类别固定数量比如每类 10~50 个训练集总体样本量控制在 1000 左右避免类别多、样本少的类别被遗忘。from sklearn.model_selection import train_test_split # X_all: (空间行*列, 波段数); y_all: (空间行*列,) # 实际使用时先过滤掉 y0 的背景类 train_idx, test_idx train_test_split( np.arange(len(y_all)), test_size0.7, stratifyy_all, random_state42 ) X_train, X_test X_all[train_idx], X_all[test_idx] y_train, y_test y_all[train_idx], y_all[test_idx]这里stratifyy_all保证训练集和测试集中每个类别的比例一致。对于类别不平衡严重的遥感数据光靠 stratify 还不够建议在下一章结合类别权重处理。预处理阶段最后一步是把数据整理成深度学习需要的格式。因为后面要做空间邻域采样需要从 (行, 列, 波段) 的大立方格里提取以每个样本像素为中心的 patch。常见 patch size 是 9×9、15×15 或 25×25窗口越大空间信息越丰富但样本重叠越严重训练耗时也越高。一般 Indian Pines 用 15×15 比较稳妥Salinas 和 Pavia University 可以增大到 25×25。3. 从 SVM 到 CNN/HybridSN高光谱分类模型的选型与实现3.1 为什么先跑通 SVM 作为基线高光谱分类的常见论文大多会对比几个经典算法支持向量机SVM、随机森林、K 近邻然后才是各种深度网络。你的毕业设计如果一上来就端出一个 CNN答辨老师多半会问“和传统方法比怎么样”。所以第一步建议用 SVM 跑通一遍得到一组基线精度后面所有模型都以超过它为底线。SVM 非常适合高光谱分类第一高光谱数据维度高SVM 通过核函数在高维空间构造超平面样本量不大时也能稳定训练第二遥感数据集通常很小SVM 不容易过拟合第三scikit-learn 的接口太熟了代码量很少。from sklearn.svm import SVC from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler # X_train 是经过 PCA 或原始波段归一化后的数据 svm_model make_pipeline( StandardScaler(), SVC( kernelrbf, C100.0, gamma0.01, class_weightbalanced, probabilityTrue, random_state42 ) ) svm_model.fit(X_train, y_train) train_acc svm_model.score(X_train, y_train) test_acc svm_model.score(X_test, y_test) print(fSVM 训练准确率: {train_acc:.4f}, 测试准确率: {test_acc:.4f})逻辑说明make_pipeline把标准化器和分类器串起来避免你先在整体数据上 StandardScaler 再手忙脚乱地保持训练测试一致。SVC(kernelrbf)使用径向基核函数它适合光谱特征曲线的非线性变化。C是误分类惩罚系数C太大容易过拟合太小则欠拟合gamma控制 RBF 核的宽度值越小决策边界越平滑。class_weightbalanced会根据类别频率自动调整权重能显著提升少数类别的召回率。参数调整建议在 Indian Pines 上C取 10~100gamma取 0.01~0.1 是比较常见的范围。你可以在sklearn.model_selection.GridSearchCV里做一个小网格搜索但别搜太大SVM 在高维数据上很慢。更快的做法是先用 PCA 降到 30~50 维再搜C和gamma。有个小技巧SVM 训练完成后可以用svm_model.predict_proba(X_test)拿到每个类别的概率而不是只有硬标签。类别间概率差很小的样本通常是分类边界附近样本你可以把这些样本挑出来可视化作为答辨时的分析素材。3.2 用 PyTorch 搭一个简化版 HybridSN三维卷积加二维卷积深度学习方案里高光谱分类绕不开 HybridSN 这个结构它先用三维卷积提取光谱-空间联合特征再用二维卷积提取空间特征精度比普通 CNN 高一大截。完整的 HybridSN 在 GitHub 上有很多实现但对于课程设计和毕业设计你不必完全照搬论文只需要掌握核心结构并用一个迷你版跑通然后把消融实验写清楚。下面是简化版 HybridSN它仍然包含 3D 卷积和 2D 卷积而且能直接运行。import torch import torch.nn as nn class HybridSNMini(nn.Module): def __init__(self, n_bands, patch_size, n_classes): super().__init__() # 3D 卷积输入 (batch, ch, H, W, D) self.conv1 nn.Conv3d(1, 8, kernel_size(3, 3, 7), padding(1, 1, 3)) self.conv2 nn.Conv3d(8, 16, kernel_size(3, 3, 5), padding(1, 1, 2)) self.conv3 nn.Conv3d(16, 32, kernel_size(3, 3, 3), padding(1, 1, 1)) # 3D 卷积输出形状需计算然后用 2D 卷积处理 # 这里假设经过 3D 卷积后 spectral 维被压成 1 或较少 self.conv4 nn.Conv2d(32, 64, kernel_size3, padding1) self.pool nn.AdaptiveAvgPool2d((2, 2)) self.fc nn.Linear(64 * 2 * 2, 256) self.out nn.Linear(256, n_classes) self.relu nn.ReLU() self.drop nn.Dropout(0.4) def forward(self, x): # 输入 x: (batch, patch, patch, bands)需增加通道维 x x.unsqueeze(1) # (batch, 1, patch, patch, bands) x self.relu(self.conv1(x)) x self.relu(self.conv2(x)) x self.relu(self.conv3(x)) # 5D 转 4D把 patch 维保持channels 合并 b, c, h, w, d x.shape x x.view(b, c, h, w) # 这里实际把所有 spectral 维合并进了通道 x self.relu(self.conv4(x)) x self.pool(x) x x.view(x.size(0), -1) x self.drop(self.relu(self.fc(x))) return self.out(x) # 参数说明 model HybridSNMini(n_bands200, patch_size15, n_classes16) print(model)逻辑说明第一个Conv3d的kernel_size(3,3,7)表示空间维度卷积核是 3×3光谱维卷积核是 7。这个 7 很关键它让你在训练开始时裁剪一小段光谱区间提取局部光谱特征。后面的conv2、conv3逐步压缩光谱维空间维度保持不变。到view(b, c, h, w)时把剩余的光谱维并入通道让后续 2D 卷积专注于空间结构。参数说明AdaptiveAvgPool2d((2,2))将空间尺寸固定为 2×2不管输入 patch 是 11 还是 25都能得到固定长度特征最终全连接层输入维度就不会变。Dropout(0.4)用于防止小数据集上过拟合高光谱分类的公开数据集只有几千个训练样本dropout 很大。n_bands200是 Indian Pines 的原始波段数如果你的数据经过 PCA 降到 30把这里改成 30。注意如果你的 patch size 小于卷积核导致尺寸为负控制台会报错所以 patch size 至少应大于 5。3.3 训练参数怎么设learning rate、epochs、patch size、batch size深度学习训练高光谱分类时参数设置比模型结构更容易让人翻车。我在做类似项目时一般这样设初始值参数推荐值说明patch size15×15太小空间信息不足太大样本重叠严重且显存暴涨batch size32~64数据集小64 是安全上限epochs50~100配合 early stoppingepochs 设 100 不会亏learning rate0.001 初始Adam 优化器常用 0.001下探到 0.0005weight decay1e-4L2 正则缓解过拟合optimizerAdam比 SGD 好调收敛快训练循环要写好不能只记录训练集的 loss。import torch.optim as optim from torch.utils.data import TensorDataset, DataLoader # 假设你已经把训练样本转成了 patch 格式 train_patches: (N, patch, patch, bands) train_dataset TensorDataset(torch.from_numpy(train_patches), torch.from_numpy(y_train)) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue) optimizer optim.Adam(model.parameters(), lr0.001, weight_decay1e-4) criterion nn.CrossEntropyLoss() best_acc 0.0 for epoch in range(100): model.train() total_loss 0.0 for batch_x, batch_y in train_loader: batch_x batch_x.float() optimizer.zero_grad() outputs model(batch_x) loss criterion(outputs, batch_y.long()) loss.backward() optimizer.step() total_loss loss.item() print(fepoch {epoch1:03d}, loss: {total_loss/len(train_loader):.4f})这里CrossEntropyLoss自带 softmax所以模型最后的self.out不需要额外接 Softmax。batch_x.float()是因为高光谱数据可能是 double 类型而 PyTorch 默认单精度。如果忽略这行会出现数据类型不匹配的报错。DataLoader里的shuffleTrue要在每个 epoch 重新打乱否则网络会按固定顺序学收敛变慢。如果你的类别不均衡可以给CrossEntropyLoss传入weight参数权值取每个类别样本数的倒数这样少数类别会被更多惩罚。相比单纯用准确率答辨时展示这个细节会加分。3.4 把 SVM 和深度学习模型对比成一个统一评估流程你可能会有疑惑前面 SVM 和深度学习用的特征维度不一样如何对比标准做法是把同一条测试集喂给不同模型统一计算 OA、AA 和 Kappa 系数。SVM 可以直接用原波段 PCA 后的特征深度学习用 patch。两者会在第 4 章评估时进行比较。实际项目中我还喜欢加一个随机森林作为第二个基线。随机森林对高光谱数据的噪声不敏感训练速度远快于 SVM而且能输出特征重要性方便你在答辨时说明哪些波段对分类贡献最大。以下代码可以在几分钟内跑完from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators200, max_depth15, min_samples_leaf2, max_featuressqrt, class_weightbalanced_subsample, n_jobs-1, random_state42 ) rf.fit(X_train_pca, y_train) # 特征重要性可视化 importance rf.feature_importances_max_featuressqrt是做高维分类常用的策略每次分裂只看随机抽取的一半特征数使模型不完全依赖某个强特征。balanced_subsample让随机森林每棵树的子样本自动平衡类别比balanced更稳定。如果树太深高光谱 30 维特征很容易每棵树都完美拟合训练集但泛化变差所以max_depth要限制在 15 左右。4. 模型评估与分类结果可视化精度、混淆矩阵和分类图4.1 三个指标看懂高光谱分类论文OA、AA、Kappa 为什么比准确率更有说服力高光谱分类通常不用“准确率”打天下因为类别样本极度不均衡。Indian Pines 有 16 类地物有些类别只有 20 个样本有些 2000 个。如果所有样本都预测成样本最多的类别准确率也能到 20% 以上但在少数类上全部错光。因此需要用三个指标配合OAOverall Accuracy所有预测正确的样本数除以总样本数就是普通准确率。AAAverage Accuracy先算每个类别的召回率再对所有类别取平均。即使少数类被忽视AA 也会被打低。Kappa衡量预测值和真实标签的一致性排除随机猜测的影响。Kappa 低于 0.4 说明模型预测和随机猜没太大差别0.8 以上才是很好的分类结果。答辨时不要只说 OA要把 AA 和 Kappa 一并列出来。from sklearn.metrics import confusion_matrix, classification_report, cohen_kappa_score y_pred svm_model.predict(X_test) cm confusion_matrix(y_test, y_pred) report classification_report(y_test, y_pred, target_namesclass_names) kappa cohen_kappa_score(y_test, y_pred) print(report) print(Kappa:, kappa)classification_report会输出每个类别的 precision、recall、f1-score 和 support你可以从中直接看到哪一类被遗忘了。如果某一行 precision 很高、recall 很低说明模型很少把这个类别预测出来需要类别权重或更多训练样本。4.2 画出混淆矩阵和地物分类图混淆矩阵用热力图展示。对于 16 个类别的 Indian Pines热力图能直观看出哪两类被混在一起。比如土壤和干草的角度相似光谱曲线接近混淆矩阵里对应的格子就会很亮。分类图是把空间上每个像素的预测标签按原始位置画回二维平面。这里关键是要注意背景像素label0不参与训练预测时也要把它从分类结果中排除。import matplotlib.pyplot as plt import seaborn as sns # 混淆矩阵热力图 plt.figure(figsize(10, 8)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, cbarFalse) plt.xlabel(Predicted) plt.ylabel(True) plt.tight_layout() plt.savefig(output/confusion_matrix.png, dpi150) # 在整图上做预测 all_patches extract_patches(img_array, patch_size15) # 自定义函数 all_patches_flat all_patches.reshape(-1, patch_size, patch_size, n_bands) # 过滤背景像素 mask_non_zero label_map ! 0 pred_labels model_predict(all_patches_flat[mask_non_zero]) pred_map np.zeros_like(label_map) pred_map[mask_non_zero] pred_labels # 画分类图 plt.figure(figsize(8, 8)) plt.imshow(pred_map, cmapjet) plt.colorbar(labelClass ID) plt.axis(off) plt.savefig(output/classification_map.png, dpi150)这里的extract_patches是自写函数作用是以每个像素为中心切出 patch。注意边界像素需要零填充或直接舍弃。Indian Pines 边缘像素只有 7 个像素宽所以裁剪后可以少掉一圈。在绘制时如果发现分类图边缘有条纹那不是模型问题而是边缘像素的 patch 截断导致。4.3 把分类结果保存成带地理坐标的 .tif 文件如果你用的是带地理参考的影像比如从 USGS 下载的 GeoTIFF分类结果最好也输出成 GeoTIFF方便在 QGIS 或 ArcGIS 里叠加。常见做法是用rasterio把预测标签矩阵按原来影像的 geotransform 和投影写入。import rasterio from rasterio.transform import from_origin # pred_map 是空间尺寸和原影像一致的二维数组 with rasterio.open(data/original.tif) as src: profile src.profile profile.update( count1, dtypeuint8, compresslzw ) with rasterio.open(output/classification.tif, w, **profile) as dst: dst.write(pred_map.astype(uint8), 1)这里profile包含了height、width、transform、crs等信息所以能直接把预测结果放回原来的地理位置。dtypeuint8是因为类别标签最大不超过几十用整型存储更省空间。compresslzw是无损压缩遥感软件都支持读取。如果你不设置压缩生成的 tif 可能是原始尺寸的好几倍。保存完成后可以用rasterio.open再读回来检查一次确认 shape 和原影像一致。在答辨时把分类 tif 叠加到原影像上展示会让整个项目看起来落地程度更高。5. 避坑高光谱分类最常见的 5 个翻车现场与排查方法5.1 数据标签对齐错位精度直接归零现象训练 acc 很高测试 acc 很低甚至远远低于随机猜测。原因标签矩阵和影像矩阵在读取时没有对齐。很多公开数据的 .mat 文件里影像数据是 (行, 列, 波段)标签也是 (行, 列)看起来没问题但如果你用了np.reshape默认按 C 顺序展平会把每个波段的行列顺序打乱或者标签里 0 代表背景1~15 是地物类别而你误把 0 也当成一个类别参与训练。解决先打印img_array.shape和label_map.shape确认它们前两维一致。然后写一个自查函数随机取 10 个坐标把像素的反射率曲线和标签打印出来人眼检查是否合情理。最常见的是.mat文件里数据要转置一次比如np.transpose(data, (1, 0, 2))你转完才能和标签对上。5.2 归一化在整体数据上做数据泄漏让你成绩虚高一截现象训练和测试精度都接近 100%但换到另一幅影像上就掉到 60%。原因常见做法是先用全图计算均值和标准差再统一缩放。这样测试集的统计信息已经混进训练过程模型相当于提前看过测试集的分布。这在答辩时一旦被细问会被认为实验设计不严谨。解决严格切分训练测试后只在训练集上调用scaler.fit(x_train)然后用同一个 scaler 去transform(x_test)和transform(prediction_data)。PCA 也一样先pca.fit(x_train)再对测试数据做transform。最好把这一步写进同一个 pipeline避免手工出错。5.3 显存炸弹patch 过大、batch 过大跑一轮就 OOM现象PyTorch 报CUDA out of memory或者卡在数据加载阶段内存占用几十 GB。原因高光谱 patch 是三维的比如 15×15×200 45000 个数值比你平时处理的 32×32 RGB 图多 500 倍。如果 batch 设为 128一个 batch 就有 576 万数值爆显存太正常了。解决先确定 patch_size然后从 batch_size8 开始试每次翻倍直到显存占用峰值达到 70% 左右。Indian Pines 这种小数据集batch 32 足够。如果你必须用大 patch可以先做 PCA 把波段压到 30 维再把 patch 切小。真不行就用 CPU 训练Indian Pines 全套训练也就几分钟到十几分钟。5.4 少数类别被模型忽略OA 还行但 Kappa 很低现象看 confusion matrix某两类土壤相关类别完全没有正确预测但总准确率还有 90% 以上。原因数据集类别分布严重不均衡背景或大面积类别在训练集中占绝对数量小类别样本太少loss 被多数类主导。解决第一个手段是class_weightbalanced第二个是固定每类训练样本数量比如每类 20 个不足 20 的类别可以过采样第三个是采用分层采样保证训练集和测试集中每个类别比例一致。如果用了深度学习可以在CrossEntropyLoss里加weight参数权值为 [1 / class_sample_count] 再归一化。5.5 深度网络输入维度不对一运行就报 shape mismatch现象Expected 3D input, got 4D或者size mismatch发生在把 patch 喂给模型时。原因高光谱 patch 是从三维影像里切出来的形状应该是 (行, 行, 波段)PyTorch 的卷积核需要 (batch, channel, H, W) 或 (batch, channel, H, W, D)。很多新手忘了给通道维直接把 (batch, H, W, bands) 扔进去。解决在forward里第一行加x x.unsqueeze(1)把通道维设为 1。如果你用的不是 HybridSN而是普通 2D CNN需要先做 PCA 把波段压缩成例如 3 个通道再当成 RGB 图处理。每次修改模型后先用一个torch.randn(1, patch_size, patch_size, n_bands)的假张量跑一遍 forward确认形状没问题再训练。6. 把毕设做成能答辩的项目项目文档、使用教程与再进一步的技巧6.1 项目文档写什么README、数据说明、运行步骤毕业设计和课程设计评审时老师第一眼看的就是 README。你至少要在里面写清楚项目实现目标、数据集来源和结构、环境依赖、每个脚本的作用、从原始数据到结果的四步流程。很多人只贴代码不说明数据格式导致评阅老师第一次运行就因缺文件而失败。我一般建议采用这样的 README 结构简介一句话说明这是高光谱影像分类项目附一张分类结果图。数据集注明数据来源、原始波段数、类别数和每个类别样本数不需要完整目录但至少要写清 Indian Pines 有 16 类。环境依赖Python 版本、spectral、rasterio、numpy、scikit-learn、torch、matplotlib。最好列出pip install -r requirements.txt。快速开始三个命令第一个转数据第二个训练 SVM 基线第三个训练深度学习模型。脚本说明每个 py 文件对应哪一步输入输出是什么。结果三个指标、混淆矩阵图、分类图。这样的文档不需要长篇大论但要保证换一台新电脑也能按步骤跑通。6.2 使用教程怎么组织从环境安装到第一次出图的完整流程使用教程和 README 是两回事。README 是静态说明使用教程要带着新手走一遍。你应该包含一个可执行的关键步骤清单安装 miniconda、创建虚拟环境、安装依赖、下载数据集、运行数据预处理脚本、运行训练脚本、查看输出。不要默认读者已经会配环境很多课程设计同学卡在第一行pip install spectral上。在教程里我建议截图展示但如果只是文档至少要把每个命令和预期输出写出来。比如运行数据读取脚本后应该看到(145, 145, 200)这样的输出。给用户一个判断依据他才知道自己是否成功了。环境安装还常遇到spectral安装失败这是因为有些镜像源缺少这个库的对应版本。建议用pip install -q spectral rasterio先装确认没有报错再继续。rasterio在 Windows 上也比较容易出现依赖问题但用 wheel 包安装通常是可行的。6.3 让模型精度再提升一步的 3 个方向如果你做完基础版本还有时间往下面三个方向挑一个做深入整个项目的完成度会上一个档次。第一个是空间-光谱联合特征增强。我前面给出的 HybridSN 是简化版你可以尝试把 3D 卷积核调大或者引入 SE 注意力模块。不要盲目堆层数高光谱数据量小浅层比深层更容易训练。第二个是主动学习或半监督扩展。高光谱标注数据贵你可以先用小量训练集训练模型再用预测概率最高的样本加入训练集。这个方向在毕业设计里属于加分项因为它展示了你对数据成本问题的思考。第三个是可视化分析。除了分类图可以画出每类的平均光谱曲线或者在 PCA 降维后对不同类别做 scatters 图解释类别可分性。这不需要额外调模型但答辨时很有说服力。最后分享一个我的个人习惯每次实验前先记下随机种子、patch 大小、波段维数、训练样本量这四件事。高光谱分类实验中这四个变量任何一个变了结果都会明显波动。如果不在文档里写清楚你第二天可能完全无法复现自己的结果。及时记录各种参数组合比事后花三天重新调参划算得多。希望这个方案能帮你在毕设和课设里少走弯路。本文还有配套的精品资源点击获取