新闻详情

新闻详情

首页 / 资讯中心 / 详情

CNN遥感影像分类实战:Landsat地物分类模型加载与调优指南

发布时间:2026/10/1 23:31:04来源:尧图网络
CNN遥感影像分类实战:Landsat地物分类模型加载与调优指南
简介这是一套基于CNN深度学习的遥感Landsat影像地物分类实用资源面向遥感科研人员、测绘工程师及深度学习入门者帮助快速完成水体、植被、建筑区等地表覆盖类型的自动分类解决传统方法依赖人工特征、分类精度有限的问题。压缩包共10个文件包含3个Python源代码分别用于影像切片、模型训练与新数据预测、1个训练好的HDF5格式h5模型、2幅TIF样例影像及对应的xml、tfw辅助文件整体大小约14.49MB结构紧凑清晰。已有182人学习下载适合直接对照源码理解CNN在遥感影像处理中的完整流程。资源最大亮点是无需从零训练可直接加载h5模型对Landsat影像进行地物分类同时提供可运行的样例数据和完整代码便于二次开发、算法调优及教学演示为环境监测、城市规划、农业估产等应用提供高效的分类工具。1. 拿到这份“CNN遥感分类源码模型”你第一件事不是跑代码做过遥感影像分类的人都有体会landsat数据好下但把自己从“会跑通一个demo”推到“能出一张可信的地物分类图”中间隔着数据预处理、波段选择、标签对齐、模型迁移好几道坎。这份“Python基于CNN深度学习的遥感landsat影像地物分类源代码训练好的模型”真正值钱的地方是两个一是源代码本身把“从影像到分类图”的完整pipeline串起来了你能看到数据是怎么进模型的而不是只在Jupyter里跑一个孤零零的卷积网络二是它附带训练好的模型权重意味着你不用从零开始训一个动辄几小时、还容易过拟合的网络拿过来做推理、做迁移、做微调都行。但这东西也有明显的“黑匣子”效应——很多下载了类似资源的人第一步就卡在“文件解压了下一行命令是什么”上。原因是这类资源往往不做环境说明或者训练时用的数据分布和你要分的区域差异极大。这篇笔记我按照“数据准备→模型加载→推理成图→复训调参→避坑→进阶验证”的顺序写目标是让你拿到任何一份同类源码包都能在两小时内跑出自己的第一张分类图并且知道每个环节的坑在哪。2. landsat影像先处理到位波段选择、裁剪与标签对齐2.1 为什么不能把landsat原始文件直接塞给CNN如果你打开过USGS下载的Landsat 8 Level-1数据会看到一堆以LC08_xxx_20200101_20200101_01_T1_B1.TIF到B11.TIF命名的单波段文件。CNN要的是“多波段堆叠后的一个多维数组”不是十几个零散的tif。直接塞进去会出两类问题波段数量不匹配Landsat 8有11个波段但很多分类任务只用可见光到短波红外的6到7个波段多出的热红外波段对地物分类贡献小还会增加计算量。尺寸不匹配landsat原始影像一般是7000x7000到9000x9000像素级别这尺寸直接送进CNN显存不够是小事模型也根本没见过这么大尺寸的输入——它训练时用的通常是patch小块比如64x64、128x128。常见的做法是先把需要的波段用rasterio读进来用numpy.dstack堆成一个多通道数组然后裁剪成固定大小的patch每个patch对应一个标签。这个“对应”不是自动发生的需要你用一张已经做好的标签图shapefile或者栅格去对齐。2.2 波段选择默认用多光谱组合别一上来就全上遥感地物分类里水体、植被、裸土、建筑这几类地物在可见光和近红外波段上的可分性最好。我一般会用Landsat 8的2、3、4、5波段蓝、绿、红、近红外加上6、7波段短波红外1和2组成一个6通道输入。如果你用的模型是预训练好的它训练时的输入波段数可能是固定的——这份源码里大概率是用了一个固定波段组合比如常见的4波段R、G、B、NIR或6波段组合。判断方法很简单看代码里数据加载部分对tif文件列表的定义或者看模型第一层卷积的in_channels参数。in_channels4就输入4个波段in_channels6就输入6个波段。不看这个直接拿你自己拼的8波段数据去喂模型报错都是轻的更隐蔽的是通道顺序不对导致分类结果完全混乱。import rasterio import numpy as np def load_landsat_patch(tif_dir, bands[2,3,4,5,6,7], patch_size128): 读取landsat影像指定波段并裁剪为patch tif_dir: 存放波段tif的目录 bands: landsat 8波段编号注意是从1开始而我们最终通道数等于len(bands) band_arrays [] for b in bands: with rasterio.open(f{tif_dir}/LC08_xxx_B{b}.TIF) as src: # 这里先读全图实际大影像应配合窗口读取 arr src.read(1).astype(np.float32) band_arrays.append(arr) # 堆叠成 (H, W, C) stacked np.stack(band_arrays, axis-1) # 裁剪或填充到 patch_size 的整数倍 h, w, c stacked.shape new_h (h // patch_size) * patch_size new_w (w // patch_size) * patch_size stacked stacked[:new_h, :new_w, :] return stacked这段代码里有两个参数需要特别说明bands的选择直接决定通道数如果模型是4通道输入就改成[2,3,4,5]patch_size决定了后面送入CNN的样本尺寸一般取64到256之间太大尺寸意味着模型要看到更大的空间上下文但也在考验显存。裁剪时我故意取整到patch_size的整数倍是因为CNN分类器通常要求输入尺寸固定后面批量推理时不需要再处理“缺一块”的问题。2.3 标签对齐分类图和标签图必须“严丝合缝”如果有监督训练你还需要一幅标签栅格每个像素一个类别编码比如0水体1植被2建筑。这里最容易翻车的是地理坐标对齐问题——landsat影像和你的标签图即使显示上看起来重叠投影坐标系可能差一个UTM分带或者分辨率一个30米一个10米。实践里的稳妥办法是用rasterio.warp.reproject把标签图重采样到和影像完全相同的网格行列数、仿射变换参数一致然后逐像素配对。别信肉眼对齐一定要写代码检查src.transform和src.shape是否一致。from rasterio.warp import reproject, Resampling # 强制标签图与影像图对齐 with rasterio.open(f{tif_dir}/label.tif) as lab_src: label_arr lab_src.read(1) transform lab_src.transform profile lab_src.profile # 若与影像shape不一致重采样 with rasterio.open(f{tif_dir}/LC08_xxx_B2.TIF) as img_src: img_shape img_src.shape img_transform img_src.transform if label_arr.shape ! img_shape: reproject( sourcerasterio.band(lab_src, 1), destinationlabel_arr, src_transformtransform, src_crslab_src.crs, dst_transformimg_transform, dst_crsimg_src.crs, resamplingResampling.nearest )重采样方法我用的是nearest因为分类标签是离散值用双线性插值会插出“不存在的类别编号”比如0.5这种。如果你是把自己的标注矢量转栅格那更简单——直接在影像的transform和crs下用rasterize画出来一步到位不存在重采样误差。3. 加载训练好的CNN模型推理一张landsat影像的最小可跑通流程3.1 模型文件的几种形态看清是权重还是整模型“训练好的模型”在Python的深度学习生态里有三种常见保存方式拿到手第一件事就是分清是哪一种否则load_state_dict会直接飘红完整模型torch.save(model, model.pth)加载时torch.load(model.pth)直接得到模型对象可以立即推理但跨设备时可能出现兼容问题。仅权重torch.save(model.state_dict(), model_weights.pth)加载时必须先实例化一个结构完全一样的模型再load_state_dict。权重文件中不包含模型结构信息。checkpoint字典格式包含{model_state_dict:..., optimizer_state_dict:..., epoch:...}常见于断点续训场景。源码包里如果是“训练好的模型”大概率是第二种或第三种。有些作者还会打包成.h5TensorFlow或.ptPyTorch你需要先扫一眼代码文件里import torch还是import tensorflow然后把下面的推理流程对应着换API。下面以PyTorch为例。import torch import numpy as np # 假设你的源码包里有一个模型定义文件 model.py其中定义了 Net 类 from model import Net device torch.device(cuda if torch.cuda.is_available() else cpu) # 实例化模型——注意通道数必须和训练时一致 model Net(in_channels6, num_classes5) model.load_state_dict(torch.load(best_model.pth, map_locationdevice)) model.to(device) model.eval()这段代码的关键点是model.eval()。如果不切换到eval模式模型里的Dropout层和BatchNorm层会按训练行为走推理结果会有随机性分类图会出现“同一个地物这次预测是植被、下次是农田”的诡异现象。3.2 滑动窗口推理把patch拼回整幅分类图模型训练时是patch进patch出推理整景landsat时也得这么干。最常见的做法是滑动窗口步长等于patch_size最后把每个patch的预测结果贴回一个大数组的对应位置。步长如果小于patch_size就会有重叠区重叠区可以用来做多数投票或平均概率但推理时间成倍增加。对于一个没有重叠、纯拼接的推理代码如下def predict_full_image(model, image, patch_size128, batch_size16): image: (H, W, C) 已堆叠的landsat多波段数组 model.eval() h, w, c image.shape # 输出分类图 pred_map np.zeros((h, w), dtypenp.uint8) for i in range(0, h, patch_size): for j in range(0, w, patch_size): patch image[i:ipatch_size, j:jpatch_size] if patch.shape[0] ! patch_size or patch.shape[1] ! patch_size: continue # 边缘不完整直接跳过或者做填充 # 转成模型输入格式: (1, C, H, W) patch_tensor torch.from_numpy(patch.transpose(2,0,1)).unsqueeze(0).float() with torch.no_grad(): output model(patch_tensor) pred torch.argmax(output, dim1).squeeze(0).numpy() pred_map[i:ipatch_size, j:jpatch_size] pred return pred_map这段推理代码里pred_map是uint8类型可以直接被rasterio写为GeoTIFF保留地物类别编码。边缘不完整的patch我选择了跳过代价是最终分类图四边各少半个patch宽度的数据——对于大面积landsat影像完全可接受如果你在建图时要求完整覆盖可以用reflect模式填充后推理再把填充区域裁掉。推理速度上单patch的model(patch_tensor)循环在CPU上会慢到让你怀疑人生——一个7000x7000的landsat影像切成128的patch大约是54x542916个patch每个推理哪怕只要0.05秒也要145秒且这还没算PyTorch的Python循环开销。所以有显存就用GPU显存不够就加batch_size把多个patch拼成一个tensor批量推理。上面的示例代码为了清晰没用batch实际跑大影像时建议把2916个patch重组成(batch, C, H, W)的tensor一次前向。4. 从零复训还是微调看懂训练代码后再决定动哪个参数4.1 训练脚本里的四个关键参数batch_size、学习率、epoch、损失函数权重源码包里一般会有train.py或train.ipynb里面写了训练循环。你没必要全部看懂但四个参数必须找到并理解它们的作用否则改了等于没改还会让模型训练时直接发散。# 这些代码是通用的训练循环框架具体数值来自经验未必与你的源码包一致 batch_size 32 # 每个batch的样本数影响梯度估计的稳定性 learning_rate 0.001 # Adam优化器的初始学习率 num_epochs 50 # 遍历完整训练集的次数 class_weights torch.tensor([0.5, 1.0, 1.5, 2.0, 2.5]) # 各类别的损失权重batch_size越大每个step的梯度越平滑但显存占用线性增长而且大batch在小数据集上更容易过拟合遥感patch分类任务里32到64是常见区间。learning_rate是最容易出问题的——预训练好的模型权重已经收敛到某个局部最优点附近再用0.01的大学习率去继续训练loss会像脱缰的野马直接飙到nan所以微调阶段学习率一般降到0.0001到0.001之间。class_weights这个参数很多人忽略但landsat地物分类里水体、裸土这类类别往往只占影像的百分之几而植被占百分之五十——不加权重模型全预测成植被准确率也能到80%以上一点学习的动力都没有。4.2 三种训练策略直接训、全部微调、冻结微调拿到这份源码和预训练权重后你的需求决定了训练策略如果是做和作者类似的区域、类似的地物类别直接用预训练权重做推理就够不需要训练。但如果你的目标区域地物风格差异大比如作者训练集是山区林地你要分城市区域那有两种路径路径一冻结微调。把模型前几层卷积的requires_grad设为False只训练后面的全连接层。这样训练时间短、不容易过拟合前提是前几层提取的通用特征边缘、纹理、角点在你的新数据上也适用。路径二全部微调。所有参数都参与训练效果上限更高但需要的标注数据量也更大——通常每个类别至少要有几千个patch否则必过拟合。# 冻结前几层只微调最后两层 for name, param in model.named_parameters(): if fc not in name and layer4 not in name: param.requires_grad False # 只把需要更新的参数传给优化器 optimizer torch.optim.Adam( filter(lambda p: p.requires_grad, model.parameters()), lr0.0005 )这段代码里用到named_parameters()的好处是你可以按层名字精确控制。不同源码包模型结构不同可能是conv1、conv2这样的命名也可能是残差块加全连接层。你先打印一遍模型结构再决定冻结哪些层。判断是否冻结生效的方法是训练时看loss是否在前几个epoch里明显下降——如果loss纹丝不动且显存占用很低说明你冻结了所有参数优化器里根本没参数可更新。4.3 数据增强遥感分类最容易忽略的“后悔药”训练代码里如果没有数据增强建议你自己加上。遥感影像和自然图像不一样它的旋转不变性有方向性——一片农田旋转90度还是农田但一栋房子旋转90度后阴影方向变了类别标签不变这正好可以增强模型的泛化能力。常见的遥感增强包括随机水平/垂直翻转、随机旋转90度倍数、随机亮度对比度扰动。from torchvision import transforms train_transform transforms.Compose([ transforms.RandomHorizontalFlip(p0.5), transforms.RandomApply([transforms.ColorJitter(brightness0.2, contrast0.2)], p0.3), transforms.RandomRotation(degrees[0, 90, 180, 270]) # 只选90度的倍数避免边界插值 ])特别提醒旋转增强最好不要用任意角度比如36度因为随机旋转会引入插值产生的“假纹理”模型会学到插值伪影而不是真正的地物特征。用90度的倍数配合翻转既能扩充样本多样性又不引入伪影。这是我在实践中吃过亏后总结出来的经验——有一版模型加了任意角度旋转增强训练准确率从88%涨到92%但验证集准确率反而降了两个点后来发现就是插值伪影导致的过拟合。5. 避坑指南landsat影像分类最容易翻车的5个时刻5.1 现象加载模型时报“size mismatch for encoder.conv1”原因模型输入通道数是4RGBNIR但你保存的权重是从6通道模型训练出来的。这类报错在load_state_dict时最常见满脸写着“你对不上我的形状”。解决打开模型定义文件数一下第一层卷积的in_channels值然后调整数据加载的波段数量重新对齐。不要试图用strictFalse强行加载——这会随机初始化不匹配的层推理结果直接变成噪声图。5.2 现象loss一直在0.7左右不下降训练了10个epoch一个点都没动原因这通常是类别不平衡导致的。假设你的训练集里90%的patch都是植被模型一开始预测全部为植被loss为0.7左右这已经是最优解了——继续训练梯度极小因为全预测植被的损失和现在的损失几乎一样。解决给loss加上class_weight让稀有类别水体、裸土的错分代价更高。同时检查一下你的训练标签里是不是真的每个类别都有足够样本某个类别只有几十个patchCNN根本学不到它的特征。5.3 现象推理出来的分类图有明显的“棋盘格”效应原因滑动窗口推理时patch边缘的预测不稳定。CNN虽然输入是128x128的patch但patch边缘的卷积感受野不完整导致边缘像素预测置信度低拼起来就出现方块纹理。解决推理时用重叠滑动窗口步长设为patch_size的一半比如64重叠区的每个像素会得到多个预测结果取众数作为最终类别。代价是推理时间变为之前的4倍左右但分类图会平滑很多。5.4 现象同一块区域两次推理结果不一致有的像素一会儿是水体一会儿是植被原因忘了model.eval()。训练模式下BatchNorm层会利用当前batch的统计量去归一化但单个patch推理时batch只有1个样本统计量波动极大导致输出概率不稳定。解决推理前必须调用model.eval()推理代码块里用with torch.no_grad():包裹前向传播。这样BatchNorm会使用训练时保存的全局统计量Dropout层也直接失效输出就稳定了。5.5 现象分类结果中建筑边缘有“晕圈”和真实边界偏移一两个像素原因landsat影像30米分辨率本身就有混合像元问题一个像素里既有屋顶又有树加上训练标签如果是人工标注的边界本身就不精确。CNN学到的是“最可能类别”在混合像元上输出概率分布比较平均argmax后的边界就是模糊的。解决如果你的分类任务允许“不确定”类可以设置一个置信度阈值——最大概率低于0.6的像素标记为255未分类后期再人工处理。如果必须要硬分类可以尝试在损失函数里加上边界加权的交叉熵让模型对边界区域投入更高的学习权重代价是训练代码要改得复杂一些。提示这些坑不是只有这份源码包里才有任何“预训练模型新区域推理”的遥感分类任务都可能踩到。排查顺序建议是先看输入通道数再看模型是否eval模式最后才去调训练策略。6. 用你自己的验证样本评估模型一份能在30分钟内跑完的指标计算流程6.1 从分类图里随机采样验证点计算混淆矩阵拿到推理出来的分类图后别急着沾沾自喜先用验证样本来一次定量评估。如果你手上没有独立的验证集可以在原始影像上用人工目视解译的方式随机抽300到500个点标注真实类别。from sklearn.metrics import confusion_matrix, classification_report import numpy as np # pred_flat: 分类图的摊平数组剔除了255未分类 # true_flat: 验证标签的摊平数组 sample_idx np.random.choice(len(pred_flat), size500, replaceFalse) pred_sample pred_flat[sample_idx] true_sample true_flat[sample_idx] cm confusion_matrix(true_sample, pred_sample) print(cm) # 计算每类精确率和召回率 report classification_report( true_sample, pred_sample, target_names[水体, 植被, 农田, 建筑, 裸土], digits3 ) print(report)混淆矩阵要重点看两个方向某一类被“漏分”真实是水体但预测成植被和“错分”预测是水体但真实是植被。在遥感分类里水体漏分到阴影里是高频问题——山体阴影和深水在近红外波段的表现极其相似如果分类结果把大片山体阴影分成了水体不要急着骂模型这是landsat多光谱数据的物理极限除非加入SAR数据或地形数据否则很难完全消除。6.2 用Kappa系数判断模型到底可不可用分类准确率OA在类别严重不平衡时是有迷惑性的比如一个区域90%是植被你全预测成植被准确率90%看着不错实际上一点用都没有。Kappa系数把随机一致性的影响剔除掉了一般低于0.4表示一致性极差0.4到0.6中等0.6到0.8高度一致0.8以上非常好。def kappa_score(cm): total np.sum(cm) p_obs np.trace(cm) / total # 对角线之和占比 p_exp 0 for i in range(cm.shape[0]): row_sum np.sum(cm[i, :]) col_sum np.sum(cm[:, i]) p_exp (row_sum * col_sum) / total p_exp p_exp / total kappa (p_obs - p_exp) / (1 - p_exp) return kappa print(fKappa {kappa_score(cm):.4f})如果Kappa低于0.5说明这个预训练模型在你这片区域上基本不可用需要做微调或重新收集训练数据。如果Kappa在0.7以上恭喜你这个模型可以用但你还是得看看各类别的分类精度是不是均匀——我非常不建议只给一个总分因为某一类很高而另一类几乎全错的情况太常见了。6.3 快速检验分类图是否“像真的”的三板斧没有验证样本时可以用三个经验法则做快速检查第一统计每类面积占比和该区域的已知先验信息对比比如山区林地占比应该在50%到80%如果预测结果林地占95%往往有问题第二看分类图的纹理真实地物分布是有空间自相关的农田连片、河湖连续如果分类图出现大量椒盐噪声——单个像素的类别随机跳变说明模型没有学到空间上下文或者推理patch太小第三挖几块你熟悉的小区域做放大检查跟原始影像的假彩色合成比如用5、4、3波段做RGB显示对比建筑区、水系、农田边界是不是大体重合。这三板斧里最实用的还是第三招也是我自己的血泪教训有一版kapppa到了0.75我觉得没问题后来把分类图放大到建筑区发现所有南向屋顶都被分成了裸土——因为训练数据里的裸土和建筑南向面亮度太像了而模型根本没有上下文信息去区分它们。这个坑如果不做区域放大检查光看指标是完全发现不了的。希望这套“加载→推理→评估→微调”的流程能帮到你也希望大家从这份源码里得到的不仅是一张分类图而是一套能迁移到自家研究或项目里的思路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

