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. 物理约束损失函数
代码实现了三种物理约束损失函数:
- physics_loss(): 基于流速(FS)的简单物理损失
- dynamic_balance_loss(): 动态平衡损失,考虑气体抽取、排出、移动和涌动
- enhanced_physics_loss(): 增强的物理损失,结合了气体平衡、温度影响和动态平衡
4. 训练与优化
- EarlyStopping类: 实现早停机制防止过拟合
- objective(): Optuna调参的目标函数,优化模型超参数
- train_model(): 模型训练函数,结合MSE损失和物理损失
- evaluate_model(): 模型评估函数,计算RMSE、MAE和R²分数
5. 可视化
- plot_dynamic_balance_loss(): 绘制动态平衡损失随批次的变化
- plot_predictions(): 绘制预测值与真实值的对比图
6. 主要特点
- 多步时间序列预测(预测12个时间步)
- 结合了Transformer和BiLSTM的优势
- 通过物理约束损失函数确保预测结果符合物理规律
- 使用Optuna进行超参数优化
- 完整的训练、评估和可视化流程
7. 应用场景
这段代码适用于需要物理约束的时间序列预测任务,特别是当预测结果需要符合某些物理规律时。例如工业过程控制、环境监测等领域的温度预测。