资讯详情 遥感图像识别四模型实战:KNN/SVM/CNN/LSTM完整工程
📅 2026/10/10 18:54:49
简介本资源是一套面向计算机专业本科生与初阶AI学习者的遥感图像识别综合实践项目聚焦课程设计、期末大作业及算法对比实战需求完整实现KNN、SVM、CNN与LSTM四种主流模型在遥感图像分类任务中的建模、训练与评估全流程。压缩包共33个文件包含6个核心Python脚本含数据预处理、模型构建与结果可视化、4个Jupyter Notebook支持交互式调试与结果复现、5份Markdown文档含环境配置说明、实验报告框架与方法对比分析、14张过程图如SVM决策边界、CNN特征热力图、LSTM训练曲线等以及少量辅助C代码与遥感样本图整体仅1.74MB轻量易部署。已有284人下载学习提供从数据加载、特征工程、多模型调参到性能对比的完整闭环方案特别适合理解传统机器学习与深度学习在遥感场景下的适用边界与协同思路。1. 遥感图像识别不是调个model.fit()就完事这份98分课程设计把KNN/SVM/CNN/LSTM四类模型全跑通还留了真实数据预处理痕迹和训练日志截图你手头正赶着遥感图像识别的课程设计 deadline导师说“用深度学习”你搜到一堆只贴import tensorflow和model.compile()的空壳代码一跑就报错ValueError: Input 0 is incompatible with layer...连数据长什么样都看不到别硬扛——这份被导师打了98分的 Python 源码包不是玩具 demo而是实打实走完四条技术路径的完整工程从 KNN 的手工特征提取、SVM 的核函数调参、CNN 的卷积层堆叠到 LSTM 对时序遥感序列的建模每个模块都带可复现的train.py、带标注的.jpg样本图bridge_17.jpg,footballField_13.jpg、带中间结果的.png可视化cnn1.png到cnn5.pnglstm.png,svm1.pngsvm4.png甚至保留了temp.cpp这种调试残留文件——说明作者真在本地跑过、改过、卡过。它专为计算机专业学生做课程设计、期末大作业、项目实战练习而生不讲玄学理论只给你能cd进去、python train.py起来、tensorboard --logdirlogs看到曲线的真实路径。如果你需要的是“能交差、能答辩、能讲清楚每一步为什么这么写”的源码而不是“看起来很高级但根本跑不通”的幻灯片素材这份就是你该停下的地方。2. 四种方法不是并列选择而是递进验证从KNN基线到LSTM时序建模每步都暴露真实数据瓶颈2.1 KNN用手工特征距离度量打底验证遥感图像是否真有可分性KNN 在遥感识别里常被当成“对照组”——它不学特征只靠原始像素或浅层统计量做最近邻匹配。这个项目没跳过这步反而在0_kNN/目录下放了完整的knn1.py和knn2.py对应不同特征工程策略。核心逻辑是先对遥感图做灰度化 归一化再提取HOG方向梯度直方图和GLCM灰度共生矩阵两类纹理特征拼成 128 维向量最后用sklearn.neighbors.KNeighborsClassifier(n_neighbors5)分类。关键不是n_neighbors5这个数字而是它强制你面对一个现实问题遥感图像分辨率高、信噪比低直接用原始像素做 KNN 效果极差作者在README.md里明确写了knn1.png是失败案例knn2.png才是加了 GLCM 后的正确结果。所以 KNN 这一环的价值是帮你确认你的数据集有没有足够强的纹理区分度如果 GLCMHOG 都撑不起 KNN那后面 CNN 的卷积核也大概率学不到有效模式。# 0_kNN/feature_extractor.py 关键片段 def extract_hog_glcm(img_path): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, (256, 256)) # 统一分辨率避免尺寸不一致报错 # HOG 特征block_size(16,16), cell_size(8,8) 是遥感图常用配置 features_hog hog(img, orientations9, pixels_per_cell(8, 8), cells_per_block(2, 2), visualizeFalse) # GLCM 特征距离1角度0°只取对比度、相关性、能量、同质性4维 glcm greycomatrix(img, distances[1], angles[0], levels256, symmetricTrue, normedTrue) features_glcm np.array([ greycoprops(glcm, contrast)[0, 0], greycoprops(glcm, correlation)[0, 0], greycoprops(glcm, energy)[0, 0], greycoprops(glcm, homogeneity)[0, 0] ]) return np.concatenate([features_hog, features_glcm]) # 拼成最终128维向量提示hog()函数来自skimage.feature不是 OpenCV 的cv2.HOGDescriptor()greycomatrix需要skimage.feature不是scipy.ndimage。很多同学 pip install 错包导致ImportError这是第一个坑。2.2 SVM用核技巧突破线性不可分但必须亲手调C和gammaSVM 是遥感分类的老将尤其适合小样本课程设计常只有几百张图。项目在1_SVM/下提供了svm1.py线性核、svm2.pyRBF 核默认参数、svm3.py网格搜索最优C/gamma、svm4.py结合 PCA 降维后 SVM。重点看svm3.py——它用GridSearchCV在C[0.1,1,10,100]和gamma[0.001,0.01,0.1,1]上穷举但作者没直接用X_train原始特征而是先做了StandardScaler 标准化因为 SVM 对特征尺度极度敏感再传入GridSearchCV。svm4.png显示 PCA 降到 64 维后准确率反升 1.2%说明遥感图像存在大量冗余频域信息盲目堆维度反而干扰 SVM 决策边界。# 1_SVM/svm3.py 关键片段 from sklearn.svm import SVC from sklearn.model_selection import GridSearchCV from sklearn.preprocessing import StandardScaler # 必须先标准化否则 grid search 会失效 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) # X_train 来自前面 extract_hog_glcm() X_test_scaled scaler.transform(X_test) param_grid {C: [0.1, 1, 10, 100], gamma: [0.001, 0.01, 0.1, 1]} svc SVC(kernelrbf, random_state42) grid_search GridSearchCV(svc, param_grid, cv5, scoringaccuracy, n_jobs-1) grid_search.fit(X_train_scaled, y_train) # 注意这里传的是 scaled 数据 print(Best params:, grid_search.best_params_) # 输出如 {C: 10, gamma: 0.01} best_svm grid_search.best_estimator_ y_pred best_svm.predict(X_test_scaled)注意GridSearchCV的cv5是 5 折交叉验证不是简单划分n_jobs-1表示用所有 CPU 核心但在笔记本上可能因内存不足卡死建议初试时设n_jobs1。2.3 CNN用卷积自动学遥感纹理但必须控制过拟合和显存爆炸2_CNN/是整个包最厚的目录含cnn1.py基础 LeNet-5、cnn2.py加 BatchNorm、cnn3.py加 Dropout、cnn4.py迁移学习 VGG16、cnn5.py自定义 ResNet-like 结构。作者没直接上 VGG 或 ResNet而是从cnn1.py开始逐步加组件每步都附cnn1.pngcnn5.png训练曲线图。关键教训遥感图单张尺寸大作者用256x256但课程设计数据集小data_preprocessing/下只有bridge_17.jpg、footballField_13.jpg等命名暗示约 20 类 × 30 张/类直接训大网络必过拟合。cnn3.py的Dropout(0.5)放在全连接层前cnn4.py的 VGG16 冻结前 10 层只训最后 3 层都是针对小数据的务实选择。cnn5.png显示验证损失在第 12 轮开始震荡作者手动在train.py里加了EarlyStopping(patience3)这是血泪经验——别等 50 轮遥感图训练慢早停省时间。# 2_CNN/cnn3.py 关键片段带 Dropout 的基础 CNN model Sequential([ Conv2D(32, (3,3), activationrelu, input_shape(256,256,1)), # 输入是灰度图channels1 MaxPooling2D((2,2)), Conv2D(64, (3,3), activationrelu), MaxPooling2D((2,2)), Conv2D(64, (3,3), activationrelu), Flatten(), Dropout(0.5), # 关键放在 Flatten 后、Dense 前防全连接层过拟合 Dense(64, activationrelu), Dropout(0.5), # 第二个 Dropout进一步抑制过拟合 Dense(num_classes, activationsoftmax) # num_classes 来自 data_preprocessing/label_map.json ]) model.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy]) # EarlyStopping 配置在 fit() 中非 compile() history model.fit(X_train, y_train, validation_data(X_val, y_val), epochs30, batch_size16, # batch_size16 是平衡显存和梯度稳定性的经验值 callbacks[EarlyStopping(patience3, restore_best_weightsTrue)])提示input_shape(256,256,1)中的1表示灰度通道若你用 RGB 图需改为3且extract_hog_glcm()也要同步改——否则cv2.imread(..., cv2.IMREAD_GRAYSCALE)读出来还是单通道喂给(256,256,3)模型必报错。2.4 LSTM把遥感图当“帧序列”处理解决多时相变化识别这才是项目真正出彩的地方多数课程设计只做单张图分类而它把遥感识别拓展到时序维度。3_LSTM/目录下lstm.py不是拿单张图 reshape 成(1,256,256)喂 LSTM那是玄学而是假设你有一组同一地点不同时间的遥感图如bridge_17_t1.jpg,bridge_17_t2.jpg,bridge_17_t3.jpg按时间顺序组成序列。作者用ImageDataGenerator加载每张图 → 提取 CNN 特征用cnn3.py训好的特征提取层→ 拼成(seq_len, feature_dim)序列 → 输入 LSTM。lstm.png显示测试准确率比单图 CNN 高 3.7%证明时序信息对桥梁损毁、农田轮作等动态场景确有增益。注意temp.cpp文件是作者早期尝试用 C 加速特征提取的残留说明他真卡在性能瓶颈上过。# 3_LSTM/lstm.py 关键片段时序特征流水线 # Step 1: 加载预训练 CNN 特征提取器来自 cnn3.h5 cnn_feature_extractor load_model(2_CNN/best_cnn3.h5) cnn_feature_extractor Model(inputscnn_feature_extractor.input, outputscnn_feature_extractor.layers[-2].output) # 去掉最后 Dense 层取倒数第二层输出 # Step 2: 对每个时序样本如 [t1.jpg, t2.jpg, t3.jpg]提取特征 def extract_seq_features(seq_img_paths): features [] for img_path in seq_img_paths: img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, (256,256)) img img.reshape(1,256,256,1) / 255.0 # 归一化 feat cnn_feature_extractor.predict(img) # 输出 shape(1, 64) 假设 Dense 前是 64 维 features.append(feat[0]) # 取 batch 维度 return np.array(features) # shape(3, 64) # Step 3: 构建 LSTM 模型输入 shape(None, 3, 64) 即 (batch, timesteps, features) model Sequential([ LSTM(64, return_sequencesFalse, dropout0.3, recurrent_dropout0.3), # 双重 dropout 防 LSTM 过拟合 Dense(32, activationrelu), Dropout(0.4), Dense(num_classes, activationsoftmax) ])注意return_sequencesFalse表示只输出最后一个时刻的隐藏状态适合分类若做时序预测如预测下一帧需设True并接TimeDistributed(Dense())。3. 数据预处理不是 copy-pastedata_preprocessing/目录藏着课程设计最易翻车的三道关3.1 文件组织必须严格遵循class_name/image_001.jpg格式课程设计最常翻车的不是模型是数据加载。data_preprocessing/下没有train/val/test子目录而是直接平铺bridge_17.jpg,footballField_13.jpg等文件靠文件名前缀bridge_,footballField_自动解析类别。preprocess.py里关键逻辑是# data_preprocessing/preprocess.py import os import re def build_dataset(data_dir): image_paths [] labels [] class_names [] for fname in os.listdir(data_dir): if not fname.lower().endswith((.jpg, .jpeg, .png)): continue # 正则提取类别名bridge_17.jpg → bridge match re.match(r^([a-zA-Z_])_\d\., fname) # 匹配下划线前的字母下划线 if match: class_name match.group(1) if class_name not in class_names: class_names.append(class_name) label_idx class_names.index(class_name) image_paths.append(os.path.join(data_dir, fname)) labels.append(label_idx) return image_paths, labels, class_names # 调用示例 X_paths, y_labels, classes build_dataset(data_preprocessing/) print(Classes found:, classes) # 输出 [bridge, footballField, river, ...]提示如果你的数据是bridge_001.jpg,bridge_002.jpg正则r^([a-zA-Z_])_\d\.能匹配但如果是bridge001.jpg无下划线此正则会失败需改为r^([a-zA-Z_])\d\.。作者用下划线命名是刻意降低正则难度方便学生调试。3.2 标签映射必须生成label_map.json并被所有模块读取README.md提到label_map.json但它不在根目录而在data_preprocessing/label_map.json。内容长这样{ bridge: 0, footballField: 1, river: 2, forest: 3 }所有模型脚本0_kNN/knn2.py,1_SVM/svm3.py,2_CNN/cnn3.py,3_LSTM/lstm.py开头都有一段with open(data_preprocessing/label_map.json, r) as f: label_map json.load(f) num_classes len(label_map)这意味着你新增一个类别urban_area必须手动加到label_map.json否则cnn3.py会因num_classes不匹配报错ValueError: Shapes (None, 4) and (None, 5) are incompatible。这不是 bug是课程设计要求你理解标签一致性——label_map.json是整个项目的“数据契约”。3.3 图像增强必须用ImageDataGenerator而非 OpenCV 手写data_preprocessing/augment.py没用cv2.flip()或cv2.rotate()而是标准 Keras 流水线from tensorflow.keras.preprocessing.image import ImageDataGenerator datagen ImageDataGenerator( rotation_range20, # 随机旋转 ±20°遥感图地物方向敏感不宜过大 width_shift_range0.1, # 水平平移 10%模拟卫星定位误差 height_shift_range0.1, # 垂直平移 10% shear_range0.1, # 错切 0.1 弧度模拟视角倾斜 zoom_range0.1, # 缩放 ±10%模拟不同高度拍摄 horizontal_flipTrue, # 水平翻转——对桥梁/河流有意义对文字标识无效但遥感图少文字 fill_modenearest # 填充边缘像素避免黑边 ) # 生成器必须指定 class_modecategorical否则 y_train 是整数而非 one-hot train_generator datagen.flow_from_directory( data_preprocessing/, target_size(256, 256), color_modegrayscale, # 关键保持灰度与 CNN input_shape(256,256,1) 一致 batch_size16, class_modecategorical, shuffleTrue )注意color_modegrayscale是硬性要求。若你误设rgb生成器输出(256,256,3)而cnn1.py的input_shape(256,256,1)会报错Input 0 is incompatible。这个错误在cnn1.png曲线图里表现为训练损失恒为nan是典型维度错配。4. 避坑四类模型在遥感课程设计中踩过的五个真实坑附现象、原因、解法4.1 KNN 报ValueError: Found array with dim 3. Expected dim 2现象运行0_kNN/knn2.py时knn.fit(X_train, y_train)报此错X_train.shape显示(100, 256, 256)。原因extract_hog_glcm()返回的是(256,256)图像矩阵没 flatten 成一维向量。KNN 只接受(n_samples, n_features)二维数组。解法在extract_hog_glcm()最后加return features.reshape(1, -1)[0]或return features.flatten()。作者在knn2.py里已修复但knn1.py是未修复版本故意留作教学对比。4.2 SVM 训练 10 分钟没反应CPU 占用 100%现象1_SVM/svm2.py运行后卡住top显示 Python 进程吃满 CPUps aux | grep python查到进程 ID 后kill -9才退出。原因GridSearchCV默认n_jobs-1用所有核心但SVC的 RBF 核计算复杂度是O(n²)200 张图 × 200 张图 4 万次核计算笔记本扛不住。解法临时注释掉GridSearchCV改用SVC(kernelrbf, C1, gamma0.01)手动试参或改n_jobs1耐心等 30 分钟或先用 PCA 降到 32 维再搜参见svm4.py。4.3 CNN 训练 lossnancnn1.png曲线直线下跌到负无穷现象2_CNN/cnn1.pymodel.fit()后history.history[loss]第一轮就是nancnn1.png纵坐标标着-inf。原因ImageDataGenerator的rescale1./255和model.fit()前的手动归一化img/255.0叠加导致输入值变成0~0.0039激活函数如 ReLU在极小值区梯度消失Adam 优化器更新失稳。解法二选一① 删除ImageDataGenerator(rescale1./255)只在preprocess_input()里做/255.0② 保留rescale删掉preprocess_input()里的除法。作者在cnn3.py里选方案①。4.4 LSTM 报ValueError: Input 0 is incompatible with layer lstm: expected shape(None, None, 64), found shape(None, 64)现象3_LSTM/lstm.pymodel.fit(X_seq, y_seq)报此错X_seq.shape是(100, 64)。原因LSTM 层期望输入是三维(batch, timesteps, features)但你喂了二维(batch, features)即把单张图当序列了。解法检查extract_seq_features()输出形状确保是(seq_len, feature_dim)若只有单图用np.expand_dims(X_single, axis0)增加时间维度变成(1, 64)再喂model.predict()。作者在lstm.py注释里写了# Note: X_seq must be 3D: (samples, timesteps, features)。4.5 所有模型 predict 结果全是 class 0classification_report显示 precision1.0 recall0.02现象model.predict()输出argmax全是 0混淆矩阵第一行占满其他行全零。原因label_map.json里类别顺序与flow_from_directory()实际读取顺序不一致。Kerasflow_from_directory按文件夹名排序a_,b_,c_但label_map.json是手动写的{bridge:0,footballField:1}若实际文件夹叫footballField/,bridge/则bridge被分配 label 1但label_map.json说它是 0导致标签错位。解法删除label_map.json运行preprocess.py自动生成或确保data_preprocessing/下文件名前缀按字母序排列bridge_,footballField_,river_使flow_from_directory分配的 label 与label_map.json一致。5. 模型对比不能只看 accuracy用classification_report和confusion_matrix拆解遥感识别的真难点5.1 为什么 accuracy 95% 还可能不及格看classification_report的 per-class 指标课程设计答辩时导师绝不会只问“你准确率多少”而会点开classification_report问“为什么river类的 recall 只有 62%forest类的 precision 为什么崩到 41%” 这份源码在每个train.py结尾都加了from sklearn.metrics import classification_report, confusion_matrix import numpy as np y_pred model.predict(X_test) y_pred_classes np.argmax(y_pred, axis1) print(classification_report(y_test, y_pred_classes, target_namesclass_names))输出类似precision recall f1-score support bridge 0.98 0.96 0.97 50 footballField 0.95 0.93 0.94 48 river 0.82 0.62 0.70 45 forest 0.41 0.91 0.57 42 accuracy 0.83 185 macro avg 0.79 0.86 0.80 185 weighted avg 0.83 0.83 0.82 185看到forest类 precision0.41说明模型把大量其他类尤其是river误判为forest。原因遥感图中森林和河流常伴生纹理相似GLCM 能量值接近KNN 和 SVM 难区分CNN 的卷积核可能学到了水面反光 vs 树冠阴影的细微差异但 LSTM 因时序信息少只有 2-3 帧无法捕捉季节变化故forest类在 LSTM 上 precision 反而更低。这就是为什么四模型对比不能只比 accuracy——它掩盖了类别不平衡和地物混淆的本质。5.2confusion_matrix可视化用热力图定位模型“认错规律”README.md提到svm3.png,cnn4.png等图其实是confusion_matrix热力图。作者用seaborn.heatmap()生成import seaborn as sns import matplotlib.pyplot as plt cm confusion_matrix(y_test, y_pred_classes) plt.figure(figsize(8,6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsclass_names, yticklabelsclass_names) plt.title(Confusion Matrix) plt.ylabel(True Label) plt.xlabel(Predicted Label) plt.savefig(results/cnn4_confusion.png, dpi300, bbox_inchestight) plt.show()关键洞察热力图对角线越亮越好但更要关注非对角线的亮块。例如cnn4.png中river行、forest列有高值23说明模型把 23 张river图判成forest而svm4.png中同一位置只有 8说明 SVM 的 RBF 核在区分水体/植被上比 CNN 更鲁棒——这正是你答辩时能展开的技术细节“CNN 学到了局部纹理但 SVM 用全局核函数抓住了光谱反射率差异”。5.3 四模型性能横向对比表不是谁高谁赢而是谁解决了什么问题模型AccuracyriverRecallforestPrecision训练时间RTX3060适用场景课程设计价值KNN (GLCMHOG)78.2%62.3%41.1%1 min快速验证数据可分性无需 GPU理解特征工程必要性SVM (RBFPCA)83.5%71.8%68.4%8 min小样本、高维特征CPU 可训掌握核技巧与调参逻辑CNN (ResNet-like)91.3%85.6%82.7%42 min大样本、GPU 可用需调参实践深度学习全流程LSTM (CNNLSTM)92.7%89.2%86.3%65 min多时相遥感序列动态变化识别理解时空联合建模注意表中时间基于作者README.md记录的 RTX3060 实测若你用 CPUCNN/LSTM 时间乘以 10 倍若用 M1 Mac需换tensorflow-metal且ImageDataGenerator可能报错建议降级到tensorflow2.12.0。5.4 一个血泪技巧每次改模型前先git stash保存当前权重和日志我带过 7 届课程设计学生最大的后悔药不是模型选错是python cnn3.py跑到第 25 轮时 CtrlC 中断再python cnn3.py却发现best_weights.h5被覆盖cnn3.png曲线图没了。这份源码在2_CNN/train.py里埋了硬编码# 每次训练前自动备份上次最佳权重 if os.path.exists(best_cnn3.h5): timestamp datetime.now().strftime(%Y%m%d_%H%M%S) shutil.copy(best_cnn3.h5, fbackup/best_cnn3_{timestamp}.h5) shutil.copy(logs/cnn3_history.npy, fbackup/cnn3_history_{timestamp}.npy)但更可靠的做法是在项目根目录初始化 git每次重大修改如从cnn3.py改到cnn4.py前执行git add . git commit -m cnn3: baseline with dropout git stash # 临时存档当前所有未提交变更 # 然后放心改 cnn4.py跑通后再 git stash popgit stash不仅保权重还保confusion_matrix图、classification_report文本、甚至temp.cpp这种调试残骸——它们是你答辩时展示“我真调过、真卡过、真解决过”的证据链。从那以后我每次指导学生都强制他们git init后第一件事是git stash哪怕只改一行注释。希望帮到你。本文还有配套的精品资源点击获取