wmy 574c973f3b 1:项目结构修正 5 meses atrás
..
README.md 574c973f3b 1:项目结构修正 5 meses atrás
args.py 574a5d969b 修正命名 5 meses atrás
data_preprocessor.py 574a5d969b 修正命名 5 meses atrás
data_trainer.py 574a5d969b 修正命名 5 meses atrás
gat_lstm.py 574a5d969b 修正命名 5 meses atrás
main.py 574a5d969b 修正命名 5 meses atrás
model.pth 574a5d969b 修正命名 5 meses atrás
predict.py 574a5d969b 修正命名 5 meses atrás
scaler.pkl 574a5d969b 修正命名 5 meses atrás

README.md

90天TMP预测模型训练逻辑说明

模型概述

这是一个用于预测反渗透(RO)系统未来90天压力差变化的长期趋势预测模型。与20分钟模型不同,这个模型关注的是长期的膜污染趋势。

预测目标:8个RO压力差指标

  • 4个一段压差:C.M.RO1-4_DB@DPT_1
  • 4个二段压差:C.M.RO1-4_DB@DPT_2

与20分钟模型的区别

特性 20分钟模型 90天模型
预测时长 20分钟(5步) 90天(2160步)
时间分辨率 4分钟/点 1小时/点
输入历史 4小时(60点) 180天(4320点)
预测目标 16个指标 8个指标
输入特征 79个 16个
应用场景 实时监控、短期调度 长期趋势、维护计划

核心思路

1. 为什么需要长期预测?

膜污染是一个缓慢累积的过程:

  • 短期(分钟级):压力波动主要由流量、温度变化引起
  • 长期(天级):压力持续上升是膜表面污染物积累导致

模型目标:预测未来90天内,每个RO膜组的压力差如何演变,从而:

  • 提前规划化学清洗(CEB)时间
  • 评估膜寿命
  • 优化运行参数

2. 超长输入窗口:4320个时间点

过去180天 → [LSTM处理] → 未来90天
(4320小时)              (2160小时)

为什么这么长?

  • 膜污染速率受季节、水质、运行策略影响
  • 需要足够长的历史才能捕捉这些缓慢变化
  • 类似"看过去半年天气,预测未来三个月天气"

3. 精简特征集:只用16个核心指标

不同于20分钟模型的79个特征,这里只用16个:

  • 4个RO进水流量:C.M.RO1-4_FT_JS@out
  • 2个RO系统指标:温度 C.M.RO_TT_ZJS@out、电导率 C.M.RO_Cond_ZJS@out
  • 8个当前压差:C.M.RO1-4_DB@DPT_1/2(输入也是输出,捕捉自相关)
  • 2个时间特征:年周期的正弦/余弦编码

为什么减少特征?

  • 长期趋势主要由流量、温度、当前压差决定
  • 太多特征在超长序列上会导致过拟合
  • 简化计算,减少内存占用

训练流程详解

