实现了一个结合Transformer和双向LSTM(BiLSTM)的时间序列预测模型,用于预测温度值(T0),并包含了物理约束的损失函数来增强模型的物理合理性

python 复制代码
# Import libraries
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim.lr_scheduler import ReduceLROnPlateau
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from joblib import dump, load
import optuna
import warnings

warnings.filterwarnings("ignore", category=UserWarning, module="torch.nn.modules.transformer")

# 加载并预处理数据
def load_and_preprocess_data():
    """
    Load CSV data and apply standard scaling.
    载入CSV数据并标准化特征和目标值
    """
    csv_path = r"D:\Assignments\programInc\241209.csv"
    data = pd.read_csv(csv_path)

    # 选择用于建模的特征
    features = data[['T1', 'T2', 'T3', 'T4', 'FS', 'WD', 'Target']]
    target = data['T0']

    scaler = StandardScaler()
    scaled_features = scaler.fit_transform(features)
    scaled_target = scaler.fit_transform(target.values.reshape(-1, 1))
    dump(scaler, 'scaler.joblib')  # 保存缩放器以便反转换

    return torch.tensor(scaled_features, dtype=torch.float32), torch.tensor(scaled_target, dtype=torch.float32)

# 创建时间序列样本
def create_sequences(data, target, window_size, pred_steps):
    """
    Create input-output pairs using sliding window for multi-step prediction.
    构建输入输出对,用于多步预测
    """
    X, y = [], []
    max_step = max(pred_steps)
    for i in range(len(data) - window_size - max_step + 1):
        X.append(data[i:i + window_size])
        target_indices = [i + window_size + step - 1 for step in pred_steps]
        y.append(target[target_indices].flatten())
    return torch.stack(X), torch.stack(y)

# 模型定义:Transformer + BiLSTM
class TransformerBiLSTM(nn.Module):
    """
    Time series model combining Transformer and BiLSTM
    混合Transformer和BiLSTM的时间序列预测模型
    """
    def __init__(self, input_dim, hidden_dim, num_layers, num_heads, output_dim, dropout_rate=0.3):
        super(TransformerBiLSTM, self).__init__()
        assert input_dim == 7, "Input dim should be 7 (特征数)"
        assert output_dim == 12, "Output dim should be 12 (预测步数)"

        self.transformer = nn.TransformerEncoder(
            nn.TransformerEncoderLayer(input_dim, num_heads, hidden_dim, dropout=dropout_rate, batch_first=True),
            num_layers
        )
        self.bilstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True, bidirectional=True)
        self.fc = nn.Linear(hidden_dim * 2, output_dim)
        
    def forward(self, x):
        transformer_out = self.transformer(x)

        # 初始化LSTM的隐状态和记忆状态
        batch_size = x.size(0)
        h0 = torch.zeros(2 * 2, batch_size, self.bilstm.hidden_size).to(x.device)
        c0 = torch.zeros(2 * 2, batch_size, self.bilstm.hidden_size).to(x.device)
        
        bilstm_out, _ = self.bilstm(transformer_out, (h0, c0))
        output = self.fc(bilstm_out[:, -1, :])  # 使用最后一个时间步的输出
        return output

# 简化的物理损失函数(物理约束项)
def physics_loss(pred, fs, alpha=0.8):
    """
    Physics-informed loss based on flow speed (FS)
    简单的物理损失,使用流速
    """
    fs_expanded = fs.unsqueeze(1).expand(-1, pred.size(1))
    physics_penalty = torch.mean(torch.abs(fs_expanded * pred - pred))
    return alpha * physics_penalty

# 动态平衡损失函数
def dynamic_balance_loss(pred, t0, t1, fs, wd, alpha=0.5, beta=0.3, gamma=0.2):
    """
    动态平衡损失:结合抽取、排出、移动和涌动气体量
    """
    t0_exp = t0.unsqueeze(1).expand(-1, pred.size(1))
    t1_exp = t1.unsqueeze(1).expand(-1, pred.size(1))
    fs_exp = fs.unsqueeze(1).expand(-1, pred.size(1))
    wd_exp = wd.unsqueeze(1).expand(-1, pred.size(1))
    
    Q_extraction = pred
    Q_ejection = t1_exp * fs_exp
    Q_movement = fs_exp * (t0_exp - t1_exp)
    Q_surge = wd_exp * (t0_exp - t1_exp)
    
    balance_loss = torch.mean(torch.abs(Q_extraction - (Q_ejection + Q_movement + Q_surge)))
    return alpha * balance_loss

