多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

TOA深度学习反演PM2.5:从MODIS原始反射率到站点浓度全流程

TOA深度学习反演PM2.5:从MODIS原始反射率到站点浓度全流程 简介基于Python与深度学习的遥感毕业设计项目聚焦TOA大气层顶数据反演PM2.5浓度面向遥感、环境、计算机、人工智能等专业学生以及需要完成毕设、课设或项目演示的开发者。资源提供完整源码与配套文档覆盖数据预处理、特征提取、深度学习建模、PM2.5反演结果输出等环节代码均测试通过答辩评审平均分达94.5分既适合零基础入门也可作为二次开发基底。包体共6个文件以4个Python脚本为主承担数据准备、索引提取、数值反演等任务另有1个Jupyter Notebook用于分步演示“TOA反演PM2.5”全流程1个说明文档帮助快速上手。压缩包仅12KB轻量且结构清晰便于本地运行与对照学习。目前已有244人学习下载。配套说明文档可帮助读者快速建立项目脉络重点理解遥感反演中深度学习模型的输入输出设计、参数设置与调优思路并借鉴其工程目录组织方式迁移到其他遥感或环境监测任务中。1. TOA深度学习反演PM2.5为什么拿大气顶反射率直接训网络把MODIS在大气顶TOA测到的反射率跳过大气校正直接扔给深度学习网络去拟合地面站点PM2.5浓度——这个思路听起来有点玄学但真跑通之后精度往往不输传统物理反演。这个源码包就是把整件事拆成一套完整可落地的毕设材料数据准备脚本把站点观测和卫星像元对齐成训练矩阵Deep.py负责网络训练最后的TOA反演PM2.5.ipynb把流程串成答辩现场能一步步演示的材料。适合遥感、人工智能、自动化方向做毕设的学生也适合刚入手遥感反演、想快速看到完整pipeline的新手。它解决的核心问题是给了你一份从数据清洗到精度评估、再出反演图的全程源码你只需要换自己的数据跑通。2. 数据准备与像元提取三个脚本把站点观测变成训练矩阵2.1 反演原理气溶胶如何影响TOA信号PM2.5反演要理解一个前提气溶胶粒子会对太阳辐射产生散射和吸收PM2.5浓度越高大气顶的反射率信号就会被扰动得越明显。物理反演的经典做法是查辐射传输查找表但实际场景里地表不均匀、气溶胶类型不固定、观测几何随时变化物理模型很难把每个因子都精确参数化。深度学习在这里走的是另一条路不解释每个物理过程直接学一个从TOA多波段反射率到地面PM2.5浓度的非线性映射。这份资源选TOA而不是地表反射率一个重要原因是省事且稳定。地表反射率需要做大气校正校正过程本身就引入大量中间误差而TOA反射率是卫星的原始观测值误差可控。代价是你得让模型自己去消化大气校正环节本来要处理的那部分非线性关系所以后续网络结构不能太浅。我一般会建议先把这套流程跑通再考虑用L2级产品替换L1B输入。跑通的标准是训练集R²能稳定在0.7以上验证集R²不低于0.5这样答辩时才有底气说“模型学到了气溶胶对TOA信号的影响”。2.2 数据源选型MODIS L1B与站点PM2.5怎么配对数据准备是整个项目的第一步也是最容易让新手懵圈的地方。常见做法是用MODIS的MOD021KM L1B产品1KM分辨率每个HDF文件里自带多个反射率波段和观测几何信息。地面PM2.5数据一般来自中国环境监测总站的站点观测或者导师直接给一份Excel字段通常包含站点编号、经纬度、时间、PM2.5浓度。选1KM分辨率不是随便定的因为一个地面站点代表的本来就是周边一定范围内的浓度水平1KM像元大小跟站点的空间代表性大致匹配。如果分辨率太高像元噪声大会导致模型过拟合太低站点信息被平均掉反演结果没有空间细节。数据配对的关键在时间对齐。MODIS是上午过境站点浓度也是小时级记录我习惯取卫星过境时刻前后1小时的浓度均值作为标签这样能明显降低时间错配带来的噪声。配对结果就是一份表每行一个样本包含站点ID、经纬度、TOA各波段反射率、观测几何参数、PM2.5浓度。2.3 Extraction_PM_V3.py把站点观测清洗成标签这个脚本负责从原始站点表格里切出模型要用的标签列。下边是它的核心处理逻辑import pandas as pd import numpy as np df pd.read_csv(station_pm25.csv, parse_dates[time]) df df[[station_id, lat, lon, time, pm25]] # 删除浓度缺失的样本 df df.dropna(subset[pm25]) # 过滤异常值PM2.5浓度0以下和500以上基本是传感器漂移 df df[(df[pm25] 0) (df[pm25] 500)] # 取卫星过境前后1小时的平均值平滑短时波动 df[time_key] df[time].dt.floor(h) df df.groupby([station_id, lat, lon, time_key]).agg( pm25_mean(pm25, mean), pm25_std(pm25, std) ).reset_index() df.to_csv(pm25_label.csv, indexFalse)这里有两个参数值得细说。pm25 0的判断用的是严格大于零而不是大于等于零主要是把负的仪器噪声值排除掉上限500的依据是绝大多数城市站点的观测上限超出这个值基本能断定仪器故障硬留着会让深度学习模型为拟合这根尾巴浪费大量容量。time_key的floor操作把分钟级数据归一到小时级groupby再取均值相当于给了标签一个平滑窗口。这一步做得好不好直接决定后面所有训练结果。我见过不止一个人在这个环节偷懒不过滤异常值最后模型R²看起来还行但反演图上一片一片的伪高值答辩时被老师一问就露馅。2.4 Data_Extraction_index.py经纬度换算行列索引站点坐标要映射到MODIS影像的像元上这一步是数据流水线里最容易翻车的地方。MODIS L1B用的是正弦投影网格全球范围行和列的基准偏移值都是固定的。不同分辨率产品在一个HDF片内的行列数不一样1KM产品是1200行乘1200列。理论上有投影公式可以直接反算行列号我一般会先用MOD03自带的经纬度层做最近像元匹配精度更高。如果手上只有坐标信息和影像文件用正弦投影换算也能接受。下边是换算的核心代码from pyproj import Proj # MODIS正弦投影地球半径使用MODIS标准值 sinu Proj(projsinu R6371007.181 unitsm no_defs) x, y sinu(lon, lat) # 全球网格基准偏移正弦投影下X方向半个地球周长 offset_x 11119505.0 offset_y 11119505.0 cell 926.6254 # 1KM产品单像元尺寸单位米 row int((offset_y - y) / cell) col int((x offset_x) / cell)注意两点。第一row的计算用了offset_y - y因为正弦投影的Y轴原点在赤道北半球y值为正而影像行号是从上往下排的所以要翻转。第二int()直接截断换算结果会有最多一个像元的误差这对训练数据来说影响有限因为后续取像元值时还会做邻域平均来缓冲。如果你发现站点经纬度跟影像上明显对应不上先查投影参数是不是R6371007.181这个值跟通用WGS84椭球半径不一样差一点就会差出几百米。还有个容易忽略的点脚本里如果同时存在多个HDF分片要按文件名里的h、v编号先算出该片左上角的全球行列号然后再加片内行列号这套换算可以拿站点落在哪个片来验证。2.5 Data_Extaction_Value_V4.py按索引抠TOA像元值并拼特征文件名叫Data_Extaction_Value_V4.py注意Extaction少了个r这是资源包里的原始文件名运行时保持原样不用改。这个脚本做的事情是拿着2.4节算出的行列索引打开MOD021KM对应的波段把像元值抠出来拼成特征矩阵。from osgeo import gdal import numpy as np hdf_path MOD021KM.A2020001.h26v04.061.2020001123456.hdf gdal.UseExceptions() ds gdal.Open(hdf_path) # 读取波段子集这里示意取前7个反射率波段 selected_bands [1, 2, 3, 4, 5, 6, 7] patch_size 5 features [] for band_idx in selected_bands: band ds.GetRasterBand(band_idx) array band.ReadAsArray() # 取站点为中心的5x5邻域做均值缓冲几何定位误差 patch array[row-2:row3, col-2:col3] features.append(np.nanmean(patch)) # 附加观测几何参数太阳天顶角、观测天顶角、相对方位角 geometries ds.GetMetadata()[SUNZENITH] features.extend([solar_zenith, view_zenith, rel_azimuth])代码里真正决定效果的是patch_size这个参数。取5x5邻域平均而不是单像元值是因为行列换算有截断误差加上MODIS影像本身有定位抖动单像元取值会让模型学到的人为噪声变大。取均值会损失一点空间细节但换来的是特征稳定性对训练回归模型来说好处更大。np.nanmean是必须的云边像元经常是NaN普通mean会把整行特征变NaN网络直接报错。拼好特征后脚本会把所有样本写到一份csv里每行一个站点前7列是TOA反射率接着3列是几何参数最后一列是PM2.5标签。这份csv就是深度学习脚本的输入。写到这里要提醒一句有的同学会把高反射率像元比如云顶也当成有效值直接丢进训练这个坑我会在第5章专门讲。3. Deep.py训练主脚本网络结构、归一化与超参数收敛3.1 选MLP还是CNN先看你的样本组织形式打开Deep_learning目录下的Deep.py你首先要判断它用的是哪种网络组织方式。按这份资源的文件结构推断核心是逐站点样本也就是一行一个站点的特征向量这种形式用多层感知机MLP最直接输入是10维左右的向量输出是1维浓度值。每个样本之间相互独立没有天然的网格邻域结构硬塞CNN反而别扭。如果你想把像元邻域的空间信息也用上那就得把输入改成以站点为中心的影像块比如16x16x7的三维张量网络换成卷积层加全连接层。但代价是数据量暴涨原来一个站点一条样本现在一个站点要抠一个块训练时间翻几倍。我自己的习惯是先跑通MLP拿到基准精度如果导师要求“考虑空间相关性”再升级成CNN这样对比实验也好做。Deep.py大概率就是Keras或PyTorch的MLP实现。不管底层是哪个框架网络骨架和训练流程都逃不出下边这套写法import tensorflow as tf from tensorflow.keras.layers import Dense, Dropout model tf.keras.Sequential([ Dense(128, activationrelu, input_shape(10,)), Dropout(0.2), Dense(64, activationrelu), Dropout(0.1), Dense(32, activationrelu), Dense(1) # 回归任务输出层不加激活函数 ]) model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), lossmse, metrics[mae] )几个设计细节说一下。输入层设成10维对应前7个反射率波段加3个几何参数如果你在数据准备阶段加了额外的特征这个维度要同步改。Dropout放在128和64层后面作用是在训练时随机丢弃一部分神经元防止模型把站点噪声背下来0.2和0.1的区别是因为越靠近输出的层容量越小需要的正则力度也越小。输出层不加激活函数这是回归任务的基本要求加了ReLU会把预测值截断在非负区间但浓度值理论上限不固定反而限制表达。3.2 归一化的正确姿势scaler只能在训练集上fit训练脚本里最容易做错的一件事是标准化器的数据泄漏。有些同学图省事把全部样本读进来后直接scaler.fit_transform再切训练验证集。这个做法在数据量小的情况下会让验证集结果虚高因为scaler已经见过了验证集的均值和方差相当于验证信息提前泄漏进训练流程。正确做法是先切分再fitfrom sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split # data: n_samples x n_features, label: n_samples X_train, X_val, y_train, y_val train_test_split( data, label, test_size0.2, random_state42 ) scaler StandardScaler() X_train scaler.fit_transform(X_train) X_val scaler.transform(X_val) # 用训练集的统计量不重新fit # 保存scaler反演整景影像时要用同一组参数做变换 import joblib joblib.dump(scaler, scaler.pkl)train_test_split这里的random_state固定成42保证每次运行划分一致答辩复现时结果对得上。test_size0.2是一个比较通用的起点站点数量多可以降到0.15少了就提高到0.3原则是验证集至少要保留20到30个站点样本不然评估指标的方差太大。scaler.transform(X_val)这行是关键它用的均值方差完全来自训练集。反演影像时也一样整景影像的反射率分布跟训练集肯定不同但还是要用这个scaler来变换保证特征空间一致。3.3 训练参数与早停怎么看收敛训练参数直接决定模型质量。下面这张表是我调这类TOA回归模型时常用的起点也是Deep.py这类脚本里常见的配置参数建议值调整依据learning_rate0.001太大loss震荡太小收敛慢batch_size32样本量小32比64稳定epochs150配合早停上限设大点无妨early_stopping_patience20验证loss连续20轮不降就停验证集比例0.2按站点数量微调早停是必须写的不然150轮跑完后期90轮都在过拟合from tensorflow.keras.callbacks import EarlyStopping early_stop EarlyStopping( monitorval_loss, patience20, restore_best_weightsTrue, verbose1 ) history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs150, batch_size32, callbacks[early_stop], verbose1 )restore_best_weightsTrue这个参数很多人忽略。它会在训练结束后把模型权重回滚到验证loss最低的那个epoch而不是用最后一轮epoch的权重。加上它之后你不必手工去挑历史权重文件训练完直接拿去用就行。看loss曲线有个经验训练loss持续降但验证loss在第20轮左右开始回升是典型过拟合调大Dropout或减小网络反过来训练loss和验证loss都不降先查特征是不是做了归一化再看标签里有没有异常值两者都正常还不能收敛把learning_rate降到0.0001再试一轮。3.4 输入特征还可以怎么扩如果MLP在验证集上精度上不去先别急着换模型考虑加特征。常见拓展方向有三个一是试探性地加入MOD04的气溶胶光学厚度AOD产品作为额外输入虽然这有点“用官方产品辅助反演”的意思但作为对照实验很有说服力二是加时间特征比如月份、小时让模型学到气溶胶的季节性规律三是加地表类型特征比如站点周边2km内的土地利用比例这能帮助模型区分地表反射率的差异。每加一组特征都要重跑一次基线对比记录验证集R²和RMSE的变化这样论文里能写清楚“逐特征组合实验”。4. TOA反演PM2.5.ipynb主流程从模型到精度图和反演图4.1 Notebook为什么适合做毕设主流程这个资源里最核心的演示文件是TOA反演PM2.5.ipynb。用Jupyter Notebook而不是纯Python脚本对毕设答辩特别友好每个cell可以独立运行导师问到“这步在做什么”你当场从头跑一个cell展示中间变量就行。Notebook的组织一般分成三大块数据清洗与合并、模型训练与精度评估、整景反演出图。我在跑这类Notebook时习惯先把Cell Run All跑通一遍确认无报错再回到每个cell逐段看输出。如果某个cell输出体积特别大比如打印了整张表记得改成只打印前几行不然答辩演示时切到输出界面会很尴尬。4.2 用训练好的模型反演整景MODIS影像训练收敛后最有说服力的成果是一张整景PM2.5空间分布图。反演整景影像跟预测站点样本有个不同点整景几百行乘几百列像元不能一次性全塞进模型要按块推理。我一般写这样的循环from osgeo import gdal import numpy as np import joblib model tf.keras.models.load_model(pm25_model.h5) scaler joblib.load(scaler.pkl) ds gdal.Open(MOD021KM.A2020001.h26v04.061.2020001123456.hdf) rows, cols ds.GetRasterBand(1).ReadAsArray().shape block_size 64 output np.full((rows, cols), np.nan) for i in range(0, rows - block_size 1, block_size): for j in range(0, cols - block_size 1, block_size): block np.stack([ ds.GetRasterBand(b).ReadAsArray(j, i, block_size, block_size) for b in [1, 2, 3, 4, 5, 6, 7] ], axis-1).reshape(-1, 7) block_s scaler.transform(block) pred model.predict(block_s, verbose0).reshape(block_size, block_size) output[i:iblock_size, j:jblock_size] predblock_size64是实践里比较稳的选择。块太大内存占用高遇到云覆盖区域NaN直接整块污染块太小Python循环次数多prediction耗时指数上升。循环里ReadAsArray(j, i, ...)的参数顺序是列起始、行起始、列数、行数别跟numpy的索引方向搞混这个顺序错一次就全图错位。推理完成后保存成GeoTIFF这步需要把地理参考信息写进文件driver gdal.GetDriverByName(GTiff) out_ds driver.Create(pm25_inversion.tif, cols, rows, 1, gdal.GDT_Float32) out_ds.SetGeoTransform(ds.GetGeoTransform()) out_ds.SetProjection(ds.GetProjection()) out_band out_ds.GetRasterBand(1) out_band.WriteArray(output) out_band.FlushCache() out_ds NoneSetGeoTransform和SetProjection直接从原始HDF里拷过来保证反演图跟原影像坐标一致。如果你后续要在ArcGIS或QGIS里叠加站点或行政边界这一步必须做对否则图是歪的答辩时被问到坐标系统就麻烦了。4.3 精度评估表R²、RMSE、MAE怎么算才像论文反演模型的精度评估通常看三个指标公式分别是指标公式说明R²1 - SS_res / SS_tot模型解释的方差比例0.7以上算可用RMSEsqrt(mean((y_true - y_pred)²))误差的绝对量级单位μg/m³MAEmean(abs(y_true - y_pred))对异常值更稳健代码实现很短用scikit-learn三行搞定from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error y_pred model.predict(X_val).flatten() r2 r2_score(y_val, y_pred) rmse np.sqrt(mean_squared_error(y_val, y_pred)) mae mean_absolute_error(y_val, y_pred) print(fR2{r2:.3f}, RMSE{rmse:.2f} ug/m3, MAE{mae:.2f} ug/m3)论文里表格一般还会加上与已有研究的对比行例如“本研究R²0.78优于XXX的0.72”这类对比留到写论文时再补。要注意的是验证集的划分方式如果按站点随机切分R²通常偏高如果按时间切分用前11个月训练、最后1个月验证R²会低不少但更接近真实应用场景。两种都跑一遍放在论文里反而是加分项。4.4 论文级出图散点密度图和时间序列最后的出图质量直接影响答辩印象。散点图我建议用hist2d画密度散点而不是普通scatter因为几百个站点样本堆在一起时普通散点图黑糊糊一片看不出样本密度分布import matplotlib.pyplot as plt plt.figure(figsize(6, 6)) plt.hist2d(y_val, y_pred, bins40, cmapviridis) plt.plot([0, 150], [0, 150], r--, linewidth1.5) plt.xlabel(Observed PM2.5 (ug/m3)) plt.ylabel(Predicted PM2.5 (ug/m3)) plt.colorbar(labelSample count) plt.savefig(val_density.png, dpi200, bbox_inchestight)bins40控制散点图的分箱密度样本多可以加到60少就减到30。红虚线是1:1参考线点越贴近它说明预测越准。dpi200是投稿级的清晰度用普通屏幕截图放在论文里会被压缩得发虚。时间序列图要按天聚合把预测均值和实测均值画两条折线。出图之后一定要肉眼看一遍如果某几个时间点预测值突然飞到300大概率是那些天的数据里有云污染像元参与了建模这就是下一章要讲的核心坑。5. 反演实验避坑预处理、维度对齐与评估里的五个翻车点5.1 特征没归一化loss从一开始就下不去现象训练集上loss从第一个epoch开始就异常大比如MSE在10⁵量级而且训练几十轮都不见明显下降。原因TOA反射率和PM2.5浓度虽然都在一个量级附近但太阳天顶角是0到90的数值相对方位角是0到360这些特征直接拼进去之后量纲差异导致梯度更新被数值大的维度主导网络很难学到平衡的权重。解决把所有连续特征统一做StandardScaler标准化。注意下采样率差异大的特征比如反射率值域是0到1角度特征值域是0到360靠标准化统一到零均值单位方差之后模型才能一视同仁地对待每个维度。我每次跑新数据都会先打印一下X_train.std(axis0)如果不全接近1scaler一定没接对。5.2 随机划分验证集R²虚高到0.9以上现象验证集R²高达0.9但反演到整景影像上空间分布看起来异常平滑甚至城市和农村没有区分度。原因随机划分验证集时同一个站点不同日期的样本同时出现在训练集和验证集。站点本身的观测偏差比如站点位置离污染源很近被模型当作有效信号学到了验证时又拿同一批站点的其他日期样本去测等于开卷考试。解决按站点ID划分子集保证一个站点的所有样本只在训练集或者只在验证集中stations data[station_id].unique() train_stations, val_stations train_test_split(stations, test_size0.2, random_state42) train_mask data[station_id].isin(train_stations) val_mask data[station_id].isin(val_stations) X_train, X_val data[train_mask], data[val_mask]这个坑在毕设答辩里杀伤力很大因为老师一旦问“训练和验证是不是按站点分开的”回答不上来前面的精度全部存疑。5.3 索引脚本与值提取脚本不一致站点位置错位现象反演图上某个站点位置的预测值跟附近地表类型完全不匹配比如农田中间出现一个超高值孤点。原因Data_Extraction_index.py算出的行列号跟Data_Extaction_Value_V4.py里读取影像时的行列顺序不一致最常见的问题是行列号先在HDF片内计算后来又拿片内索引去另一个分片文件里取值。解决在索引脚本末尾自检一个已知点。取一个明显的地标比如大型湖泊的中心经纬度换算成行列号后用gdal读取该位置的反射率值应该跟已知地表反射率大致相符。我习惯在流水线里让两个脚本共用同一个行列换算函数而不是各写一套。5.4 云污染像元直接参与训练模型学到伪高值现象验证集R²不错但反演图上云覆盖区域出现一片片高浓度假象且高值区边缘锐利不像真实气溶胶分布。原因L1B反射率没有大气校正云和厚气溶胶都会造成高反射率。模型只看到“高反射率→高浓度”的相关性就把云的强反射当成了PM2.5浓度高的信号。解决用MODIS官方QA波段或者用简单的红外波段阈值法先做云检测。阈值法常用做法是判断1.24μm附近的短波红外反射率超过0.2就标为云更稳的是用MOD35云掩膜产品。处理方式是在提取像元值时把这些像元的特征设为NaN让np.nanmean自动忽略cloud_mask swir 0.2 # 或读取QA波段 features np.where(cloud_mask, np.nan, features)云检测阈值要根据季节微调冬季地表反照率高阈值要相应调高否则会把地面误判成云导致训练样本量锐减。5.5 卫星过境时刻与站点浓度时间不对齐现象时间序列图上预测值整体滞后或提前于实测值相关系数高但存在系统性偏差。原因MODIS上午过境时间是当地10点半左右而站点数据如果是日均值包含的是一整天的污染过程和瞬间的卫星观测天然不一致。模型被迫用一个时刻的TOA信号去拟合24小时均值自然学不准。解决标签只用卫星过境前后1小时内的站点浓度均值。如果站点数据只有日均值可以保守一点只保留过境时刻前后各半小时的样本。下采样到小时数据的代码在2.3节已经写过关键是匹配时不要用天做key用小时。6. 进阶技巧把单场景反演扩成多月份时空制图6.1 批量处理多景影像第2到第4章跑通的是单景反演但导师大概率会要求“看看这个季节的时空分布”。批量处理的第一步是把所有MOD021KM文件按日期整理成清单用glob扫一下import glob hdf_files sorted(glob.glob(MOD021KM*.hdf))批量跑之前务必写一个文件清单校验脚本把所有HDF的过境日期、片号打印出来对比确认覆盖的时间范围和空间范围是你想要的。这一步是我吃过亏才养成的习惯后面会提到。6.2 跨时间验证用季节切分替代随机切分多月份数据的验证要换一种思路。单日数据按站点切分多个月份数据则应该按时间切分比如用1到9月训练10到12月验证。这种验证方式考察的是模型的时序泛化能力比随机切分更有说服力。整理成月度数据后可以按季节统计预测误差看模型在夏季高湿和冬季高污染条件下的表现差异这部分分析写进论文能明显提升工作量饱满度。6.3 与官方AOD产品交叉对比拿反演结果和MODIS官方AOD产品做相关性分析是检验模型是否学到真实物理信号的硬核手段。具体做法是在同一站点位置上提取MOD04的气溶胶光学厚度跟模型预测的PM2.5浓度做Pearson相关计算。因为AOD本身就是气溶胶柱浓度的光学表征理论上与地面PM2.5正相关如果你的反演结果和AOD相关性很低那说明模型学到的主要是地表反射率干扰。我也经常把这个相关矩阵直接放进答辩PPT回应“你的模型黑匣子会不会是乱学”这种问题。从实践经验看反演结果与AOD相关系数在0.3到0.5之间算正常超过0.6说明模型已经学到部分气溶胶垂直分布信息属于惊喜。到这里整套资源就完整拆完了。说句实在话我第一次用这套源码做多月份批量反演时因为临时换了一批HDF数据没有重跑站点清洗脚本结果前三个月的反演图上城市站点周围全是异常高值排查了两天才发现是新的站点表格里混入了几行错误经纬度。从那以后我每次跑新的数据集都会强制走一遍“先画站点分布图、再画特征均值图、最后才训练”的三步检查流程。希望这个习惯也能帮到你少走一次弯路。本文还有配套的精品资源点击获取
返回列表