英招

牢骚太盛防肠断,风物长宜放眼量

0%

LSTM(by pytorch)

可参考视频长短期记忆神经网络(LSTM)预测

前期准备

下载anaconda、pytorch

通过anaconda搭建虚拟环境并下载所需的工具包,通过cuDNN等实现GPU加速(可选)

本章采用python3.9+torch2.3.0+cuda11.8+cuDNN8.0

LSTM时间序列预测

1
2
3
4
5
6
7
8
9
10
import os
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from sklearn.model_selection import train_test_split
import random
import time
1
2
3
4
5
6
7
8
9
#设置随机数,保证结果可复现
def set_seed(seed):
np.random.seed(seed)
random.seed(seed)
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed)

set_seed(42)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 1. 数据加载,导入一个文件夹内的所有csv文件,并提取固定列合并成一个数据集
def load_data(folder_path, num_sites):
files = sorted([f for f in os.listdir(folder_path) if f.endswith('.csv')])
all_data = []
for file in files:
file_path = os.path.join(folder_path, file)
df = pd.read_csv(file_path,encoding='gbk')
all_data.append(df.iloc[:, [3]].values) # 使用第4列数据

combined_data = np.concatenate(all_data, axis=1)
return combined_data
# 2. LSTM 模型定义,此处构建4层LSTM,每层接一个dropout层删除部分神经元防止过拟合,后构建两层全连接层作为输出
class LSTM_Model(nn.Module):
def __init__(self, input_size, hidden_size, output_size, num_layers=4, dropout=0.1):
super(LSTM_Model, self).__init__()
self.lstm = nn.LSTM(input_size, hidden_size, num_layers=num_layers,
batch_first=True, dropout=dropout, bidirectional=False)
self.dropout = nn.Dropout(dropout) # 添加 dropout 层
self.fc1 = nn.Linear(hidden_size, hidden_size) # 第一层全连接层
self.fc2 = nn.Linear(hidden_size, output_size) # 第二层全连接层

def forward(self, x):
h0 = torch.zeros(self.lstm.num_layers, x.size(0), self.lstm.hidden_size).to(x.device)
c0 = torch.zeros(self.lstm.num_layers, x.size(0), self.lstm.hidden_size).to(x.device)

# 前向传播
out, _ = self.lstm(x, (h0, c0))
out = self.dropout(out[:, -1, :]) # 只取最后一个时间步的输出,并应用 dropout

# 通过全连接层
out = F.relu(self.fc1(out)) # 第一层全连接层和 ReLU 激活
out = self.fc2(out) # 第二层全连接层

return out
# 3. 制作滑动窗口,根据滑动窗口大小将数据形状由(num_time_steps,feature_size)变为(num_time_steps,window_size,feature_size)
def create_sliding_windows(data, window_size):
num_time_steps = data.shape[0]
num_features = data.shape[1]

windows = []
for i in range(num_time_steps - window_size):
windows.append(data[i:i + window_size])

return np.array(windows)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 4. 训练模型
folder_path = 'E:/data'
num_sites = 9 #9个csv
num_time_steps = 30642 #时间步长
window_size = 50 #滑动窗口
time_interval=24 #预测多少时间步长后的数据(每个时间步长1h,则预测24小时后的数据)
label_window_size=24 #多步预测的时间范围,即预测24个时间步
num_epochs = 1000 #epoch
target_station = 8 #选择目标站点 (从0开始)
LSTM_input_size=9 #LSTM输入特征个数
LSTM_hidden_size=50 #LSTM隐藏层神经元个数
LSTM_output_size=24 #LSTM输出特征个数

#制作数据
data = load_data(folder_path, num_sites)

#制作滑动窗口
features_windows = create_sliding_windows(data, window_size)

#提取标签数据
target_data = data[:, target_station] # 目标站点
#制作标签数据
labels_data = target_data[window_size + time_interval - 1:,np.newaxis] # 选择24小时后数据作为标签
labels = create_sliding_windows(labels_data, label_window_size)
labels = np.squeeze(labels)

#转为torch格式
features_windows_tensor = torch.tensor(features_windows[:len(labels)], dtype=torch.float).to(device)
labels_windows_tensor = torch.tensor(labels, dtype=torch.float).to(device)

#7:3划分样本,shuffle取False,并保存在GPU中
X_train,X_test,y_train,y_test=train_test_split(features_windows_tensor,labels_windows_tensor,shuffle=False,test_size=0.3)
X_train=X_train.to(device)
X_test=X_test.to(device)
y_train=y_train.to(device)
y_test=y_test.to(device)

#初始化模型并保存在GPU中,初始化损失函数
model = LSTM_Model(input_size=LSTM_input_size, hidden_size=LSTM_hidden_size, output_size=LSTM_output_size) # GAT 输出通道 + 原始特征
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
model.to(device)

#生成模型信息表格
epoch_logs=[]

#训练模型
for epoch in range(num_epochs):
start_time=time.time()
model.train()
# 前向传播
outputs = model(X_train)

# 计算损失
loss = criterion(outputs.squeeze(), y_train.squeeze())

# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()

end_time=time.time()
epoch_duration=end_time-start_time
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f},Time:{epoch_duration:2f}s')

# 测试模型
model.eval()
with torch.no_grad():
test_outputs = model(X_test)
test_loss = criterion(test_outputs.squeeze(), y_test.squeeze())
epoch_logs.append([epoch+1,loss.item(),test_loss.item(),epoch_duration])
print(f'Test Loss: {test_loss.item():.4f}')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#保存模型
torch.save(model,'model.pth')
epoch_logs_df=pd.DataFrame(epoch_logs,columns=['Epoch','Loss','Test_loss','Time'])
#每个epoch的损失(训练集与测试集)
epoch_logs_df.to_csv('LSTM_logs.csv',index=False)
#提取数据的时间
df = pd.read_csv('E:/data/time.csv', encoding='gbk')
df.set_index('time', inplace=True)
df_time=df.index[-len(y_test):]
predict_df=pd.DataFrame({
'predict':test_outputs.cpu().flatten(),
'actual':y_test.cpu().flatten()
})
df_time.to_csv('./output/time.csv')
#每个时间步长的真实值与预测值
predict_df.to_csv('LSTM_pre.csv',index=False)