# 综合增强的物理损失函数
def enhanced_physics_loss(pred, t1, t0, fs, wd, alpha=0.5, beta=0.3, gamma=0.2):
    """
    组合气体平衡 + 温度影响 + 动态平衡的增强物理损失
    """
    t0_exp = t0.unsqueeze(1).expand(-1, pred.size(1))
    t1_exp = t1.unsqueeze(1).expand(-1, pred.size(1))
    fs_exp = fs.unsqueeze(1).expand(-1, pred.size(1))
    wd_exp = wd.unsqueeze(1).expand(-1, pred.size(1))

    gas_balance = torch.mean(torch.abs(t0_exp - t1_exp - fs_exp * pred))
    temp_impact = torch.mean(torch.abs(wd_exp * pred - pred))

    Q_extraction = pred
    Q_ejection = t1_exp * fs_exp
    Q_movement = fs_exp * (t0_exp - t1_exp)
    Q_surge = wd_exp * (t0_exp - t1_exp)
    dynamic_bal = torch.mean(torch.abs(Q_extraction - (Q_ejection + Q_movement + Q_surge)))

    return alpha * gas_balance + beta * temp_impact + gamma * dynamic_bal

# 提前停止类
class EarlyStopping:
    """
    Early stopping to prevent overfitting.
    早停机制以防止过拟合
    """
    def __init__(self, patience=10, min_delta=1e-4):
        self.patience = patience
        self.min_delta = min_delta
        self.best_loss = float('inf')
        self.counter = 0

    def check(self, loss):
        if loss < self.best_loss - self.min_delta:
            self.best_loss = loss
            self.counter = 0
        else:
            self.counter += 1
        return self.counter >= self.patience

# 可视化动态平衡损失
def plot_dynamic_balance_loss(model, data_loader):
    """
    绘制动态平衡损失随批次的变化图
    """
    model.eval()
    dynamic_losses = []
    with torch.no_grad():
        for X_batch, y_batch in data_loader:
            preds = model(X_batch)
            db_loss = dynamic_balance_loss(preds, X_batch[:, -1, 0], X_batch[:, -1, 1], X_batch[:, -1, 5], X_batch[:, -1, 6])
            dynamic_losses.append(db_loss.item())

    plt.figure(figsize=(10, 5))
    plt.plot(dynamic_losses, label='Dynamic Balance Loss', color='green')
    plt.xlabel('Batch 批次')
    plt.ylabel('Loss 损失')
    plt.title('Dynamic Balance Loss Across Batches')
    plt.legend()
    plt.show()

# Optuna优化目标函数
def objective(trial):
    """
    Optuna objective function to optimize model hyperparameters.
    用于调参的目标函数
    """
    hidden_dim = trial.suggest_int('hidden_dim', 32, 128)
    num_layers = trial.suggest_int('num_layers', 1, 4)
    num_heads = trial.suggest_int('num_heads', 1, 4)
    dropout_rate = trial.suggest_float('dropout_rate', 0.1, 0.5)

    # 载入数据
    features, targets = load_and_preprocess_data()
    window_size = 20
    pred_steps = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23]
    X, y = create_sequences(features, targets, window_size, pred_steps)

    # 划分训练和测试集
    train_size = int(len(X) * 0.8)
    X_train, y_train = X[:train_size], y[:train_size]
    X_test, y_test = X[train_size:], y[train_size:]

    model = TransformerBiLSTM(input_dim=7, hidden_dim=hidden_dim,
                               num_layers=num_layers, num_heads=num_heads,
                               output_dim=len(pred_steps), dropout_rate=dropout_rate)

    optimizer = optim.Adam(model.parameters(), lr=0.001)
    criterion = nn.MSELoss()
    early_stopper = EarlyStopping(patience=5)

    # 模型训练过程
    for epoch in range(50):
        model.train()
        optimizer.zero_grad()
        preds = model(X_train)
        loss = criterion(preds, y_train)

        # 加入物理损失
        phy_loss = enhanced_physics_loss(preds, X_train[:, -1, 1], X_train[:, -1, 0],
                                         X_train[:, -1, 5], X_train[:, -1, 6])
        total_loss = loss + phy_loss
        total_loss.backward()
        optimizer.step()

        # 早停检查
        if early_stopper.check(total_loss.item()):
            break

    # 模型评估
    model.eval()
    with torch.no_grad():
        preds = model(X_test)
        loss = criterion(preds, y_test)
    return loss.item()  # 返回优化目标:MSE损失


def train_model(X_train, y_train, model, criterion, optimizer, epochs=50):
    """
    模型训练函数
    """
    early_stopper = EarlyStopping(patience=10)
    for epoch in range(epochs):
        model.train()
        optimizer.zero_grad()
        preds = model(X_train)
        loss = criterion(preds, y_train)

        # 加入物理损失
        phy_loss = enhanced_physics_loss(preds, X_train[:, -1, 1], X_train[:, -1, 0],
                                         X_train[:, -1, 5], X_train[:, -1, 6])
        total_loss = loss + phy_loss
        total_loss.backward()
        optimizer.step()

        print(f"Epoch {epoch+1}, Loss: {total_loss.item():.4f}")

        if early_stopper.check(total_loss.item()):
            print("早停触发。停止训练。")
            break

