资讯动态

卫星云图识别:从CSV数据到PyTorch CNN全流程实战

发布时间:2026/9/28 11:05:24 来源:尧图企业网站定制
简介面向计算机视觉课程大作业与毕业设计场景这份资料包围绕卫星云层图像的理解与识别任务提供了完整可运行的 Python 源码、文档说明、报告 PPT 与实验报告。项目代码经测试可正常执行覆盖数据预处理、模型训练与结果分析等环节适合计科、人工智能、通信工程等专业学生在课程设计、项目初期立项或答辩演示中参考也便于在已有代码基础上进行二次扩展功能。包内共 140 个文件以 70 个 Python 源码文件为主附有编译生成的 pyc、Shell 脚本、CSV 数据表、YAML 配置文件、PNG 结果图以及 README、实验报告 PDF 和 PPTX 演示文稿整体约 62.3 MB。文件组织方式清晰便于按代码、数据、配置和说明文档分模块查阅能节省整理资料的时间。目前已有 239 人学习下载。从内容预览看内含训练与测试的 CSV 数据、多组模型测试结果、实验报告 PDF 和结果图片读者可直接获取项目全貌、复现识别流程并参考其文档与答辩材料完善自己的大作业或毕设展示。1. 卫星云图识别大作业从CSV到可跑的CNN全流程拿到“计算机视觉大作业卫星云层图像的理解与识别”这套资源时绝大多数人的第一反应是找现成的图片文件夹结果打开train_700.ver0.csv才发现数据全在文本表格里。这不是数据坏了而是小规模课程数据集常用的保存方式700张图每张图按像素拉平成一行标签放在第一列。整套资源包含训练集、四个测试CSV、Python源码、实验报告PDF和汇报PPT目标是让一个没有完整跑过深度学习流程的人在一天内把卫星云图分类从数据加载做到测试评估。它适合正在做计算机视觉课设、毕设初期演示或者想用一份规范实验报告模板的同学。接下来的章节我按自己拆这套项目时的顺序来讲先处理数据再选模型然后训练、评估最后是几个必须避开的坑。2. 先把数据集吃透CSV图像读取、还原与划分的三个实操点2.1 为什么用CSV装图像卫星云图大作业选CSV而不是图片文件夹我一开始也疑惑。后来拆完发现CSV对小数据集有实打实的好处。第一是版本管理方便700个样本的像素改动在git diff里一目了然这对课设答辩时讲“我改过什么”很有价值第二是pandas直接能读做类别分布统计、像素均值方差分析不用另外写IO逻辑第三是省去了图片路径在不同操作系统上的兼容问题Windows和Linux下不会出现路径分隔符翻车。当然它的边界很明显CSV按文本存像素一张512×512的图拉平就是26万列文件会迅速膨胀所以这种格式只适合几百到几千张图的小样本超出这个量级还是老老实实用TFRecord或者按文件夹组织图片的ImageFolder方案。2.2 读取CSV并还原成图像张量常见的做法是先读进来看看shape再决定怎么reshape。我一般用pandas读入打印前几行确认标签列import pandas as pd import numpy as np df pd.read_csv(train_700.ver0.csv) print(shape:, df.shape) print(df.head(2))第一次打印shape时要注意两个数字行数是样本数700列数减1就是单张图像的像素总数。如果列数是4097说明每张图4096个像素开方正是64那就是单通道64×64如果不能整除就得考虑是不是RGB三通道。这一步判断决定了后面的reshape参数值得花30秒确认。接下来把标签和像素拆开。这里有个细节读入像素要用float32不要用int因为CSV里有些值可能带小数用int会直接截断云层灰度细节会丢得干干净净。labels df.iloc[:, 0].values.astype(np.int64) pixels df.iloc[:, 1:].values.astype(np.float32) img_size (64, 64)然后写一个标准Dataset类把一行像素还原成图像张量import torch from torch.utils.data import Dataset class CloudDataset(Dataset): def __init__(self, pixels, labels, img_size(64, 64)): self.pixels pixels self.labels labels self.img_size img_size def __len__(self): return len(self.labels) def __getitem__(self, idx): img self.pixels[idx].reshape(1, *self.img_size) img torch.from_numpy(img) / 255.0 label torch.tensor(self.labels[idx], dtypetorch.long) return img, label这段代码的逻辑说明reshape成(1, 64, 64)是PyTorch的NCHW约定单通道要放最前面除以255把像素归一到[0, 1]区间让反向传播更稳定。为什么不在Dataset里做标准化而要放到后面单独做因为标准化用的均值和标准差只能从训练集算在Dataset里直接套用全局数据极容易污染验证和测试数据。如果CSV列数不是4096把img_size改成对应分辨率即可。这一节里我踩过的坑是把图像尺寸写死换数据集时报维度不匹配所以建议在类外面定义img_size并打印验证。2.3 训练集与验证集划分标准化必须在划分之后数据加载类写完后不要急着训练先做train/val划分。我一般用sklearn的train_test_split并且一定要开stratify保证云层每个类别在训练集和验证集里的比例一致from sklearn.model_selection import train_test_split X_train, X_val, y_train, y_val train_test_split( pixels, labels, test_size0.2, stratifylabels, random_state42) mean X_train.mean() std X_train.std() X_train (X_train - mean) / (std 1e-8) X_val (X_val - mean) / (std 1e-8) train_dataset CloudDataset(X_train, y_train) val_dataset CloudDataset(X_val, y_val)这里有两个关键点第一mean和std只能用X_train算哪怕X_val的信息就摆在手边也不能合起来算否则验证集的分布信息渗进特征标准化环节属于典型的数据泄露后面评估结果会虚高第二标准差分母加1e-8是为了防止像素值全相同导致除零。划分完后打印一下每个类别的样本数量确认没有哪个类只剩个位数的样本不然训练阶段模型会直接忽略它。至于random_state我习惯固定成42这样多次复现结果完全一致报告里写“随机种子42”也显得严谨别人照着跑能拿到同一份数字。3. 训练一个能用的CNN模型结构、训练参数与日志记录3.1 CNN为什么适合卫星云图卫星云图的识别本质是纹理分类云层的厚度、边界、卷云和积云在局部区域内有明显的图案差异。CNN通过卷积核在整张图上滑动提取局部纹理再用池化逐步聚合天然具备平移不变性云在画面偏左还是偏右不影响分类结果。如果换成全连接网络64×64的图输入层就有4096个节点第一层全连接的权重就是百万级在700张训练图上是灾难。所以在这个任务里两层卷积加两层全连接是性价比很高的结构卷积负责提取特征全连接负责做最终分类。自己搭而不是一上来就上ResNet是因为课设场景里老师往往要求核心模型可以讲清楚前向流程而且这么大的数据量用深层网络反而是负优化。3.2 从零搭一个轻量CNNimport torch.nn as nn import torch.nn.functional as F class CloudCNN(nn.Module): def __init__(self, num_classes2): super().__init__() self.conv1 nn.Conv2d(1, 16, kernel_size3, padding1) self.conv2 nn.Conv2d(16, 32, kernel_size3, padding1) self.pool nn.MaxPool2d(2) self.fc1 nn.Linear(32 * 16 * 16, 64) self.fc2 nn.Linear(64, num_classes) self.dropout nn.Dropout(0.3) def forward(self, x): x self.pool(F.relu(self.conv1(x))) # 64x64 - 32x32 x self.pool(F.relu(self.conv2(x))) # 32x32 - 16x16 x x.view(x.size(0), -1) x self.dropout(F.relu(self.fc1(x))) return self.fc2(x)逻辑说明conv1从1通道变成16通道padding1保持尺寸不变经过第一层池化变成32×32conv2扩到32通道再池化成16×16所以全连接层的输入维度是32×16×168192。加Dropout是为后面过拟合留的缓冲小数据集上模型记忆训练数据太容易0.3的丢弃比例通常够用。注释里标出了每一步的空间尺寸变化这是答辩时老师必问的东西——如果讲不清特征图怎么从64变到16报告会扣分。建议第一轮跑通后打印model摘要看每层输出形状是不是和预期一致再进完整训练。3.3 训练参数怎么定训练参数我用下表给出推荐值这样报告里可以直接引用参数推荐值说明batch_size32700张图太小64会让梯度震荡变小epochs30~50小数据集上过50轮后验证loss通常开始回升optimizerAdam自适应学习率省去手动调整的麻烦lr1e-3Adam下1e-3是稳妥起点跑不稳再降到3e-4schedulerCosineAnnealingLR让学习率在训练后期平滑衰减lossCrossEntropyLossPyTorch里该损失自带Softmax损失函数这块有个初学者常走的弯路在模型forward里先过一遍Softmax又在CrossEntropyLoss里过一次两个Softmax叠在一起反而让梯度变小。CrossEntropyLoss已经把Softmax和交叉熵合并了所以模型的最后一层就输出原始logits不要额外加激活函数。3.4 训练循环与日志记录训练循环我习惯封装成一个函数把每个epoch的loss和acc存下来def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss, correct, total 0.0, 0, 0 for imgs, labels in loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() out model(imgs) loss criterion(out, labels) loss.backward() optimizer.step() total_loss loss.item() * imgs.size(0) correct (out.argmax(1) labels).sum().item() total imgs.size(0) return total_loss / total, correct / total这个函数的逻辑是每批数据先清零梯度前向得到out和目标标签算交叉熵loss反向传播后更新参数。计算正确率时用argmax(1)取每个样本得分最高的类别再跟labels比较。用out.argmax(1)而不是torch.max(out, 1)的原因是前者直接返回索引代码意图清晰。调用时每轮验证集也跑一遍把结果追加到日志列表train_log, val_log [], [] best_acc 0.0 for epoch in range(30): tr_loss, tr_acc train_one_epoch(model, train_loader, optimizer, criterion, device) va_loss, va_acc evaluate(model, val_loader, criterion, device) train_log.append((tr_loss, tr_acc)) val_log.append((va_loss, va_acc)) if va_acc best_acc: best_acc va_acc torch.save(model.state_dict(), best_model.pth) print(fepoch {epoch:02d} | train {tr_loss:.4f}/{tr_acc:.4f} | val {va_loss:.4f}/{va_acc:.4f})设备管理这里注意在一开始就把device写成torch.device(cuda if torch.cuda.is_available() else cpu)千万别在脚本里到处写死cuda否则在无GPU的机器上整个脚本直接崩掉。保存模型时只存state_dict不存整个model对象这样换机器加载方便也不会因为类定义位置不同而反序列化失败。4. 测试集评估与实验报告四个测试文件怎么用、指标怎么看4.1 四个测试CSV的关系资源里同时出现了model_test_1.csv、model_test_3.csv、model_test_4.csv和test.csv第一次看到时我也愣了一下。看名字前三个更可能是不同实验阶段第1轮、第3轮、第4轮留出的测试分片test.csv是最终统一的测试集。怎么验证这个判断简单办法是分别读进来看行数和列数是否一致如果数据来源相同特征分布应当接近。我倾向于把它们当四份独立测试集分别跑预测再对比结果如果评分环节只要求一个数字就以test.csv为准其他三个作为调试参考。这样做还有一个额外的好处答辩时能拿“多测试集交叉验证”当作加分项说明你考虑了模型的稳定性。4.2 预测与评估指标加载训练好的权重在测试CSV上做预测def predict_csv(model, csv_path, mean, std, img_size(64, 64), devicecpu): df pd.read_csv(csv_path) pixels df.iloc[:, 1:].values.astype(np.float32) pixels (pixels - mean) / (std 1e-8) imgs torch.from_numpy(pixels).view(-1, 1, *img_size) model.eval() with torch.no_grad(): preds model(imgs.to(device)).argmax(1).cpu().numpy() return preds注意这里标准化用了训练集算好的mean和std原因和验证集一样不能拿测试集自己的均值来标准化否则等价于把测试集的分布信息提前暴露给模型。跑完后打印预测结果的类别分布如果某一类占比异常高说明模型预测有偏要回到混淆矩阵排查。评估不能只看准确率。卫星云图类别常有不均衡某一类占80%时全猜这一类的准确率也有80%看起来很漂亮但模型没有泛化能力。我一般会打印混淆矩阵和F1分数from sklearn.metrics import classification_report, confusion_matrix print(classification_report(y_val, val_preds, digits3)) print(confusion_matrix(y_val, val_preds))classification_report会按类别给出precision、recall、f1-score哪个类别拖后腿一目了然。判断模型是否可用时我的标准是多数类准确率高不算达标少数类的recall必须也有实际值否则答辩时老师挑一个样本追问就能难住你。指标关注点场景Accuracy总体正确比例类别均衡时参考Precision预测为某类的可信度误报成本高的场景Recall某类能不能找全不均衡数据必看F1两者的调和平均综合排名用4.3 实验报告的组织资源里的实验报告PDF是一个现成的写作模板我拆下来觉得它的结构值得复用。这类课设报告的套路是实验目的与数据集介绍、预处理流程、模型设计、实验参数、结果分析与对比。最关键的是不能只贴训练好的最终结果得有中间对照比如把只用两层卷积和三层卷积都跑一遍在报告里放两个准确率再解释为什么在这个数据量下浅层更稳。报告里的图表无非是训练曲线、混淆矩阵、几张预测结果的样例可视化用matplotlib就能出重点是每张图下面必须有结论性说明而不是只写“如图所示”。这份报告PDF正好是个结构参考比从零排版省事得多。5. 卫星云图识别避坑指南五个翻车现场的排查记录这一章把拆这套资源时实际遇到的坑按“现象→原因→解决”整理出来每一条都是先有现象再有结论可以按编号快速对照。5.1 数据泄露验证集准确率虚高的真相现象训练集、验证集准确率都到0.98换到test.csv只有0.7差距大得不正常。原因数据标准化时手滑用了全部数据的mean/std验证集和测试集的分布信息已经参与计算模型在验证集上被“剧透”了。解决严格按划分后的数据分别计算先train_test_split再取X_train.mean()和X_train.std()。从那以后我每次写预处理都在代码里加注释标注mean和std的来源防止回头自己都忘了数据是怎么算的。5.2 类别不均衡让准确率失真现象训练日志显示准确率一直上升但查看混淆矩阵发现有一整类几乎没预测对。原因云层数据里某类占绝大多数模型把所有样本都推向多数类就能拿到高准确率少数类的梯度信号被淹没。解决打印每个类的样本数如果差距超过10倍给DataLoader加WeightedRandomSampler让少数类样本在每轮中被抽到的概率提高效果不理想时再给损失函数加class_weight作为补充。5.3 小数据集上的过拟合现象训练loss降到0.05验证loss反而从0.3涨到0.8训练曲线后期完全背离。原因两层卷积加两层全连接对700张图来说容量已经偏大epoch跑到后期模型开始背训练样本而不是学泛化特征。解决把Dropout从0.3提到0.5减少conv1的通道数比如从16降到8同时监控验证loss连续5轮不降就early stop保存历史最优权重而不是最后一轮权重。5.4 设备写死导致换机器直接崩现象在自己笔记本上跑得好好的脚本拿到有GPU的机器上反而报错或者反过来GPU环境下写的代码在CPU机器上直接RuntimeError。原因代码里出现了imgs.cuda()这类写死GPU的调用环境一换就失效。解决所有张量和模型统一走device torch.device(cuda if torch.cuda.is_available() else cpu)数据加载后执行.to(device)。习惯上我在脚本最开头集中定义device后面一律引用变量不要再出现device字样以外的硬编码。5.5 CSV像素值精度丢失与噪声现象训练loss下降奇慢把预测错误的图片画出来发现全是颗粒状噪点云层纹理完全看不清。原因读取像素时用了int类型CSV里的小数被截断有些列本身存在缺失值直接读成了0。解决读入用astype(np.float32)再画一张图检查像素分布是否连续直方图集中在几个孤立峰值就说明精度丢了。缺失值先看df.isna().sum()把带有缺失值的行删掉或按列均值填充别让0值混进正常像素。6. 迁移学习与可视化验证不做重复实验的进阶做法6.1 把ResNet18接在云图数据上如果老师允许用预训练权重最省事的进阶路线是用torchvision自带的ResNet18做迁移学习。卫星云图和ImageNet的纹理结构不完全一样但底层边缘、颜色过渡这些特征是可以复用的。常见做法是冻结backbone只替换最后的全连接层import torchvision.models as models model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) for param in model.parameters(): param.requires_grad False model.fc nn.Linear(512, num_classes)这段代码逻辑weightsmodels.ResNet18_Weights.DEFAULT会加载ImageNet预训练权重冻结全部参数后只有最后接入的Linear层可训练前面多层学到的通用特征不需要再调整。这里需要注意resnet18默认输入是3通道而CSV里只有单通道灰度。两种处理方式一是把单通道复制三次变成3通道二是修改backbone第一个卷积层的输入通道为1并加载权重时删掉对应参数。第一种稳妥第二种省内存但要额外处理权重名新手建议直接用第一种。6.2 用曲线和样例图验证结论训练完成后把train_log里的loss和验证准确率画出来两条曲线一起看。训练损失还在下降但验证曲线开始抬升是过拟合的明确信号损失曲线出现锯齿状波动说明学习率偏大把lr降到3e-4重跑一轮。再把混淆矩阵画成热力图哪些类互相搞混一目了然比如卷云和薄云经常混在一起可以在报告里写成“两类特征相近需要增加纹理方向的增强”。最后随机挑几张测试图片把预测标签和置信度标在图上这是答辩时最直观的展示素材。从那以后我每次拿到这类课设源码都先做一个单样本前向确认输入维度和数据范围画一张样本图再跑完整训练循环节省下来的排错时间比这点步骤多得多。这套资源从数据到报告模板都是现成的下载后对照README把文件放到位按我上面的顺序跑一遍就能出完整结果。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