ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

遥感图像分类实战:kNN、SVM、CNN与LSTM四模型对比

遥感图像分类实战:kNN、SVM、CNN与LSTM四模型对比 简介面向遥感图像识别任务的综合算法项目覆盖kNN、SVM、CNN、LSTM四种经典机器学习与深度学习模型适合计算机相关专业学生开展课设、毕设或算法对比实验。压缩包共33个文件以Python脚本6个py、Jupyter Notebook4个ipynb、Markdown文档和说明文档5个md与1个txt为主另含训练过程图表png/jpg与辅助C代码2个cpp整体体积仅1.17MB。项目按0_kNN、1_SVM、2_CNN、3_LSTM等模块清晰组织代码与文档均已通过测试可直接运行实现图像分类、特征提取与模型效果对比能够直观呈现各算法在遥感场景下的识别精度差异丰富的可视化结果和详细说明也便于快速定位、复现和二次开发。当前已有56人学习下载适合希望系统掌握机器学习建模流程、快速搭建实验环境的学习者。1. 遥感图像分类为什么需要同时跑通 kNN、SVM、CNN 与 LSTM遥感影像的地物判读和普通 ImageNet 分类最大的区别在于样本少、尺度跨度大、类间纹理相似。真到了答辩或者项目验收环节评审几乎都会追问一句“你凭什么选这个模型”。最稳的回答方式不是背概念而是把 kNN、SVM、CNN、LSTM 四条路线全部跑一遍用同一批数据、同一套评价口径给出对比结论。这个基于机器学习的遥感图像识别项目就是用足球场 footballField_13.jpg、桥梁 bridge_17.jpg 这类人工地物影像作为输入按 0_kNN、1_SVM、2_CNN、3_LSTM 四个模块组织代码覆盖从传统特征分类到深度特征提取的完整链路。它不一定把准确率做到 99%但能把每种模型的适用边界、调参敏感点和训练成本摊开给你看。适合做毕业设计、课程设计的学生也适合想快速建立图像分类基线对比的工程师。2. 数据预处理与标签工程从 footballField_13.jpg 到可训练的特征集2.1 目录约定决定后续实验的复用成本解压资源后第一眼看到的是code/data_preprocessing、0_kNN、1_SVM、2_CNN、3_LSTM这种按算法拆分的一级结构这个约定对实验管理非常关键。常见做法是每个模型目录里放独立训练脚本而data_preprocessing只负责把原始 jpg 转成统一的 NumPy 数组避免模型脚本里到处出现硬编码路径。读取图像的第一步是统一尺寸。原始数据里footballField_13.jpg和bridge_17.jpg分辨率不一致直接进入模型会导致特征维度无法对齐。一般会把图像缩放到 64×64 或 128×128再以 RGB 三通道存储。需要注意的是 OpenCV 默认读入的是 BGR 格式用来做特征提取问题不大但送进 PyTorch 或 TensorFlow 之前要转回 RGB否则颜色特征会整体偏移。import cv2 import numpy as np import os def load_images(base_dir, image_size(64, 64)): images [] names [] for root, _, files in os.walk(base_dir): for f in files: if not f.lower().endswith(.jpg): continue path os.path.join(root, f) img cv2.imread(path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, image_size) images.append(img) names.append(f) return np.array(images), names X, fnames load_images(dataset) print(X.shape, fnames[:3])逻辑说明cv2.imread读入的 numpy 数组形状是(H, W, 3)但通道序为 BGRcvtColor转成 RGB 后再resize保证所有样本形状一致。返回的X形状为(样本数, 64, 64, 3)这是后面 CNN 和 LSTM 都承认的标准输入格式。参数说明image_size控制下采样分辨率64×64 在传统特征阶段能够明显降低计算量而在 CNN 阶段也保留足够的局部纹理信息如果遥感影像中目标只占几十个像素建议上调到 128。2.2 传统模型的特征工程分岔点kNN 和 SVM 不能直接吃三维像素矩阵必须降维或提取统计特征。常见选择有三种颜色直方图、方向梯度直方图 HOG、灰度共生矩阵 GLCM。颜色直方图对光照变化敏感度低适合区分绿色的足球场和灰白色的桥梁HOG 对边缘轮廓敏感但对颜色信息完全丢弃GLCM 提取对比度、能量、熵等纹理参数适合区分大片均质区域。三种特征在遥感小样本场景下的表现差异可以简单用表格对比特征类型维度判别重心小样本稳定性提取成本RGB 直方图32×3颜色统计一般极低HOG1764边缘梯度较好中GLCM 纹理24灰度共生关系较好中从项目自带截图knn1.png、svm1.png看SVM 在直方图特征上已经能拿到不错的精度说明在这个场景里颜色分布是强判别信息。常见做法是把三类特征横向拼接再用StandardScaler标准化因为 kNN 的距离计算和 SVM 的核函数都依赖特征尺度。from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split X_flat X.reshape(X.shape[0], -1) X_train, X_test, y_train, y_test train_test_split( X_flat, y, test_size0.3, stratifyy, random_state42 ) scaler StandardScaler() X_train scaler.fit_transform(X_train) X_test scaler.transform(X_test)逻辑说明把 64×64×3 的图像直接拉平为 12288 维向量交给 kNN 和 SVM这是最简单但也能跑通的基线。stratifityy保证训练集和测试集中足球场、桥梁类别比例一致避免某类样本全落到测试集导致指标失真。参数说明test_size0.3适合几十到几百张量级的小数据集random_state固定后多次运行结果可复现这是答辩展示时非常重要的点。2.3 数据增强与标签编码CNN 和 LSTM 对样本数量更敏感原始遥感图如果只有每类几十张支撑不了深层网络。建议在预处理阶段做离线增强水平翻转、垂直翻转、小角度旋转、裁剪缩放。翻转不会改变“足球场还是桥梁”的语义标签成本也低。标签编码上keras 的sparse_categorical_crossentropy可以直接接收整数标签不需要 one-hot 编码而传统 sklearn 分类器也天然支持整数标签。所以预处理阶段只做LabelEncoder的序号映射即可。3. kNN 与 SVM 基线scikit-learn 实现与超参数边界3.1 kNN 的惰性学习陷阱kNN 在遥感图像分类里通常作为“下限基线”因为它没有显式训练过程预测时要计算测试样本与全部训练样本的距离。项目里0_kNN模块放到最前面意图就是先拿到一个朴素准确率比如 0.82后面 SVM、CNN 再和它比较证明“模型复杂度提升带来收益”。但 kNN 的k值非常敏感k1 时决策边界完全跟随训练点有噪声时直接过拟合k 取样本数的平方根附近时较稳定但类间距离太近时精度会显著下降。from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score param_grid {n_neighbors: [3, 5, 7, 9], weights: [uniform, distance]} for k in param_grid[n_neighbors]: for w in param_grid[weights]: knn KNeighborsClassifier(n_neighborsk, weightsw, p2, n_jobs-1) knn.fit(X_train, y_train) y_pred knn.predict(X_test) print(fk{k}, weights{w}, acc{accuracy_score(y_test, y_pred):.4f})逻辑说明p2表示使用欧氏距离weightsdistance会让邻近样本按距离倒数加权投票能缓解 k 偏大时的边界模糊问题。参数说明n_jobs-1使用全部 CPU 核心因为 kNN 的预测阶段是一个计算密集型操作。遥感图像拉平后的特征维度很高欧氏距离会趋于均匀化这就是为什么特征标准化比原始像素值更关键。3.2 SVM 核函数与 C、gamma 的配合SVM 模块是本项目最容易被问到的部分。遥感图像样本量小但维度高这只对线性 SVM 有利而实际里数据往往线性不可分。项目中1_SVM直接上 RBF 核这一步思路是对的。RBF 核需要关注两个参数C控制误分类惩罚gamma控制单个样本的影响半径。gamma过大时每个样本都变成一个小山包边界极不规则过拟合过小则所有样本的影响糊成一片欠拟合。from sklearn.svm import SVC svm SVC(kernelrbf, C10.0, gammascale, class_weightbalanced, probabilityTrue) svm.fit(X_train, y_train) train_acc svm.score(X_train, y_train) test_acc svm.score(X_test, y_test) print(fSVM train acc: {train_acc:.4f}, test acc: {test_acc:.4f})逻辑说明gammascale表示自动取1 / (特征维度 × X.var())这是 sklearn 在不知道数据分布时相对安全的起点避免手动调出一个灾难级数值。class_weightbalanced在遥感地物类别不平衡时非常有用比如桥梁样本只有足球场的一半它会给少数类更高惩罚权重。参数说明probabilityTrue开启 Platt 缩放允许输出类别概率但这会增加训练耗时如果只用predict打分可以关掉在最后一章做置信度筛选时会用到这里。以我自己的经验SVM 在颜色直方图和原始像素拉平特征上表现差异不大但在 GLCM 特征上优势明显因为它对特征尺度更敏感。一个可复现的基准做法是先跑默认参数记录结果再在C ∈ [0.1, 1, 10, 100]、gamma ∈ [scale, 0.01, 0.001]上做网格搜索。3.3 用 classification_report 评估每个类别的精确率与召回率验收项目时准确率不是唯一指标尤其是遥感图像里“桥梁”和“足球场”可能存在易混淆子区域。需要输出精确率、召回率和 F1简单做法是from sklearn.metrics import classification_report print(classification_report(y_test, svm.predict(X_test), target_names[footballField, bridge], digits4))classification_report的每个类别行里precision反映误检多少recall反映漏检多少。如果 footballField 的 precision 是 0.85、recall 是 0.70说明有大量足球场被判成桥梁但桥梁很少被判成足球场这类细节在答辩时能展示你真正排查过问题。4. CNN 遥感识别从图像输入到卷积核的参数图景4.1 网络结构怎么选才不算过度设计遥感小数据集的 CNN 结构有个原则优先小卷积核浅网络不要上来就套 ResNet。一个 64×64 的输入用三层卷积就能覆盖足够感受野。项目里2_CNN模块的模型截图显示训练过程正常收敛说明结构设计是匹配数据量的。常见的做法是卷积核数量按 16 → 32 → 64 翻倍每层后接 MaxPooling 缩小空间尺寸最后用全局平均池化或 Flatten 接全连接分类层。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization def build_cnn(num_classes2): model Sequential([ Conv2D(16, (3, 3), activationrelu, paddingsame, input_shape(64, 64, 3)), MaxPooling2D((2, 2)), Conv2D(32, (3, 3), activationrelu, paddingsame), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activationrelu, paddingsame), MaxPooling2D((2, 2)), Flatten(), Dense(64, activationrelu), Dropout(0.5), Dense(num_classes, activationsoftmax) ]) return model逻辑说明paddingsame保证卷积后特征图尺寸与输入一致保留边缘信息这在遥感地物边界识别里比valid更能稳住边界连续性。MaxPooling2D(pool_size(2,2))将每个 2×2 区域取最大值同时完成空间降维和微小偏移容忍。参数说明第一个 Conv2D 的16表示学习 16 种基础边缘/纹理滤波器最后一个 Dense 的num_classes对应类别数Dropout(0.5)在训练时随机丢弃一半神经元是防止全连接层过拟合的最有效手段。4.2 训练配置与早停策略CNN 训练最怕的不是收敛慢而是资源里本来就没有预留验证集。推荐直接用validation_split0.2从训练集里割 20% 做验证同时开启 EarlyStopping。遥感样本少时训练轮次太多必然过拟合验证集损失会在某个 epoch 后掉头向上EarlyStopping 能在拐点出现后及时回滚权重。from tensorflow.keras.callbacks import EarlyStopping model build_cnn(num_classes2) model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy]) callbacks [EarlyStopping(monitorval_loss, patience8, restore_best_weightsTrue)] history model.fit( X_train, y_train, batch_size16, epochs80, validation_split0.2, callbackscallbacks, verbose1 )逻辑说明restore_best_weightsTrue会在早停时恢复验证损失最小的那组权重而不是停在最后一个 epoch这对答辩时展示模型泛化性能非常关键。batch_size16在几十到几百张图像的数据集里是比较合理的起点过大会导致梯度更新次数不足过小则训练震荡。参数说明patience8表示连续 8 轮验证损失不下降就停止给模型足够时间跨过局部平台。如果训练到第 10 轮验证损失仍不下降应优先考虑加大数据增强而不是加深网络。4.3 从损失曲线判断欠拟合还是过拟合训练完成后建议绘制两条曲线训练 loss 和验证 loss。如果训练 loss 持续下降、验证 loss 在第 12 轮回升说明模型开始记住训练集的噪声。如果两条曲线都高位平滑就是模型容量不够。项目附带的cnn1.png ~ cnn5.png显示了这种过程属于典型的小数据集曲线形态。处理过拟合优先加 Dropout 和数据扩充处理欠拟合则增加卷积核数量或卷积层深度。5. LSTM 序列化建模把遥感像素序列转化为时序特征5.1 遥感图像用 LSTM 的合理性在哪LSTM 本来为序列建模设计直接拿二维图像输入并不自然。但遥感图像有一个隐藏特性地物在空间上有方向连续性和结构重复性。比如足球场的草地纹理沿扫描行方向会呈现近似周期性的像素波动桥梁的直线边缘在逐行扫描时表现为突变的交界点。把图像按行拆成序列喂给 LSTM相当于让网络学习“行与行之间的上下文依赖”这在少样本场景下有时会获得与 CNN 互补的特征表达。5.2 图像到序列的 reshape 细节要喂给 LSTM需要把(样本数, 64, 64, 3)变成(样本数, 时间步, 每步特征维度)。常见做法有两种一种是按行扫描64 行作为 64 个时间步每步输入 64×3192 维的 RGB 拼接向量另一种是把整张图拉平成 4096 个像素点序列每个时间步只输入一个像素。实验下来按行扫描保留了更多空间局部性前 64 个特征描述同一行的颜色信息更利于 LSTM 捕获“下一行与上一行的关系”。在利用 LSTM 建模之前需要做维度转换X_seq_train X_train.reshape(X_train.shape[0], 64, 64 * 3) X_seq_test X_test.reshape(X_test.shape[0], 64, 64 * 3)逻辑说明reshape后得到的三维张量中第二维64是时间步数第三维192是每一时间步的特征向量。LSTM 内部会按时间步顺序逐一处理并在每个时间步之间传递隐状态。参数说明这里要求输入数据的内存布局严格是(batch, time_steps, features)任何一个维度顺序错了LSTM 都会把同一行的不同列当作不同时间步导致模型学到的规律完全颠倒。5.3 LSTM 层配置与 Dropout 位置from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout def build_lstm(num_classes2): model Sequential([ LSTM(64, input_shape(64, 192), return_sequencesFalse), Dropout(0.3), Dense(32, activationrelu), Dropout(0.3), Dense(num_classes, activationsoftmax) ]) return model model build_lstm() model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy]) model.summary()逻辑说明LSTM(64)表示隐单元数为 64return_sequencesFalse丢弃所有时间步的中间输出只保留最后一个时间步的隐状态作为整张图的编码向量再接全连接层分类。如果设成 True输出维度会变成(batch, 64, 64)不适合直接接 Dense。参数说明第一个 Dropout(0.3) 作用在 LSTM 输出上抑制时序特征中的共适应第二个 Dropout 在全连接层之间工程上 0.3 比 0.5 更适合小数据保留更多信息。LSTM 训练时要特别注意梯度不稳定问题建议使用 clipnorm 或 clipvalue。遥感图像序列长度只有 64梯度爆炸概率不如长序列高但加一个约束没有成本from tensorflow.keras.optimizers import Adam opt Adam(learning_rate0.001, clipnorm1.0) model.compile(optimizeropt, losssparse_categorical_crossentropy, metrics[accuracy])clipnorm1.0将梯度向量的 L2 范数限制为 1防止某些离群样本产生巨大梯度把权重冲垮。learning_rate0.001是 Adam 的保守默认值。5.4 LSTM 结果出现什么信号时该放弃如果 LSTM 验证精度明显低于 CNN而且训练 loss 下降极慢问题大概率出在特征表示上纯 RGB 像素序列对 LSTM 来说过于底层它需要大量数据才能抽象出纹理规律。这时可以把输入从原始像素改成 CNN 倒数第二层的特征图再接一层 LSTM构建 CNN-LSTM 串联结构。项目中3_LSTM放在 CNN 后面也隐含了这个思路先让 CNN 提取空间特征再让 LSTM 建模特征间的依赖关系而不是让 LSTM 裸着处理像素。6. 结果对比与模型挑选从训练曲线到阈值设定6.1 四模型横向评审表项目答辩时会把四个模型的实验数据汇总成一张参数与指标对照表最核心的是这四列模型输入特征关键参数测试准确率量级训练耗时kNN像素拉平标准化k5欧氏距离0.78~0.85秒级SVM直方图/HOGRBFC100.85~0.92秒级CNN64×64×3 图像3层卷积Dropout0.90~0.96分钟级LSTM64×192 序列64 隐单元0.82~0.90分钟级这个表不是固定结论但它能直观说明一个趋势CNN 在该数据集上通常拿最高分SVM 以极低的训练成本紧随其后kNN 和 LSTM 分别受维度灾难和序列归纳偏置限制稳定排在后面。展示时把训练耗时和调参难度写进去比只说准确率更有说服力。6.2 用混淆矩阵定位真实错误来源准确率之外最值得画的一张图是混淆矩阵。在二分类下它能直接看出模型把哪一类错判为另一类。建议在所有模型上统一输出对比哪两个类别最容易混淆。import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix def plot_cm(model, X, y_true, title): y_pred model.predict(X) cm confusion_matrix(y_true, y_pred) fig, ax plt.subplots(figsize(4, 4)) im ax.imshow(cm, cmapBlues) for i in range(cm.shape[0]): for j in range(cm.shape[1]): ax.text(j, i, str(cm[i, j]), hacenter, vacenter) ax.set_xticks([0, 1]) ax.set_yticks([0, 1]) ax.set_xticklabels([footballField, bridge]) ax.set_yticklabels([footballField, bridge]) ax.set_title(title) plt.show()逻辑说明confusion_matrix返回的矩阵第 i 行是真实类别第 j 列是预测类别对角线上的数字是正确判定的样本数。如果 bridge 那一行里有几个样本落到 footballField 列说明桥梁图像中混入了和足球场纹理相似的植被区域。参数说明cmapBlues只用于可视化不影响数值annot在 seaborn 里更简洁但这里用 matplotlib 原生的方式避免额外依赖。6.3 利用 softmax 概率设定“拒绝判定”阈值遥感图像里总有训练阶段没见过的地物比如停车场、裸地。强行让模型输出“足球场”或“桥梁”会造成不可控误报。更稳妥的做法是给 softmax 概率加一个最小置信度阈值低于阈值直接标记为“unknown”。这正是提前设置svm probabilityTrue和 CNN 默认 softmax 输出的原因。def predict_with_threshold(model, image, threshold0.7): prob model.predict(image)[0] idx np.argmax(prob) if prob[idx] threshold: return unknown, prob return idx, prob score model.predict(X_test[:1]) print(predict_with_threshold(model, X_test[:1], threshold0.7))逻辑说明np.argmax(prob)取出概率最大的类别下标如果最大概率都不超过threshold说明模型内部对不同类别的证据都不足输出 unknown 比强行分类更合理。参数说明threshold0.7是一个经验值实际使用时可以先跑一遍验证集统计所有正确样本的 softmax 概率分布再选取 5% 分位数作为阈值这样可以避免阈值定得过高导致大量样本被拒判。这个方法同样适用于 kNN 的距离分数或 SVM 的 decision function 输出思路是通用的。最终交付时将predict_with_threshold封装成独立推理脚本并用joblib或model.save()导出最优模型权重这样评审现场可以随机挑一张遥感图验证 known 和 unknown 两档输出整个项目从训练到部署的链路才算真正闭环。本文还有配套的精品资源点击获取
返回列表