def evaluate_model(model, X_test, y_test):
    """
    模型评估函数
    """
    model.eval()
    with torch.no_grad():
        preds = model(X_test)
    preds_np = preds.numpy()
    y_test_np = y_test.numpy()

    rmse = mean_squared_error(y_test_np, preds_np, squared=False)
    mae = mean_absolute_error(y_test_np, preds_np)
    r2 = r2_score(y_test_np, preds_np)

    print(f"RMSE: {rmse:.4f}")
    print(f"MAE: {mae:.4f}")
    print(f"R² Score: {r2:.4f}")

    return preds_np

def plot_predictions(preds_np, y_test_np):
    """
    绘制预测值 vs 真实值
    """
    plt.figure(figsize=(15, 5))
    plt.plot(preds_np[:100, 0], label='Predicted 预测')
    plt.plot(y_test_np[:100, 0], label='Actual 实际')
    plt.title('Predicted vs Actual (First Step 第一时间步预测)')
    plt.xlabel('Sample 样本')
    plt.ylabel('T0')
    plt.legend()
    plt.show()

代码功能详细解释

这段代码实现了一个结合Transformer和双向LSTM(BiLSTM)的时间序列预测模型,用于预测温度值(T0),并包含了物理约束的损失函数来增强模型的物理合理性。以下是主要功能的详细解释:

1. 数据加载与预处理

  • load_and_preprocess_data(): 从CSV文件加载数据,选择特定特征(T1-T4, FS, WD, Target)和目标变量(T0),然后使用StandardScaler进行标准化处理。
  • create_sequences(): 创建时间序列的滑动窗口样本,支持多步预测。

2. 模型架构

  • TransformerBiLSTM类 : 结合了Transformer和BiLSTM的混合模型:
    • Transformer部分用于捕获全局依赖关系
    • BiLSTM部分用于处理序列数据
    • 全连接层输出12个时间步的预测结果

3. 物理约束损失函数

代码实现了三种物理约束损失函数:

  1. physics_loss(): 基于流速(FS)的简单物理损失
  2. dynamic_balance_loss(): 动态平衡损失,考虑气体抽取、排出、移动和涌动
  3. enhanced_physics_loss(): 增强的物理损失,结合了气体平衡、温度影响和动态平衡

4. 训练与优化

  • EarlyStopping类: 实现早停机制防止过拟合
  • objective(): Optuna调参的目标函数,优化模型超参数
  • train_model(): 模型训练函数,结合MSE损失和物理损失
  • evaluate_model(): 模型评估函数,计算RMSE、MAE和R²分数

5. 可视化

  • plot_dynamic_balance_loss(): 绘制动态平衡损失随批次的变化
  • plot_predictions(): 绘制预测值与真实值的对比图

6. 主要特点

  1. 多步时间序列预测(预测12个时间步)
  2. 结合了Transformer和BiLSTM的优势
  3. 通过物理约束损失函数确保预测结果符合物理规律
  4. 使用Optuna进行超参数优化
  5. 完整的训练、评估和可视化流程

7. 应用场景

这段代码适用于需要物理约束的时间序列预测任务,特别是当预测结果需要符合某些物理规律时。例如工业过程控制、环境监测等领域的温度预测。

相关推荐
深度学习入门1 分钟前
学习深度学习是否要先学习机器学习?
人工智能·深度学习·神经网络·学习·机器学习·ai·深度学习入门
willhu20081 小时前
Tensorflow2保存和加载模型
深度学习·机器学习·tensorflow
Sylvan Ding1 小时前
远程主机状态监控-GPU服务器状态监控-深度学习服务器状态监控
运维·服务器·深度学习·监控·远程·gpu状态
赵青临的辉2 小时前
简单神经网络(ANN)实现:从零开始构建第一个模型
人工智能·深度学习·神经网络
2303_Alpha3 小时前
深度学习入门:深度学习(完结)
人工智能·笔记·python·深度学习·神经网络·机器学习
深度学习入门3 小时前
机器学习,深度学习,神经网络,深度神经网络之间有何区别?
人工智能·python·深度学习·神经网络·机器学习·机器学习入门·深度学习算法
埃菲尔铁塔_CV算法4 小时前
深度学习驱动下的目标检测技术:原理、算法与应用创新
深度学习·算法·目标检测
欲掩5 小时前
神经网络与深度学习第六章--循环神经网络(理论)
rnn·深度学习·神经网络
qyhua6 小时前
用 PyTorch 从零实现简易GPT(Transformer 模型)
人工智能·pytorch·transformer
高建伟-joe9 小时前
内容安全:使用开源框架Caffe实现上传图片进行敏感内容识别
人工智能·python·深度学习·flask·开源·html5·caffe