步骤1:数据预处理(data_preprocessor.py

1.1 时间分辨率处理

resolution = 60  # 每60条原始数据取1条
chunk = all_data.iloc[::60, :]  # 从4秒一条变成4分钟一条

之后再在训练时按小时聚合

1.2 年周期时间编码

# 只编码年周期,不编码日周期(长期趋势不关心一天内变化)
day_year_sin = sin(2π × 天数 / 366)
day_year_cos = cos(2π × 天数 / 366)

1.3 滑动窗口策略

# 超长窗口需要特殊处理
window_time_span = 1小时 × (4320 + 314)  # 额外314小时buffer

# 训练样本生成:step_size=1(密集采样)
# 测试样本生成:step_size=2160(跳跃采样,节省计算)

为什么测试集跳跃采样?

  • 测试时不需要每个时间点都预测
  • 只需验证模型在不同起点的预测能力

步骤2:模型架构(gat_lstm.py

结构与20分钟模型完全相同

# 8个独立专家(对应8个压差指标)
class GAT_LSTM:
    def __init__(self):
        self.models = nn.ModuleList([SingleGATLSTM() for _ in range(8)])

每个专家内部

class SingleGATLSTM:
    # LSTM层
    self.lstm = nn.LSTM(
        input_size=16,      # 输入16个特征
        hidden_size=64,     # 隐藏层64单元
        num_layers=1,       # 单层LSTM
        batch_first=True
    )
    
    # 输出层
    self.final_linear = nn.Sequential(
        nn.Linear(64, 64),
        nn.LeakyReLU(0.01),
        nn.Dropout(0),
        nn.Linear(64, 2160)  # 输出2160步(90天)
    )

注意:虽然输入序列长达4320,但LSTM的隐藏层只有64维:

  • LSTM能压缩长时间序列的信息
  • 避免梯度消失(如果层数太多会有问题)

步骤3:训练策略(data_trainer.py

与20分钟模型相同的训练流程

for epoch in range(1000):
    # 前向传播
    outputs = model(inputs)  # [batch, 2160×8]
    
    # 计算MSE损失
    loss = MSELoss(outputs, targets)
    
    # 反向传播
    loss.backward()
    optimizer.step()
    
    # 验证
    val_loss = validate(val_loader)
    
    # 早停
    if val_loss没有改善超过500轮:
        break

特别注意的参数

batch_size = 128  # 比20分钟模型小(1024 vs 128)
                  # 因为每个样本更长,内存占用大

learning_rate = 0.01  # 学习率相同
scheduler_step = 100   # 每100轮衰减0.9

步骤4:评估与反归一化(data_trainer.py

多步预测的展平

# 模型输出:[样本数, 2160×8]
# 需要重塑为:[样本数, 2160步, 8个指标]
predictions = predictions.reshape(n_samples, 2160, 8)

# 再展平为:[样本数×2160, 8]用于反归一化
predictions = predictions.reshape(-1, 8)

反归一化

# 只反归一化最后8列(压差指标)
column_scaler = MinMaxScaler()
column_scaler.min_ = scaler.min_[-8:]
column_scaler.scale_ = scaler.scale_[-8:]
predictions = column_scaler.inverse_transform(predictions)

预测流程(predict.py

实时预测接口

class Predictor:
    # 参数设置
    seq_len = 4320        # 需要180天历史数据
    output_size = 2160    # 预测90天
    feature_num = 16      # 16个输入特征
    labels_num = 8        # 8个预测目标

预测步骤

  1. 历史数据准备

    • 需要连续180天的数据(4320小时)
    • 按小时分辨率采样
  2. 预测起始时间

    # 预测起点 = 最新数据时间 + 3小时
    test_start_date = max_date + timedelta(hours=3)
    
  3. 多重平滑处理

    # 对预测结果进行3层平滑(减少噪声)
    predictions = moving_average_smooth(predictions)
    predictions = exponential_smooth(predictions)
    predictions = savitzky_golay_smooth(predictions)  # 可选
    

为什么需要平滑?

  • 90天预测本身就是粗粒度的
  • 小的波动可能是噪声而非真实趋势
  • 平滑后的曲线更符合长期趋势的预期

三种平滑方法

1. 滑动平均

window = 30  # 30小时窗口
smoothed = convolve(data, window)

简单有效,适合去除高频噪声

2. 指数移动平均

EMA[t] = α × data[t] + (1-α) × EMA[t-1]

近期数据权重更高,适合捕捉趋势变化

3. Savitzky-Golay滤波

# 用多项式拟合窗口内的数据
savgol_filter(data, window=25, polyorder=2)

保留趋势的同时降噪,适合平滑的长期曲线

训练参数说明

参数 说明
seq_len 4320 输入历史长度(180天)
output_size 2160 预测未来步数(90天)
feature_num 16 输入特征数
labels_num 8 预测目标数
hidden_size 64 LSTM隐藏层大小
batch_size 128 每批训练样本数
lr 0.01 初始学习率
epochs 1000 最大训练轮数
patience 500 早停耐心值

文件结构说明

90天TMP预测模型源码/
├── args.py              # 参数配置(长期预测专用参数)
├── data_preprocessor.py # 数据预处理(年周期编码、长序列处理)
├── gat_lstm.py          # 模型定义(8个专家模型)
├── data_trainer.py      # 训练器(与20分钟版本类似)
├── main.py              # 训练入口
├── predict.py           # 预测接口(包含多重平滑)
├── model.pth            # 训练好的模型权重
└── scaler.pkl           # 归一化器

使用场景

1. 维护计划

# 预测90天后的压差
predictions = predictor.predict(historical_data)

# 找出何时需要CEB
threshold = 0.20  # 压差超过0.20 MPa需要清洗
ceb_dates = find_dates_above_threshold(predictions, threshold)

2. 膜寿命评估

# 分析压差上升速率
rate = (predictions[-1] - predictions[0]) / 90  # 平均每天增速
expected_lifetime = (max_TMP - current_TMP) / rate

3. 运行策略优化

# 对比不同运行参数下的长期压差
scenario_A = predict_with_params(flow=300)
scenario_B = predict_with_params(flow=360)

模型局限性

  1. 需要大量历史数据:至少180天连续数据,数据缺失会影响预测
  2. 对突变事件敏感:如果中途更换膜组或大修,需要重新训练
  3. 长期预测不确定性:越往后预测,误差累积越大
  4. 假设运行模式不变:如果未来改变流量、温度等运行参数,预测会失准

改进建议

提升预测精度

  1. 增加外部因素

    • 水质指标(浊度、COD等)
    • 清洗历史(上次CEB时间、效果)
    • 季节变量(显式编码春夏秋冬)
  2. 改进模型结构

    • 使用Transformer替代LSTM(更适合超长序列)
    • 添加注意力机制(关注关键时间点)
    • 引入外部变量的条件预测
  3. 集成多模型

    • 训练多个模型取平均(集成学习)
    • 分段预测(前30天精确,后60天粗略)

加速训练

# 使用更小的batch_size
batch_size = 64

# 减少训练数据量(只用近期数据)
start_files = 5  # 从第5个文件开始

# 降低采样率
step_size = 10  # 训练时每10步取1个样本

常见问题

Q:为什么用4320步输入,而不是更短?
A:膜污染是周期性+趋势性的复合过程,需要看足够长的历史才能区分周期波动和长期趋势。

Q:预测90天会不会太长?
A:对于膜污染这种缓慢过程,90天只是一个CEB周期。实际应用中可以每周重新预测,不断修正。

Q:如何判断模型是否学到了长期趋势?
A:绘制预测曲线,看是否捕捉到压差的缓慢上升。如果曲线波动剧烈,可能只学到了短期噪声。

Q:能否用这个模型做短期预测?
A:不建议。这个模型牺牲了短期精度换取长期趋势,用于短期预测反而不如20分钟模型。