ARTICLE DETAIL

资讯详情

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

1100张卫星建筑图像分类实战:迁移学习与避坑指南

1100张卫星建筑图像分类实战:迁移学习与避坑指南 简介这份数据面向卫星遥感图像的分类应用整理出建筑与地物共十一类已标注样本覆盖机场、大桥、教堂、森林、湖泊、体育场、公园等常见场景可用于图像分类网络训练、遥感场景识别教学或算法效果验证。所有图片经过统一预处理并按七比三比例划分为训练集与验证集同类图片集中在同一目录分类网络可直接读取压缩包内附Python脚本和JSON标注文件用来检查类别映射、统计数据分布或运行自带可视化脚本快速预览样本。包体共有文件一千一百六十八个以一千一百六十六张JPG图片为主另含一个Python脚本与一个JSON说明文件整体大小约四十点四兆轻量便携。目前已有四十九人浏览学习适合遥感AI入门、课程设计和分类模型快速验证。获取后可直接进入训练或评估流程也可参考JSON中的类别清单借助脚本完成分布查看与效果展示省去自行整理目录和划分数据集的重复工作。1. 卫星建筑分类的“小数据集”1100张能做什么不能做什么手头是一份只有1100张、覆盖11类建筑的已标注图像分类数据集。第一反应往往是这么点量喂图像分类模型是不是太寒酸但实际做下来会发现卫星俯视角下的建筑识别和日常ImageNet风格的图像分类完全是两回事——目标小、视角固定、类间高度相似数据量少反而逼你把预处理、迁移学习和验证策略做到位。这份数据集适合两类人一是想快速验证卫星建筑识别可行性的从业者二是刚接触图像分类、想用一个小而完整的已标注数据集跑通全流程的新手。下面按我的实操路径展开先讲数据结构与类别边界再给迁移学习的最小可运行代码接着调参数、排坑最后用混淆矩阵做最终验收。2. 认识这1100张图11个类别从哪来、怎么标分类边界在哪2.1 11个类别怎么定义高分辨率卫星视角下的视觉差异一份卫星建筑分类数据集的核心不是“多少张”而是“类别怎么划”。按建筑功能划分是最常见的做法典型的11个类别大致沿着住宅、商业、工业三大块展开住宅小区、独栋别墅、工业厂房、商业综合体、学校、医院、体育场馆、仓储物流、办公写字楼、在建工地、临时板房。这个划分的好处是每个类都有可描述的物理特征和相对清晰的标注边界。但它同时埋了一个雷部分类别在俯视角下外观高度接近比如独栋别墅和低密度住宅都是“小体量屋顶绿地围合”医院和学校都有浅色楼群加活动场地如果采集时没有定义清楚区分规则模型学到的就是噪声而不是语义。拿到数据后我做的第一件事不是写训练脚本而是把每一类的样本各抽10张拼成一张网格图肉眼过一遍。这一步通常会在半小时内暴露两个关键问题一是哪些类在卫星视角下很难用视觉区分二是每类的样本数量是否均衡。这两点直接决定后面的模型选择和数据增强策略。跳过这一步直接训模型后面十有八九返工——分类边界的模糊会在验证集上表现为“怎么调都提不上去的某几类”而且极难定位。2.2 标注格式和数据清洗读一遍文件结构再动手已标注数据集的落地形式通常只有两种目录结构当标签或者CSV/JSON标注文件。yolov8训练自己的数据集时绝大多数人用目录结构——train/类别名/xxx.jpg——而基于PyTorch的分类工程里torchvision.datasets.ImageFolder可以直接消费这种结构省去手写解析器。但“省事”的前提是文件结构干净。我一般先跑一小段脚本把目录和数量盘清楚再决定要不要清洗。import os from collections import Counter data_root satellite_building_dataset train_dir os.path.join(data_root, train) class_counts Counter() for class_name in sorted(os.listdir(train_dir)): class_path os.path.join(train_dir, class_name) if os.path.isdir(class_path): files [f for f in os.listdir(class_path) if f.lower().endswith((.jpg, .jpeg, .png))] class_counts[class_name] len(files) total sum(class_counts.values()) print(f样本总数: {total}, 类别数: {len(class_counts)}) for cls, cnt in class_counts.most_common(): print(f{cls}: {cnt} 张, 占比 {cnt / total:.1%})这段脚本在本质上做三件事确认总量是否为描述中的约1100张统计每个类别的绝对数量和占比顺手排除掉不是jpg/jpeg/png的系统文件。这里有一个容易忽略的细节用endswith过滤扩展名时大写后缀.PNG会被漏掉写代码先统一小写再匹配。如果你在Windows上解压过数据集还要注意Thumbs.db这类隐藏文件它会混在某类目录里被ImageFolder当成正常图片读取轻则报解码错误重则让数据加载变慢但指标正常——属于典型的黑匣子陷阱。排查完目录接下来要划分数据。很多数据集只给了train和val两段不够的话就从train里再切一小块出来。常用的划分比例是8:1:1但1100张样本下val和test各110张的波动非常大单次epoch的acc上下能差5个百分点。这里我推荐直接用sklearn的train_test_split按类别分层切保证每个类在val和test里都有样本再用stratify参数锁住分布避免随机切分把某个稀有类全部带走。from sklearn.model_selection import train_test_split # 已用ImageFolder读出的样本路径和标签 filepaths train_ds.samples # [(路径, 类别索引), ...] paths [p for p, _ in filepaths] labels [l for _, l in filepaths] train_paths, val_paths, train_labels, val_labels train_test_split( paths, labels, test_size0.2, stratifylabels, random_state42, )stratifylabels是关键参数它要求切分前后各类别比例保持一致random_state42保证可复现。别小看这个参数——它直接决定你的迭代是“可比较的实验”还是“每次重开一局”。如果你在同一个脚本里反复执行train_test_split而不固定seed每次划分都不同模型指标的自然波动就会盖过调参带来的真实收益。2.3 类别均衡性检查先看一眼分布再谈训练结构干净只是第一步类别均衡才是决定训练策略的分水岭。把2.2节的class_counts画成直方图你会立刻看到两种典型形态一种接近均匀分布简单loss就能学另一种则是“头部类别占40%尾部类别只有3%”的长尾形态这个形态直接用默认CrossEntropy训练会出现第5.3节说的“永远猜同一类”的假象。对卫星建筑数据类别不均衡几乎不可避免——住宅小区和工业厂房这类类别天然比体育场馆和临时板房多。处理办法按优先级排第一优先是分层采样加WeightedRandomSampler第二优先是过采样尾部类别第三才是修改loss权重。我不建议在1100张的体量下做欠采样因为本来数据就少砍掉头部类别等于白白丢弃特征。到这里数据侧的工作才算完整。边界定了、划分定了、均衡策略定了后面调参才有的放矢。接下来进入模型环节。3. 用现成图像分类模型跑通流程ResNet迁移学习的最小脚本3.1 从目录结构到DataLoader拿到已标注数据集的第一个目标不是刷准确率而是跑通一条完整的图像分类流水线读数据、训练、评估。我见过太多人一上来就换最新图像分类模型结果被环境配置卡了两天。1100张数据量下最稳的起点是ResNet50加ImageNet预训练权重——它足够深、公开实现多、训练资源要求低。import torch from torch.utils.data import DataLoader from torchvision import transforms, datasets data_root satellite_building_dataset train_dir os.path.join(data_root, train) val_dir os.path.join(data_root, val) # 训练集变换含轻量随机增强 train_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees15), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) # 验证集只用确定性变换 val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) train_ds datasets.ImageFolder(train_dir, transformtrain_tf) val_ds datasets.ImageFolder(val_dir, transformval_tf) train_loader DataLoader(train_ds, batch_size16, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4, pin_memoryTrue) print(f类别数: {len(train_ds.classes)}) print(f训练样本数: {len(train_ds)}, 验证样本数: {len(val_ds)})逻辑说明ImageFolder要求目录结构是“根目录/类别名/图片文件”这正好匹配已标注数据集的常见组织方式。Resize到224×224是ResNet系列的标准输入尺寸换成其他模型时要跟着换——EfficientNet系列可能是240或260ViT通常是384。RandomHorizontalFlip和RandomRotation都是轻量增强不改变卫星俯视角的语义但注意不要加RandomVerticalFlip除非你的数据集里确实存在“上下颠倒”的卫星图否则它会引入现实中不存在的分布。参数说明batch_size16是大多数消费级显卡的稳妥默认值。如果你的显卡显存只有4GB优先降batch到8不要降分辨率——因为卫星图像里的建筑可能只有几十个像素降分辨率等于直接扔掉特征。num_workers在Windows上容易出现DataLoader worker进程异常退出这是多进程启动方式的兼容性问题可以先设0跑通再逐步调高。pin_memoryTrue在GPU训练时能减少等待纯CPU机器上开了也没坏处。3.2 加载预训练权重并改造分类头图像分类模型在ImageNet上预训练后前面的卷积层已经学会了边缘、纹理、形状这类通用特征迁移到卫星建筑识别时只需替换最后的分类头并重新训练很少的参数。1100张数据量不建议从零随机初始化训练——那几乎必然过拟合这是小样本图像分类的铁律。做法如下。import torch.nn as nn from torchvision import models def build_model(num_classes11): # 加载ImageNet预训练权重 weights models.ResNet50_Weights.IMAGENET1K_V1 model models.resnet50(weightsweights) # 替换最后一层全连接原输出1000类改成11类 in_features model.fc.in_features model.fc nn.Linear(in_features, num_classes) # 冻结特征提取层只训练新加的全连接层 for name, param in model.named_parameters(): if not name.startswith(fc.): param.requires_grad False return model model build_model(num_classes11)这里的关键参数有两个。第一个是weightstorchvision从0.13开始推荐用枚举类型而不是字符串ResNet50_Weights.IMAGENET1K_V1对应的是原始ImageNet预训练权重V2是同一模型用更强蒸馏方法训练出来的版本精度通常高0.5到1个百分点可以直接替换。第二个是冻结策略我把除fc之外的所有参数都设成requires_gradFalse此时反向传播只更新最后那层一个epoch在CPU上也就十几秒。如果你想追求更高精度常见操作是解冻最后几个残差块再小学习率微调但在1100张数据上解冻越早过拟合来得越快。如果你倾向用更新的图像分类模型比如transformer图像分类方向的ViT或Swin也可以走同样的迁移学习路径——torchvision的vit_b_16和swin_t都支持weights参数。但我的经验是在小数据集上ViT收敛速度明显慢于CNN需要更长的warmup和更强的正则对新手并不友好。先把ResNet50跑通拿到基线再决定要不要换模型——这是性价比最高的顺序。3.3 最小训练循环先跑通再谈收敛这里放一个最小训练循环让它输出每个epoch的loss和val acc。这个循环刻意不做早停和warmup目的是先把整条链路跑通。你在这个阶段看到的指标不好没关系关键是确认数据流、模型结构、前向反向整个环节没有错。import torch import torch.nn as nn device torch.device(cuda if torch.cuda.is_available() else cpu) model build_model(num_classes11).to(device) criterion nn.CrossEntropyLoss() optimizer optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-2) for epoch in range(30): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() logits model(images) loss criterion(logits, labels) loss.backward() optimizer.step() running_loss loss.item() # 验证 model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) logits model(images) preds logits.argmax(dim1) correct (preds labels).sum().item() total labels.size(0) print(fEpoch {epoch1:02d}, Loss {running_loss/len(train_loader):.4f}, fVal Acc {correct/total:.4f})三个细节最容易在这里翻车。第一个model.train()和model.eval()必须成对出现漏掉eval会让BatchNorm统计量错乱验证指标忽高忽低。第二个optimizer.zero_grad()必须在每个batch计算loss之前调用漏掉的话梯度会在多个batch间累积loss可能连续几个epoch都不下降看起来像是学习率设太低。第三个前几轮loss在降但val acc纹丝不动这是分类头从随机初始化向预训练特征对齐的正常过程不要因为前5轮没见好就改学习率。把这一轮跑完拿到一个稳定可复现的基线后面的调参才有对照物。4. 1100张小样本下的训练参数学习率、增强与验证策略怎么调4.1 学习率与批大小小数据集最怕的两种崩溃小样本图像分类模型最常见的翻车方式有两种学习率太高导致loss震荡后爆炸学习率太低导致loss缓慢下降后“看起来收敛”其实根本没学到特征。1100张图下这两个问题都会被放大因为每个batch的样本方差大梯度方向不稳定。我的默认配置是AdamW加1e-4学习率、weight_decay1e-2配合余弦退火。AdamW在torch中的实现如下import torch.optim as optim from torch.optim.lr_scheduler import CosineAnnealingLR optimizer optim.AdamW( model.parameters(), lr1e-4, weight_decay1e-2, ) # 训练总轮数小样本建议20~30轮 scheduler CosineAnnealingLR(optimizer, T_max30, eta_min1e-6)参数解释lr1e-4对迁移学习来说是安全性较高的起点比全新训练模型常用的1e-3低一个数量级——因为预训练特征已经很强过大的学习率会破坏已有特征。weight_decay1e-2对全连接层是合适的但后面解冻了backbone建议对backbone单独用更小的weight_decay避免把卷积核压得太狠。T_max要跟总epoch数一致否则余弦退火的最后阶段不会落在最小学习率上你会觉得模型“怎么还差一口气”。这里有个容易忽略的细节冻结backbone之后optimizer里其实只有fc层的参数参与更新但AdamW会维护每个参数的动量状态所以optimizer的param_groups结构没有变化只是实际更新的参数变少。此时完全可以加大学习率到3e-4甚至5e-4只训练单层分类头收敛速度会快很多且不容易翻车。解冻backbone微调时再退回更小的学习率。4.2 数据增强不能乱加卫星图的翻转、旋转与色彩扰动边界卫星图像和自然图像在数据增强上有一个本质区别卫星图的“语义”对旋转方向不敏感但对颜色失真非常敏感。建筑识别主要依赖屋顶材质、结构轮廓和阴影关系——其中阴影方向携带了重要的高度信息如果增强里加了过强的色彩扰动等于把这个信息抹掉了。我在这类数据集上对比过三组增强并记录验证集准确率结论如下增强策略验证集Top-1过拟合程度仅ResizeNormalize72%左右高训练集接近100%加翻转与15度旋转76%~78%中再加色彩扰动(饱和度±0.3)74%~75%中高场景里彩色区域越少效果越差不是说色彩扰动不能用而是说在卫星建筑这种“低饱和度高结构依赖”的场景里色彩的增益远不如自然图像。更有效的增强其实是随机裁剪在224×224输入上先Resize到256再RandomCrop到224×224相当于小范围平移配合旋转可以模拟不同观测角度下的轻微位置偏移。train_tf_v2 transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomCrop((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees20), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2, hue0), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ])注意ColorJitter里我把hue设为0——色相旋转对卫星建筑是灾难性的它会把蓝色屋顶变成绿色、把红色屋顶变成紫色这种数据在真实卫星影像里不存在。brightness和contrast可以保留但要控制在0.2以内。还有一个被低估的操作是RandomResizedCrop它同时做了缩放和裁剪相当于模拟遥感影像中不同空间分辨率下的观测但这个操作的scale下限不能太低默认的(0.08, 1.0)会裁出太多纯色背景在小样本上反而引入大量无信息训练样本。我一般设到(0.35, 1.0)并配合概率0.5使用而不是每张都走。4.3 验证策略单次划分不可信5折交叉验证才是小样本的后悔药单次的随机划分在1100张样本上非常不可靠——划分的运气成分可能让同一份模型的val acc在72%和80%之间波动。所以我在这类数据集上用的默认做法是5折交叉验证把训练数据分成5份轮流拿4份训练、1份验证最后把5次val acc的平均值当作模型的真实水平。from sklearn.model_selection import StratifiedKFold import numpy as np skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) fold_accs [] for fold, (train_idx, val_idx) in enumerate(skf.split(paths, labels)): train_sub torch.utils.data.Subset(train_ds, train_idx) val_sub torch.utils.data.Subset(train_ds, val_idx) train_loader_fold DataLoader(train_sub, batch_size16, shuffleTrue) val_loader_fold DataLoader(val_sub, batch_size32, shuffleFalse) model build_model(num_classes11).to(device) fold_acc train_one_fold(model, train_loader_fold, val_loader_fold) fold_accs.append(fold_acc) print(f5折平均val acc: {np.mean(fold_accs):.4f} ± {np.std(fold_accs):.4f})需要说明的是交叉验证的代价是训练时间乘以5但1100张小样本单折通常几分钟就结束了性价比极高。报告中给出“均值±标准差”比单一数字可信得多——比如78%±3%意味着你的模型在不同数据划分下可能落在75%到81%之间后续部署验收要按这个区间来预期。如果你的模型在5折上的方差超过5个百分点优先怀疑同类样本的聚簇效应比如同一个建筑区域的多个截图被分到不同折里模型其实在“背区域”而不是“认建筑”。5. 小样本卫星识别的踩坑记录过拟合、抖动和不均衡的翻车现场这一章全部是我在这类数据集上实际遇到过的问题每一条都按“现象→原因→解决”写。卫星建筑分类的坑往往不在模型而在数据组织和验证方式以下四条是高发区。5.1 现象训练集准确率接近100%验证集不到60%这是我的血泪经验里最常见的一种翻车。1100张数据、11个类别只要模型容量足够大训练集上达到100%分类准确率只需要10轮左右但验证集死活上不去。原因分两层一是数据量太小而模型容量太大模型把训练集里的背景纹理、光照角度的个别特征全部背下来了二是数据划分不够随机比如同一个建筑区域的多张截图被分进了训练集和验证集模型学到的“建筑”其实是“地理位置”验证集自然就差。打个比方验证的不是分类能力而是在考“记住这张图有没有出现过”。解决方法有两个。第一个是交叉验证——数据少的时候不要只依赖一个固定的8:2划分直接做5折每一折训练后记录验证准确率取均值作为模型真实水平。第二个是控制解冻尺度只在最后fc层训练时验证集通常卡在70%左右解冻最后两个残差块并配合更低学习率可以推到80%附近但前提是增强做够否则解冻只会加速过拟合。5.2 现象同一张图训练和预测结果不一致用ResNet50训练完做推理时我遇到过一种诡异现象训练时val loader上预测很准但单独加载一张图片走predict流程就出错。排查后发现是两条路径的预处理不一致——训练时transforms里有ResizeNormalize而推理脚本里忘了加Normalize导致输入分布完全漂移。这属于典型的“流水线不一致”。更隐蔽的同类坑在DataLoader的shuffle上。val_loader的shuffleTrue虽然不影响指标但它会让调试时每次跑出来的结果不一样误以为模型不稳定。修复方法是val和test的loader一律shuffleFalse并且给所有可能引入随机性的环节固定随机种子。import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed)这个函数在调用DataLoader和模型初始化之前执行。它解决的是“每次训练结果都不一样”的问题。但要注意设置了随机种子不代表完全可复现——cuDNN的auto-tune在不同显卡上仍可能引入差异所以更准确的期待是“同一台机器上稳定”而不是“跨机器可复现”。跨机器的稳定性需要额外开启torch.use_deterministic_algorithms(True)但它会显著放慢训练速度1100张的小样本上其实不划算。5.3 现象模型几乎总是预测同一个类别准确率却不降这是视觉上最隐蔽、数值上最狡猾的坑。有一种情况是某个类占总样本的30%以上模型学到“全预测这类”也能有30%多准确率且不易被察觉。此时看全局acc仍然有70%上下甚至还在缓慢上升但看一眼混淆矩阵会发现模型只对两个类有反应其余九个类是半放弃状态。我一开始也犯过这个错——只盯着total acc调参折腾了一周没进展。后来打印出每个类的recall才发现某三类在val集上的recall是0。解决办法是先做类别重采样让稀有类被抽到的概率提升或者在loss里传入class_weight向量。在PyTorch里前者更直接。from torch.utils.data import WeightedRandomSampler # 按样本数反比给每个样本设置权重 counts np.array([class_counts[c] for c in train_ds.classes]) weights 1.0 / counts.astype(float) sample_weights weights[train_ds.targets] # targets是每个样本的类别索引 sampler WeightedRandomSampler( weightssample_weights, num_sampleslen(sample_weights), replacementTrue, ) train_loader DataLoader( train_ds, batch_size16, samplersampler, num_workers4, pin_memoryTrue, )逻辑说明WeightedRandomSampler让样本数量少的类别在epoch内被重复抽样平衡每个类的梯度贡献。参数上replacementTrue表示允许同一张图在一个epoch里重复出现——这是这类sampler的默认语义不要改成False否则权重毫无意义。num_samples保持和原始样本数一致保证每个epoch的总迭代步数不变。代价是训练会稍微偏向稀有类对多数类的recall有一定挤压因此训练完成后一定要回到混淆矩阵逐个类检查而不是只看聚合指标。5.4 现象训练进行到一半报CUDA out of memory这对1100张小样本来说很常见——不是因为数据多而是因为调试时把batch_size、分辨率和num_workers一起调高了。我见过最小的显存崩溃场景是224×224输入、batch_size32、ResNet50加反向传播一张8GB卡刚好够但如果val评估还同时开着两个loader、又没加torch.no_grad()就会爆。解决方法是按优先级降显存占用先降batch_size到16甚至8再检查有没有不必要的中间变量被保存到计算图比如把loss.item()误写成loss、把val阶段也保留了requires_grad。还有一个技巧是val评估挪到每5个epoch跑一次而不是每个epoch都跑既省时间也减少显存峰值。至于为这1100张数据换一张24GB显卡纯属浪费——先检查代码里有没有显存泄漏比如每次迭代都新建了模型却没释放旧图这类问题一张卡也能折腾半天。6. 验收模型不要只盯acc混淆矩阵、逐类F1与多尺度推理6.1 混淆矩阵和逐类F1怎么判断模型“真的会了”前面折腾了这么多最后一步是把“准确率”这个黑匣子打开。1100张小样本模型全局Top-1 acc达到78%听起来不错但它完全可能隐藏着前面说的“只认两类”问题。我的验收习惯是固定一套代码输出每个类的precision、recall和F1再对着混淆矩阵看哪些类在互相打架。from sklearn.metrics import classification_report, confusion_matrix import torch def evaluate_model(model, loader, device): model.eval() y_true, y_pred [], [] with torch.no_grad(): for images, labels in loader: images images.to(device) logits model(images) preds logits.argmax(dim1).cpu().numpy() y_true.extend(labels.numpy()) y_pred.extend(preds) print(classification_report( y_true, y_pred, target_namesloader.dataset.classes, digits3, )) return confusion_matrix(y_true, y_pred)注意evaluate时model要处于eval模式BatchNorm层才会用累积的running stat而不是当前batch的统计量。这是一个非常常见的坑漏了model.eval()验证集上指标会不正常波动因为BatchNorm在train和eval模式下的行为完全不同。逐类F1的目标是每一类至少过0.7如果某一类F1明显低于其他类回到5.3节的处理路径去查样本量和特征重叠。6.2 多尺度推理与滑窗把单图分类带进真实部署如果你准备把模型部署到实际卫星影像应用里单张裁剪图分类和整幅遥感影像分类之间还隔着一道多尺度推理的鸿沟。真实卫星影像通常是数千甚至数万像素宽建筑物只占画面的很小一部分直接把整幅图缩放到224×224喂给模型会丢失全部结构细节。常见做法是滑窗推理按步长在整幅图上切出224×224的patch逐patch分类再把预测结果拼成一张类别索引图。推理时还可以对每个patch做水平翻转把多次预测结果取平均这会稳定地把单patch的acc往上推一点但推理时间会成倍增加patch分辨率过低时收益并不大。落地时还有一个容易忽略的点把模型预测值和类别色带映射到输出图上生成一张可视化结果图拿结果图和原始卫星影像叠加检查比看任何数字指标都直观。我自己的习惯是无论数据集多小都会把验证集里每张图的预测结果和原图并排保存挑出预测错的图挨个看是标错了还是模型真的认不出在这个数据集上往往是五五开。这个习惯帮我躲过了好几次“数据问题伪装成模型问题”的返工。希望这些步骤和踩坑对你能有点帮助从1100张的已标注数据集出发把卫星建筑分类的整个链路稳稳踩通。本文还有配套的精品资源点击获取
返回列表