wmy 574c973f3b 1:项目结构修正 9 months ago
..
README.md 574c973f3b 1:项目结构修正 9 months ago
args.py 574a5d969b 修正命名 10 months ago
data_preprocessor.py 574a5d969b 修正命名 10 months ago
data_trainer.py 574a5d969b 修正命名 10 months ago
gat_lstm.py 574a5d969b 修正命名 10 months ago
main.py 574a5d969b 修正命名 10 months ago
model.pth 574a5d969b 修正命名 10 months ago
predict.py 574a5d969b 修正命名 10 months ago
scaler.pkl 574a5d969b 修正命名 10 months ago

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分钟模型。