ARTICLE DETAIL

资讯详情

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

基于LSTM的溶解氧时间序列预测:从数据清洗到部署

基于LSTM的溶解氧时间序列预测:从数据清洗到部署 简介面向计算机相关专业课程设计、期末大作业及项目实战练习这份Python项目围绕水体溶解氧浓度预测任务完整实现基于深度学习的时序建模流程。资源涵盖数据预处理、EMD与EEMD信号分解、LSTM基础模型以及EEMD-LSTM、EEMD-BP等组合模型并附DBSCAN、OneClassSVM、LOF、孤立森林等异常检测脚本可支撑从数据清洗、特征分解到多模型对比的整套实验。压缩包共16个文件含11个Python脚本与5个CSV水质数据集整体体积仅931KB轻量且便于按模块学习脚本按预处理、分解算法、模型训练和检测模块组织数据文件提供多组真实水质记录方便快速切换验证。项目经严格调试下载即可直接运行能省去环境配置与数据整理的时间。目前已有227人学习适合需要完整时间序列预测参考实现或希望在此基础上开展算法改进、毕设扩展与论文实验的读者。1. 溶解氧时间序列预测从传感器告警到提前几小时的预判凌晨三点被短信吵醒打开手机看到溶解氧掉到 2.5 mg/L人赶到塘边已经是半小时后。这是很多水产养殖和中小型水厂运维人员的日常。溶解氧DODissolved Oxygen是水质指标里变化最剧烈、也最致命的一项一场暴雨、一次闷热天气或者夜间的呼吸耗氧高峰都可能让它在几小时内跌破阈值。问题在于实时报警只是告诉你已经出事了而基于深度学习的溶解氧时间序列预测是想用 Python 配合回归类模型根据过去的气象、水温、pH、浊度和溶氧历史把未来 1 到 6 小时的 DO 曲线提前算出来。这件事做的不是看到故障而是预判故障能多争取出启动增氧机、调整曝气量的操作窗口。这个方向适用于经历过数据采集痛点的从业者塘口、水源地、污水处理厂进水站手里已经攒了几个月甚至几年的监测记录想用深度学习模型把这些数据变成有业务价值的预判结果。标题里的源码全部数据意味着你拿到的是一套可以直接被替换数据、修改特征、重新训练的完整项目而不是只有核心代码、缺数据少配置的 Demo。本文按数据准备 → 模型选型 → 模型实现 → 坑位避让 → 部署落地来拆这套方案。2. 数据决定上限水质时序数据的预处理与特征设计老话说垃圾进垃圾出在时间序列预测里体现得比图像和文本更残暴。图像数据模糊一点至少还认得轮廓时序数据如果采样频率没对齐、毛刺没清掉、特征里混进了未来信息模型在训练集上再漂亮拿到现场跑也是一塌糊涂。溶解氧预测是典型的多变量时间序列问题数据里不仅有溶氧本身还有它背后的物理上下文。数据阶段要回答三个问题用多细的时间粒度预测、历史窗口滑多长、哪些特征值得进模型。哪怕刚把 Python 环境装好先过这一关后面的训练才有意义否则只是把垃圾数据喂给一个昂贵的模型。2.1 采样频率与滑窗长度怎么定常见在线水质监测设备的原始采样频率有 5 分钟、15 分钟和 1 小时三档。5 分钟一个点一天能攒 288 条记录听起来丰富但实际上不少溶解氧探头本身的响应时间就有几十秒到几分钟相邻点之间的变化大多被噪声淹没高频数据带来的更多是毛刺而非信息。我一般先把原始数据重采样成 1 小时间隔如果监测站本身是 15 分钟传一次也会保留原始间隔但不会再去插密成 5 分钟。频率太低更麻烦——一天一个点的话昼夜周期信息完全丢失深度学习再强也没有发挥空间。滑窗长度window指模型回看多少个小时的历史。对溶解氧来说24 小时窗口意味着模型至少能观察到一个完整昼夜循环这是一个合理的起点如果水体大、惯性强可以拉到 48 小时。预测步长horizon取未来 6 小时既能服务于增氧机提前开启又不至于超出模型在当前数据量下的可靠预测范围。窗口与步长这两个参数不是拍脑袋定的后续要围绕它们做消融实验固定其他条件依次把窗口从 24 调到 48 再调到 72观察验证集 RMSE 的变化哪个配置稳定好用哪个。2.2 清洗顺序先去毛刺再插缺失值溶解氧数据的清洗顺序和金融时序不太一样不能一上来就插值。传感器在探头结垢、水流中断、设备反冲洗时会出现典型的毛刺某一两个点从 6 mg/L 突然掉到 0.3 mg/L下一条记录又恢复正常。如果先做插值程序会把这个突变当成真实信号平滑出一个假凹陷直接影响后续特征计算。正确顺序是先用统计方法把毛刺识别出来置空再用插值补齐。毛刺检测常用滚动窗口均值加减 3 倍标准差。这里有一个特别容易被忽略的坑如果用 centerTrue 的滚动窗口当前时刻的统计量里其实包含了未来十几个小时的数据。仅用于清洗阶段问题不大但如果你想把这个滚动均值作为特征喂给模型就必须用 shift() 把它变成滞后的统计量否则模型训练时偷看了未来验证集上好看上线之后立刻原形毕露。下面是一个可以直接套用的清洗片段import pandas as pd import numpy as np df pd.read_csv(data/do_hourly.csv, parse_dates[datetime], index_coldatetime) df df.sort_index().resample(1h).mean() # 毛刺检测以当前点前后各12小时为窗口计算均值和标准差 window 24 roll_mean df[do].rolling(window, centerTrue, min_periods6).mean() roll_std df[do].rolling(window, centerTrue, min_periods6).std() outlier_mask (df[do] - roll_mean).abs() 3 * roll_std # 先把毛刺置空再做线性插值 df.loc[outlier_mask, do] np.nan df[do] df[do].interpolate(methodlinear, limit_directionboth)逻辑说明读入数据后按小时对齐确保索引唯一且时间连续rolling 窗口设为 24作用是让均值和标准差对短期波动不那么敏感min_periods6 保证窗口边缘也有足够样本参与计算。被置空的毛刺点比重正常应低于 2%如果超过这个数说明 3σ 的阈值在这套数据上太敏感可以把阈值放宽到 4σ。interpolate 用线性插值补缺失连续缺失超过 6 个小时的片段建议直接裁掉长段插值产生的平滑信号对模型是误导。另外如果整个传感器的量程切换过或者换过探头数据会呈现明显的均值偏移。这种分段突变插值解决不了要在处理前先画出整条时间曲线找到偏移点按段分别归一化。这一步肉眼观察比任何算法都可靠。2.3 特征设计物理量、周期量、滞后统计量特征设计三层搭。第一层是物理上直接关联溶解氧的变量水温与饱和溶氧呈明显负相关20°C 和 30°C 下饱和浓度能差出 2 mg/L 以上pH 与藻类光合作用相关白天二氧化碳被消耗pH 上升溶氧同步变化电导率和盐度影响气体溶解度浊度反映水体和光照穿透情况。做特征工程时这几个变量只要监测站里有尽量都放进特征矩阵。第二层是把时间结构编码成模型能直接理解的特征。小时数不要用原始整数 0 到 23因为 23 点和 0 点在距离上只隔一小时但数值上相距 23直接传入模型会造成错误的距离概念。常见做法是用周期编码sin(2πh/24) 和 cos(2πh/24) 两个分量表达小时周期同理可对月份做 sin/cos 编码表达季节。第三层是滞后特征与滚动统计量。LSTM 本身能隐式学习滞后关系但滚动统计量仍值得手动加过去 24 小时溶氧的均值、极差、趋势斜率相当于给模型提供当前是一条缓变曲线还是在急速下滑的浓缩摘要。我一般控制在 4 个滚动特征以内不要把所有窗口统计量全部塞进去。特征太多训练时间成倍增长更容易在有限样本上过拟合。3. 模型选型LSTM 是默认解GRU 和 Transformer 何时上位很多做水质预测的人一上来就直奔 LSTM不是说不对而是得知道为什么是它。先把传统机器学习模型拿出来对比一下支持向量回归、随机森林、LightGBM 这类模型把滞后特征拼好后去拟合溶解氧短时间内就能得到不错的精度训练快、调参直接。但它们的短板在于多步预测能力弱。要预测未来 6 小时传统模型要么把 6 个目标时刻当成多输出回归要么用递归方式让预测值重新当输入后者误差逐级累积很容易越往后越偏。而深度学习模型是按序列输入、序列输出设计的天然适配用一段历史预测一段未来的结构。3.1 LSTM、GRU、Transformer 在溶解氧数据上的差异LSTM 是这类项目里最稳的默认选择。它的门控机制能记住昨天这个时候溶氧是多少这类长周期特征同时有选择地丢掉无关信息。溶解氧数据有两个特点让 LSTM 特别适合一是昼夜节律稳定24 小时自相关很强二是受天气、水温影响的部分变化缓慢LSTM 的记忆单元能持续跟踪这种慢变状态不像前馈网络那样只看到当前输入。GRU 可以理解为 LSTM 的瘦身版只有两个门更新门和重置门参数量约为 LSTM 的四分之三。在训练样本不足 3 万条时GRU 往往比 LSTM 收敛更稳、更不容易过拟合训练速度也更快。如果数据量不大、又想要一个基线模型我一般先用 GRU 跑通再换 LSTM 对比。Transformer 在时间序列预测上的表现不完全取决于模型本身而取决于数据量。它擅长捕捉长距离依赖但需要大量样本去学习位置编码和注意力权重的合理配置。在我接触过的水质时序场景里样本量通常在 1 万到 10 万条之间这个量级下 Transformer 往往打不过调好的 LSTM还会因为学习率、位置编码等超参数的选择耗费大量时间调优。一句话总结数据量不够大时Transformer 不是不好是喂不饱。3.2 按数据量选模型别让模型复杂度跑在数据前面模型复杂度应当与数据量匹配这是深度学习实战项目里很核心的一条经验。我常用的选型分界线大致如下样本量在 1 万条以内用单层 GRU 或 hidden_size64 的小 LSTM1 万到 10 万条用两层 LSTM、hidden_size 在 64 到 128 之间这是大多数水质监测项目的区间超过 10 万条且序列有明显长周期结构时才考虑 CNN-LSTM 或 Transformer 路线。样本量不是只看总记录数而是看有效样本数即做完去重、清洗、去缺失之后的真实数据量。很多传感器数据看起来有几十万条清洗完可能只留下 60%再按 7:2:1 划分训练集就更小了。在小数据上强行堆大模型验证集的表现会出现很高的方差训练十次能有八种结果这是典型的过拟合信号而不是模型不够好。3.3 多步预测直接多步、递归多步与 Seq2Seq预测未来 6 小时溶氧有几种实现方式。直接多步multi-output就是让模型一次性输出未来 6 个时间点的值实现最简单误差不累积对预警场景来说足够。递归多步是把预测出来的第一个值拼回输入序列再预测下一步这个方法的问题是误差会像滚雪球一样越来越大预测到第 5、第 6 步时往往偏得离谱不推荐在溶氧这种波动大的指标上作为主方案。Seq2Seq 结构编码器解码器配合可以灵活输出不定长序列理论上效果最好但训练复杂度高判断收益有限。我一般第一版都用直接多步预测把 6 个输出节点当作回归问题来训练跑通之后再根据业务需求决定要不要上更复杂的结构。4. 用 PyTorch 把 LSTM 跑通数据处理、训练循环与验证指标进入实现环节。环境方面网上 Python 安装教程很多项目里我固定使用 Python 3.10 配 PyTorch 2.x不需要追最新版本。代码部分按照真实项目里通用的完整流程来拆解每一步都有对应数据结构和参数说明。数据以小时级 CSV 为基础包含 datetime 列和若干水质特征列。4.1 数据读取、归一化与时间顺序划分时间序列划分有一条底线不能随机打乱必须按时间先后切分。随机打乱会让训练集里混进时间上更晚的数据模型在验证和测试时相当于开卷考试。常见划分比例是 7:2:1即前 70% 训练、中间 20% 验证、最后 10% 测试测试数据在训练过程中完全不可见只在最终评估时使用一次。归一化用 MinMaxScaler但有一个细节经常被忽略scaler 必须只在训练集上 fit再用训练集的参数去 transform 验证集和测试集。如果在全部数据上先归一化再划分验证集和测试集的统计信息已经泄露进训练过程了。import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler df pd.read_csv(data/do_hourly.csv, parse_dates[datetime], index_coldatetime) df df.sort_index().resample(1h).mean().interpolate(limit_directionboth) feature_cols [temp, ph, conductivity, turbidity] target_col do train_size int(len(df) * 0.7) val_size int(len(df) * 0.2) train_df df.iloc[:train_size] val_df df.iloc[train_size:train_size val_size] test_df df.iloc[train_size val_size:] feature_scaler MinMaxScaler() target_scaler MinMaxScaler() feature_scaler.fit(train_df[feature_cols]) target_scaler.fit(train_df[[target_col]]) X_train feature_scaler.transform(train_df[feature_cols]) Y_train target_scaler.transform(train_df[[target_col]]) X_val feature_scaler.transform(val_df[feature_cols]) Y_val target_scaler.transform(val_df[[target_col]])逻辑说明target 单独用一个 scaler 是为后续反归一化做准备预测结果是基于缩放后的空间计算出来的要还原成 mg/L 才能和业务阈值对比。单独对 target 做 scaler反变换时不需要拼回所有特征列避免出错。4.2 滑窗样本构建与 DataLoader滑窗是把时序数据转成监督学习样本的核心步骤。假如数据长度 10000 条、窗口 24、预测步长 6那么从第 0 到第 23 条取特征作为输入第 24 到第 29 条取溶氧值作为目标之后窗口整体向后滑动一位。这样做的结果是相邻样本之间存在大量重叠样本之间高度相关这是时序预测的正常状态不要尝试去打乱它们。import torch from torch.utils.data import Dataset, DataLoader def make_samples(X, Y, window24, horizon6): samples_x, samples_y [], [] for i in range(len(X) - window - horizon 1): samples_x.append(X[i:i window]) samples_y.append(Y[i window:i window horizon, 0]) return np.array(samples_x), np.array(samples_y) X_train_seq, Y_train_seq make_samples(X_train, Y_train) X_val_seq, Y_val_seq make_samples(X_val, Y_val) class DoDataset(Dataset): def __init__(self, x, y): self.x torch.tensor(x, dtypetorch.float32) self.y torch.tensor(y, dtypetorch.float32) def __len__(self): return len(self.x) def __getitem__(self, idx): return self.x[idx], self.y[idx] train_loader DataLoader(DoDataset(X_train_seq, Y_train_seq), batch_size64, shuffleFalse, drop_lastTrue)逻辑说明make_samples 生成的 X 形状为样本数, window, 特征数正好符合 PyTorch LSTM 输入的 batch_first 维度约定。DataLoader 里 shuffle 必须保持 False否则等于把时间顺序打乱破坏了序列依赖关系。如果数据量较大可以用 np.lib.stride_tricks 优化滑窗速度但数据量在 10 万条以内时上面的循环足够快。4.3 LSTM 模型定义与训练循环模型结构采用两层 LSTM 加两个全连接层。LSTM 的输出取最后一个时间步的隐藏状态再经过全连接层映射到 horizon 个输出节点分别代表未来 6 个小时的预测值。import torch.nn as nn class DOForecastLSTM(nn.Module): def __init__(self, n_features, hidden_size128, num_layers2, horizon6, dropout0.1): super().__init__() self.lstm nn.LSTM( input_sizen_features, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout if num_layers 1 else 0.0 ) self.fc nn.Sequential( nn.Linear(hidden_size, 64), nn.ReLU(), nn.Dropout(0.1), nn.Linear(64, horizon) ) def forward(self, x): out, _ self.lstm(x) last_hidden out[:, -1, :] return self.fc(last_hidden)训练循环中除了常规的反向传播还要做梯度裁剪。LSTM 在训练时容易出现梯度爆炸现象是 loss 突然变成 nan 或者跳高一个数量级。clip_grad_norm_ 把梯度范数限制在 1.0 以内是一个成本极低、效果明显的稳定性手段。下面对 LSTM 的关键参数说明一下hidden_size 决定记忆容量128 是水质项目中比较平衡的值数据小就降到 64num_layers2 对比单层能捕捉更复杂的时间特征但小于 1 万条样本时建议单层否则容易过拟合dropout 只在层数大于 1 时生效这是 PyTorch 的设定单层时设置 dropout 不会报错但也没有效果。device torch.device(cuda if torch.cuda.is_available() else cpu) model DOForecastLSTM(n_featureslen(feature_cols)).to(device) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, factor0.5, patience5) def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss 0.0 for xb, yb in loader: xb, yb xb.to(device), yb.to(device) optimizer.zero_grad() out model(xb) loss criterion(out, yb) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() total_loss loss.item() * xb.size(0) return total_loss / len(loader.dataset) best_val_loss float(inf) patience 10 wait 0 for epoch in range(50): train_loss train_one_epoch(model, train_loader, optimizer, criterion, device) model.eval() val_loss 0.0 with torch.no_grad(): for xb, yb in val_loader: xb, yb xb.to(device), yb.to(device) out model(xb) val_loss criterion(out, yb).item() * xb.size(0) val_loss / len(val_loader.dataset) scheduler.step(val_loss) if val_loss best_val_loss: best_val_loss val_loss torch.save(model.state_dict(), best_do_model.pt) wait 0 else: wait 1 if wait patience: break print(fepoch {epoch1:02d}, train loss {train_loss:.6f}, val loss {val_loss:.6f})代码说明MSE 作为损失函数时对离群点比较敏感如果数据清洗后仍有少量异常值可以换成 nn.HuberLoss它对大误差的惩罚是线性的不会让个别坏点主导梯度。关于早停patience10 的含义是验证集连续 10 个 epoch 没有改善就停止这样避免了无限训练浪费算力。模型权重只在验证集 loss 降低时保存一次这个保存时机很重要训练结束时的权重未必是验证集上最好的权重早停后的最后一个 epoch 经常已经过拟合了。4.4 验证指标RMSE、MAE 和最低值误差模型验证不能只看一个指标。RMSE均方根误差对特大误差敏感能反映出预测是否在某几个时间点严重偏离MAE 反映平均绝对误差更直观地表示平均偏差多少 mg/L。对溶氧这个业务场景两者都要算但还有一个指标比它们更贴近实际需求——未来 6 小时预测曲线的最低点误差。增氧机是否启动由最低点决定如果预测的最低点比实际低 0.5 mg/L那就要提前开增氧机如果预测最低点偏高可能错过启动时机。所以我在验证时会把预测序列每个窗口的最低值单独拿出来算一遍误差。def inverse_scale(pred, target_scaler): return target_scaler.inverse_transform(pred) y_pred inverse_scale(pred_np, target_scaler) y_true inverse_scale(true_np, target_scaler) rmse np.sqrt(np.mean((y_true - y_pred) ** 2)) mae np.mean(np.abs(y_true - y_pred)) pred_min_rmse np.sqrt(np.mean((y_true.min(axis1) - y_pred.min(axis1)) ** 2)) print(fRMSE: {rmse:.4f} mg/L, MAE: {mae:.4f} mg/L, min-value RMSE: {pred_min_rmse:.4f} mg/L)逻辑说明pred_np 和 true_np 的形状都是测试样本数, 6都是归一化后的数值先整体反归一化再计算指标。测试集的时间段是数据最后 10%模型从未见过这段时间的任何信息这个指标才是真实性能的估计。5. 溶解氧时间序列预测里的 5 个翻车坑现象、原因、解决本节写几个时间序列预测的典型翻车现场。这些坑我每个都踩过或看别人踩过写在这里按现象 → 原因 → 解决展开遇到同类型问题可以直接对号入座。5.1 归一化泄漏验证集漂亮部署现场就翻车现象训练和验证阶段误差都在 0.3 mg/L 以内模型看起来优秀模型部署后输出的预测曲线长期偏离真实值甚至稳定地整体偏低。原因代码在划分数据集之前就对全量数据做了归一化scaler 在 fit 时看到了测试集的均值和取值范围。MinMaxScaler 会把未来数据的统计信息带入缩放过程测试集的信息在训练阶段已经参与决定输入数值的大小这个叫归一化泄漏。解决严格按训练集 fit → 验证/测试集 transform的顺序执行这个顺序要写成代码注释防止后续维护时被改回去。5.2 随机打乱时间序列开卷考试式的假高分现象训练集上 RMSE 很低但把测试集按时间展开画图时预测曲线明显滞后于真实曲线峰值和谷值每次都晚一两个小时才被模型反映。原因是数据划分时用了 train_test_split 的默认参数shuffleTrue把时间打乱了。模型在训练时见过了未来的片段但它学到的并不是时间规律而是从相邻片段中查询答案的能力。解决严格按第 4 章的方法用 iloc 按索引顺序切分并且在 DataLoader 中保持 shuffleFalse。这里最容易出问题的反而是网上现成模板很多通用代码库默认带了 random split复制过来就直接用了。5.3 特征里混进未来值训练不报错上线才暴露现象训练和测试误差极低低到不真实但模型接上实时数据流后立刻崩溃。原因特征里混入了只能在未来获取的变量。典型的例子是把未来时刻的气温预报直接当作实时特征或者把第 t1 时刻的滚动均值通过 centerTrue 的窗口计算出来并当成第 t 时刻的输入。模型在训练阶段确实学到了真实关系但这些输入在线上根本拿不到或者拿到时已经是事后值。解决建特征时对每个滚动统计量做 shift(window) 检查确认当前特征在 t 时刻只使用了 t 时刻及之前的信息。还有一种检查方法把特征按时间画出来看是否在每个时间点上都有值如果有明显的前向偏移就要重新设计特征函数。5.4 突变天气与传感器漂移模型不是失灵是没见过现象平时 RMSE 稳定在 0.4 mg/L 左右遇到某场暴雨或者一次高温闷热天预测误差飙到 2 mg/L 以上接着连续好几天无法恢复正常。原因训练数据里这类极端天气事件出现得极少模型没有学习过对应的输入分布特征空间的连续性被打破。传感器本身也会漂移探头污染后读数整体下降但训练数据里读数是正常的。解决这类问题用算法很难根治。我在处理时会做两件事一是把历史极端天气事件单独标注出来在训练时加权让模型见过更多异常走向的样本二是做残差监测实时比较预测值和实测值的偏差如果偏差连续几个小时超过预设阈值触发模型进入不可信状态的标记此时回退到人工设定阈值报警。5.5 只盯 RMSE 一个指标业务上救不了急现象模型报告里 RMSE 0.3 mg/L看起来精度很高但夜间最低溶氧的预测总是偏差 0.8 mg/L 以上导致增氧机启动晚了。原因RMSE 是对所有时间点的平均夜间溶氧低、变化快的时间段在数量上不占优势被白天稳定段拉平了。这个均值掩盖问题在时间序列里很常见。解决验证阶段把预测结果按小时分组统计误差重点看凌晨 2 点到 6 点的分段误差。同时把每个窗口内预测最低值与实际最低值的偏差单独计算这比全局 RMSE 更能反映业务价值。如果发现夜间误差偏大可以考虑在损失函数里增加夜间时段的权重或者把昼夜标签作为特征传入模型。6. 部署到真实监测环境模型导出、预警阈值与增量更新最后一个环节把训练好的模型用起来。模型不能只保存在 jupyter notebook 里要能独立推理。我习惯用 TorchScript 导出模型LSTM 结构在 torch.jit.trace 下可以正常导出但要注意 trace 时需要提供一个固定的示例输入输入形状和训练时的 window、特征数保持一致。设备上部署时加载这个 scripted 文件不再依赖模型定义类代码环境迁移时更省心。model_cpu model.cpu().eval() example_input torch.randn(1, 24, len(feature_cols)) scripted torch.jit.trace(model_cpu, example_input) scripted.save(do_lstm_scripted.pt)部署后的预警逻辑不要只看平均误差而是盯预测曲线的最低点。当前实时数据送入模型得到未来 6 小时的溶氧预测曲线取 min 值和当前实测值对比连续两个时刻预测最低值都低于设定阈值比如 3.0 mg/L才触发报警或自动开启增氧机。这个做法比单点阈值报警提前量更大误报率也低一些。增量更新是部署后必须考虑的事。水质环境随季节变化模型的输入分布也会漂移一个在夏天训练好的模型到冬天可能性能下降。我习惯每 7 天把新增数据拼入训练集重新训练一轮或者用旧权重作为初始权重做微调学习率调低到 3e-4。如果平时没有重训条件至少要保留残差监测逻辑模型持续偏差过大时发出维护告警。我在这类项目里吃到最大的教训是第一版只保存了模型权重没保存 scaler。部署端加载完权重后输入数据没有归一化输出的预测值一直在错误量纲上排查了一整天才发现是漏了这个细节。自此之后我把 feature_scaler、target_scaler 和模型的版本号一起打包进配置目录另外每次重训还会把验证集预测曲线和上一版模型的输出画在同一张图上对比确认新模型没有在特定时段变差。这个习惯帮我避免过好几次指标变好但现场变差的返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表