
简介面向遥感图像分类任务WHU-RS19土地利用类型遥感卫星图像分类数据集是一套已标注的基准数据包含机场、海滩、桥梁、商业区、沙漠、农田等19类典型地物总量约1000张遥感影像覆盖常见土地利用场景。数据适合高校学生、科研人员及深度学习入门者既可用于CNN分类、迁移学习的快速上手也可作为算法精度对比的公共测试集。压缩包共1016个文件以1006张jpg图像为主体另有标签映射json、可视化脚本py和系统缩略图db整体约100MB资源已预先划分训练集与测试集同类图片按类别存放能直接接入分类项目的数据管道省去自行整理标注的时间。目前已有199人学习下载。附带show脚本可快速浏览各类别样例帮助核对标注与数据分布结合作者博客中的CNN分类、图像分割和YOLOv5改进等系列项目还可延伸到目标检测、语义分割等场景一站式掌握遥感数据从组织、训练到效果评估的完整流程。1. WHU-RS19 遥感图像分类数据集19 类标注、约 1000 张图第一份实验数据做遥感图像分类的人起步阶段最头疼的不是模型选型而是手里没有一份能直接用的标注数据。自己从 Google Earth 截图再手工标注一天能凑出三五十张就算快的标完还得担心类别分布歪了、尺寸不统一。WHU-RS19 就是拿来解决这个问题的武汉大学发布的遥感卫星图像分类数据集覆盖 19 种土地利用类型包含 airport、beach、bridge、commercial、desert、farmland 等常见地物类别总计约 1000 张已标注图像并已划分好训练集与测试集每个类别的图片独立存放在对应文件夹中。你不需要再写复杂的解析脚本PyTorch 的 ImageFolder 直接能读。资源里还带了一个 show 脚本可以可视化整个数据集。适合课程设计、毕设预研、算法对比也适合想快速跑通 CNN 分类流程的从业者。2. 数据目录与标注结构先看 labels.json 再谈训练很多人拿到数据集第一件事就是直接开训结果跑到一半才发现类别顺序对不上、训练测试划分跟预期不一致。花十分钟把目录结构和标签映射理清楚后面能省下大量排查时间。2.1 19 个类别的全景从 airport 到 viaductWHU-RS19 的 19 个类别覆盖了遥感图像中最常见的土地利用场景。除了摘要里提到的 airport、beach、bridge、commercial、desert、farmland还包括 forest、industrial、meadow、mountain、park、parking、playground、pond、port、railwayStation、resort、river、viaduct。全部类别如下表类别名含义类别名含义airport机场parking停车场beach海滩playground操场bridge桥梁pond池塘commercial商业区port港口desert沙漠railwayStation火车站farmland农田resort度假村forest森林river河流industrial工业区viaduct高架桥meadow草地mountain山地park公园每个类别大约 50 张图像总体接近 1000 张。这个分布比较均匀不像 ImageNet 那种长尾分布对新手友好。需要特别注意的是pond、park、meadow 这三类外观有重叠比如公园里可能有草地和水塘模型容易混淆后面评估时要重点盯这几类。2.2 目录结构拆解train/test 双目录加类别子文件夹资源解压后的目录结构大致如下WHU-RS19/ ├── train/ │ ├── airport/ │ │ ├── airport_001.jpg │ │ ├── airport_002.jpg │ │ └── ... │ ├── beach/ │ ├── bridge/ │ └── ... ├── test/ │ ├── airport/ │ │ └── ... │ ├── beach/ │ └── ... ├── show.py └── labels.jsontrain 和 test 下各自按类别建文件夹同一个类别的图像全部放在对应目录中。labels.json 里记录了类别名称与索引的映射关系具体内容以资源实际文件为准。这种目录组织方式与 COCO、VOC 那种带独立 annotation 文件的结构完全不同——它不需要坐标框、不需要分割掩码目录名就是标签。PyTorch 里用torchvision.datasets.ImageFolder可以直接读取连自定义 Dataset 都不用写。2.3 训练集与测试集的划分逻辑资源已经按类别将图像划分到了 train 和 test 目录常见的划分比例在 4:1 到 8:2 之间具体以实际目录里的文件数量为准。这意味着你不需要再手动做train_test_split省了一步操作。但这个省事背后有一个隐患划分是固定的同一景观下不同角度拍摄的图像可能同时出现在训练集和测试集里。模型如果记住了拍摄区域的光照、纹理背景测试指标会虚高。这个坑我在第五章会专门展开。如果你要做严谨的实验建议在固定划分基础上再做一次 StratifiedKFold 交叉验证确认模型的泛化能力不是来自数据划分的偶然性。3. 用 show 脚本做可视化检查训练前 5 分钟的数据体检数据拿到手先别急着上模型。我一般会强制自己先跑一遍可视化脚本把每类图像都看一遍确认数据质量、标注一致性、图像尺寸分布是否符合预期。这个习惯帮我挡掉过好几次训练到一半才发现数据是坏的的翻车现场。3.1 show.py 的运行方式与参数设置资源里的 show 脚本可以直接可视化数据集常见做法是在命令行指定数据目录和每类抽样数量python show.py --data_dir ./WHU-RS19/train --num_samples 5脚本会从每个类别中随机抽取指定数量的图像拼接成网格显示。这里的--data_dir指向包含类别子文件夹的根目录--num_samples控制每类显示多少张图像。如果你的脚本不支持命令行参数直接打开文件修改顶部路径变量也可以。运行前先确认环境里装了 matplotlib否则会报ModuleNotFoundError。3.2 可视化能发现的三类问题跑完可视化重点检查三个方面。第一图像尺寸是否接近。WHU-RS19 的图像大多在 600×600 像素附近但不同来源的图像会有裁切差异。如果某类图像的尺寸明显偏离主流值说明采集来源不一致训练时需要用 Resize 统一。第二类别内图像风格是否一致。比如 commercial 类别里如果混入了 residential 的图像模型学到的就不纯。第三标注是否有明显错误——farmland 里出现密集建筑物、river 里出现大面积陆地这类问题肉眼一看就能发现但如果不做可视化它会成为模型训练里的黑匣子你永远不知道 loss 不降是模型问题还是数据问题。3.3 补充一个图像尺寸统计脚本show 脚本只能看单张图整体尺寸分布还得靠统计脚本。我一般会额外写一个快速统计import os from PIL import Image base_dir ./WHU-RS19 size_counter {} for split in [train, test]: split_dir os.path.join(base_dir, split) for cls_name in os.listdir(split_dir): cls_dir os.path.join(split_dir, cls_name) if not os.path.isdir(cls_dir): continue for img_name in os.listdir(cls_dir): img_path os.path.join(cls_dir, img_name) with Image.open(img_path) as img: size img.size size_counter[size] size_counter.get(size, 0) 1 for size, count in sorted(size_counter.items(), keylambda x: -x[1])[:10]: print(f{size}: {count} 张)这段代码遍历 train 和 test 下所有类别的 jpg 文件用Image.size读取宽高并计数。逻辑很简单但能直接告诉你数据集的尺寸分布是否集中。如果出现多种尺寸混在一起训练时的 Resize 策略就要格外注意如果所有图像尺寸一致则预处理可以省掉 Resize 这一步。4. 把 WHU-RS19 接进 PyTorch 跑 CNN 分类加载、增强与评估参数设置WHU-RS19 的目录结构决定了它在 PyTorch 里非常好用。这一章我按实际训练流程从数据加载到评估指标逐段说清参数怎么设、为什么这么设。4.1 用 ImageFolder 加载数据集并构建标签映射torchvision.datasets.ImageFolder会自动按子目录名生成标签省去手写 Dataset 的麻烦from torchvision import datasets, transforms train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) test_transform 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_dataset datasets.ImageFolder(./WHU-RS19/train, transformtrain_transform) test_dataset datasets.ImageFolder(./WHU-RS19/test, transformtest_transform) print(train_dataset.class_to_idx)逻辑说明ImageFolder扫描./WHU-RS19/train下的子目录按字母序为每个类别分配索引class_to_idx里存的就是映射关系。Resize((224, 224))把遥感图统一到模型输入尺寸RandomHorizontalFlip做随机水平翻转是一种极低成本的增强ToTensor把 PIL 图像转成张量并归一化到 0-1Normalize用 ImageNet 的均值方差做标准化。这里有个容易被忽略的细节Normalize的 mean/std 虽然是 ImageNet 统计值但遥感图像和自然图像在底层特征上是共享的直接用这个标准化不会出问题。不建议因为遥感是特殊域就自定义统计值除非你的验证指标明确显示有必要。4.2 模型主干与训练参数设计WHU-RS19 每类只有 50 张图这个数据量撑不起 ResNet50 或更深的网络。我一般用 ResNet18 做主干利用 ImageNet 预训练权重import torch import torch.nn as nn from torchvision import models model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 19) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size10, gamma0.1)参数说明model.fc nn.Linear(..., 19)把最后一层全连接改成 19 维输出匹配类别数。lr1e-3是 Adam 在迁移学习场景下的常用起点如果 loss 震荡可以降到 5e-4。StepLR每 10 个 epoch 把学习率乘 0.1帮助收敛末期更稳定。关键点在于batch_size的设置。训练集总共约 800 张如果batch_size64每个 epoch 只有 12 次迭代梯度更新太稀疏。我一般固定batch_size16加上 shuffle让每个 epoch 有足够的更新步数。4.3 数据增强的取舍遥感图与自然图的差异数据集规模小增强策略直接决定过拟合程度。WHU-RS19 是俯视角遥感图与 ImageNet 的自然图相比没有严格的上下概念所以旋转增强非常适合train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(degrees30), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])RandomRotation(degrees30)在 ±30 度范围内随机旋转对遥感图像来说几乎没有语义损失。ColorJitter调节亮度对比度模拟不同光照条件下的成像差异。这里不推荐使用RandomResizedCrop随机裁剪因为 WHU-RS19 的类别语义分布在整个图像中比如机场需要看到跑道整体结构才能判断裁剪过度反而会丢失关键判别信息。4.4 评估指标别只看总准确率19 类分类任务总 accuracy 只是及格线。WHU-RS19 里 pond、park、meadow 外观相似commercial 和 industrial 也有重叠必须看 per-class 指标和混淆矩阵from sklearn.metrics import confusion_matrix, classification_report import numpy as np model.eval() all_preds [] all_labels [] with torch.no_grad(): for inputs, labels in test_loader: outputs model(inputs) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) print(classification_report(all_labels, all_preds, target_nameslist(test_dataset.class_to_idx.keys()))) cm confusion_matrix(all_labels, all_preds) print(cm)逻辑说明classification_report输出每个类别的 precision、recall、f1-scoreconfusion_matrix打印具体错分关系。如果发现 pond 和 park 互相错分严重这个网络的判别力不够要么换更大的模型要么增加对应类别的训练样本。5. 避坑指南WHU-RS19 使用中的 5 个真实踩坑记录这一章全部来自实际训练中的教训每一条都是真金白银换来的。我之前带学生跑这个数据集几乎每个人都至少踩中一条。5.1 测试集指标虚高训练 95%测试只有 70%现象训练集准确率冲到 95% 以上测试集只有 70% 出头明显过拟合却查不到原因。原因WHU-RS19 的 train/test 划分是固定的某些类别的测试图与训练图来自同一景观区域模型学到的其实是拍摄位置的光照与背景特征而不是地物语义特征。解决不要只依赖固定划分做评估。用StratifiedKFold做 5 折交叉验证每次用 4 折训练、1 折验证综合 5 次结果。如果交叉验证的均值和固定划分差很多说明固定划分的结果不可信后续实验以交叉验证为准。5.2 show 脚本在 Windows 上一闪而过现象双击运行show.py图片窗口闪一下就消失了什么都看不清。原因脚本没有阻塞等待用户查看plt.show()执行完进程就结束了Windows 控制台随之关闭。常见做法是在脚本末尾加input(Press Enter to exit...)保证窗口停留。解决从命令行运行python show.py或者在脚本里加入阻塞等待。如果脚本本身不报错看到的窗口一闪而过全都是这个问题。5.3 训练中途报 PIL.UnidentifiedImageError现象训练到第 n 轮突然抛PIL.UnidentifiedImageError进程中止且每次报错的文件可能相同。原因数据在下载或解压过程中某个 jpg 文件头损坏PyTorch 的 DataLoader 在读取时才解码平时不触发一遇到就崩。解决在训练前先用verify()全量扫描一遍不要用load()因为verify()只校验文件完整性速度快得多from PIL import Image import os for root, dirs, files in os.walk(./WHU-RS19): for f in files: if f.lower().endswith(.jpg): path os.path.join(root, f) try: Image.open(path).verify() except Exception as e: print(f损坏文件: {path}错误: {e})扫描后把损坏文件移到单独目录不要直接删除。万一后面需要追溯数据来源还有后悔药可吃。5.4 类别映射错位预测标签和实际类别对不上现象训练正常但推理时保存的类别名与图片实际内容不符比如把 river 预测成了 pond输出的标签还是错的。原因ImageFolder的类别顺序按文件夹名字母序排列airport 自然排在最前面索引为 0。但如果你自己写了标签映射用的是 labels.json 里的顺序两边的索引就对不上了。解决始终以dataset.class_to_idx为准。训练完保存模型时同时把class_to_idx存成 json推理时加载同一个映射文件。不要自己硬编码类别与索引的关系也不要直接读 labels.json 自带的顺序。5.5 batch 维度不一致stack expects each tensor to be equal size现象DataLoader 在 collate 时报错RuntimeError: stack expects each tensor to be equal size。原因一部分图像没有走 Resize或者 transform 顺序错误——把Resize放在了ToTensor之后。ToTensor已经把 PIL 图像转成 TensorTensor 上不能再做Resize。解决检查 transform 顺序必须是Resize → ToTensor → Normalize。再确认传入ImageFolder的 transform 不是 None并打印一张图像的 shape 做验证sample train_dataset[0][0] print(sample.shape) # 期望 torch.Size([3, 224, 224])6. 进阶技巧把分类数据集改造成伪检测数据的做法WHU-RS19 是图像级分类标注没有目标边界框。但如果你想在这个数据集上跑目标检测的预实验有一个低成本改造方案把每张 600×600 的图切成 4 个 300×300 的 patch每个 patch 继承原图的类别标签得到一个伪检测数据集。这个做法解决的是检测任务冷启动问题——在真实检测标注数据不足时先验证检测网络的收敛能力。import cv2 import os def split_image(src_path, out_dir, size300): img cv2.imread(src_path) h, w img.shape[:2] for i, (y, x) in enumerate([(0, 0), (0, w // 2), (h // 2, 0), (h // 2, w // 2)]): patch img[y:y size, x:x size] out_name f{os.path.basename(src_path)[:-4]}_{i}.jpg cv2.imwrite(os.path.join(out_dir, out_name), patch) # 使用示例 split_image(./WHU-RS19/train/airport/airport_001.jpg, ./patches/airport)逻辑说明(0, 0)取左上角(0, w // 2)取右上角(h // 2, 0)取左下角(h // 2, w // 2)取右下角。每块 300×300 继承原图的机场标签四张 patch 都算作正样本。这样做出的数据集可以用于训练 patch-level 分类器相当于在特征图上做滑窗定位是检测的雏形。如果后续配套 YOLO 的标签格式就只需要把每个 patch 对应的图像坐标换算成边界框坐标。我从那以后每次拿到新的遥感数据集都会强制走一遍 show 脚本看数据、统计尺寸分布、跑一次小模型基线、再上正式训练的流程。这个习惯帮我挡掉了至少两次数据损坏和一次标签错位导致的翻车。希望帮到你。本文还有配套的精品资源点击获取