EasyExcel导出异常 Can not close IO 根因与关流排查 2026/10/2 0:13:58

EasyExcel导出异常 Can not close IO 根因与关流排查

凌晨两点被一个导出接口的告警叫醒,日志里只有一行Can not close IO,堆栈往上翻三层全是 EasyExcel 的类名,看起来像是框架自己出了问题。如果你也踩过使用 EasyExcel 导出 Excel 抛异常 Can not close IO这个坑,大概率已经搜过一…

阅读更多 →
cpp-httplib 客户端超时配置完全指南:连接、读取与写入超时(C12) 2026/10/2 0:13:43

cpp-httplib 客户端超时配置完全指南:连接、读取与写入超时(C12)

后端网络 【免费下载链接】cpp-httplib A C header-only HTTP/HTTPS server and client library 项目地址: https://gitcode.com/GitHub_Trending/cp/cpp-httplib 点击查看 免费下载 导读 本指南围绕 cpp-httplib 客户端的三类超时(连接超时、读取超时…

阅读更多 →
devops-exercises 实战:用 Bash 函数与正则校验编写两数求和脚本 2026/10/2 0:13:09

devops-exercises 实战:用 Bash 函数与正则校验编写两数求和脚本

文档教程DevOps运维 【免费下载链接】devops-exercises Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions 项目地址&…

阅读更多 →
Jupyter Lab密码登录与远程访问安全配置指南 2026/10/2 0:12:55

Jupyter Lab密码登录与远程访问安全配置指南

1. 项目概述:为什么非得让 Jupyter Lab 支持密码登录和远程访问?Jupyter Lab 不是玩具,它是数据科学、机器学习、教学实验和工程验证的真实工作台。但默认安装后,它只在本地http://localhost:8888启动,连本机其他用户都…

阅读更多 →
CentOS 8 安装 GCC 全攻略:在线/离线/源码编译与避坑指南 2026/10/2 0:12:48

CentOS 8 安装 GCC 全攻略:在线/离线/源码编译与避坑指南

CentOS 8 安装 gcc,这话题看着简单,实际操作起来坑不少。尤其 CentOS 8 官方仓库停止维护之后,默认源都迁移到了 vault 地址,你要是直接跑一句yum install gcc -y,十有八九会撞上Failed to download metadata for repo…

阅读更多 →
双渠道闭环供应链跨渠道退货定价:Stackelberg与Nash均衡求解 2026/10/2 0:12:48

双渠道闭环供应链跨渠道退货定价:Stackelberg与Nash均衡求解

简介:一份面向供应链管理研究人员、高校物流相关专业师生及双渠道销售企业管理者的完整PDF资源,聚焦考虑跨渠道退货的双渠道闭环供应链决策优化。内容系统整合Stackelberg博弈与Nash均衡模型,对比集中式、制造商主导、零售商主导及Nash均衡结…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