-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtrain.py
178 lines (134 loc) · 4.64 KB
/
train.py
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# 首先 import 一些主要的包
import numpy as np
import time
import matplotlib.pyplot as plt
import os
import torch
import torch.nn as nn
import torch.utils.data as Data
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
# 获取文件名
file_name = 'train_data.npy'
# 读取数组
data = np.load(file_name)
# 生成题目所需的数据集合
def generate_data(data):
# 记录 data 的长度
n = data.shape[0]
# 目标是生成可直接用于训练和测试的 x 和 y
x = []
y = []
# 建立 (14 -> 1) 的 x 和 y
for i in range(15, n):
x.append(data[i - 15: i - 1])
y.append(data[i - 1])
# 转换为 numpy 数组
x = np.array(x)
y = np.array(y)
return x, y
x, y = generate_data(data)
x = torch.tensor(x, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32)
# 将 y 转化形状
y = torch.unsqueeze(y, dim=1)
print(x.shape, y.shape)
# 样本总数
num_samples = x.shape[0]
num_train = round(num_samples * 0.8)
num_valid = round(num_samples * 0.1)
num_test = num_samples - num_train - num_valid
dataset = Data.TensorDataset(x, y)
train_data, valid_data, test_data = Data.random_split(dataset, (num_train, num_valid, num_test))
batch_size = 512
train_loader = Data.DataLoader(train_data, batch_size=batch_size, shuffle=True)
valid_loader = Data.DataLoader(train_data, batch_size=batch_size, shuffle=False)
test_loader = Data.DataLoader(train_data, batch_size=batch_size, shuffle=False)
def compute_mae(y_hat, y):
"""
:param y_hat: 用户的预测值
:param y: 标准值
:return: MAE 平均绝对误差 mean(|y*-y|)
"""
return torch.mean(torch.abs(y_hat - y))
def compute_mape(y_hat, y):
"""
:param y_hat: 用户的预测值
:param y: 标准值
:return: MAPE 平均百分比误差 mean(|y*-y|/y)
"""
return torch.mean(torch.abs(y_hat - y)/y)
def evaluate_accuracy(data_loader, model):
"""
:param data_loader: 输入的 DataLoader
:param model: 用户的模型
:return: 对应的 MAE 和 MAPE
"""
# 初始化参数
mae_sum, mape_sum, n = 0.0, 0.0, 0
# 对每一个 data_iter 的每一个 x,y 进行计算
for x, y in data_loader:
x = x.to(device)
# 计算模型得出的 y_hat
y_hat = model(x)
# 计算对应的 MAE 和 RMSE 对应的和,并乘以 batch 大小
mae_sum += compute_mae(y_hat, y) * y.shape[0]
mape_sum += compute_mape(y_hat, y) * y.shape[0]
# n 用于统计 DataLoader 中一共有多少数量
n += y.shape[0]
# 返回时需要除以 batch 大小,得到平均值
return mae_sum / n, mape_sum / n
# 输入的数量是前 14 个交易日的收盘价
num_inputs = 14
# 输出是下一个交易日的收盘价
num_outputs = 1
# 隐藏层的个数
num_hiddens = 128
# 建立一个稍微复杂的 LSTM 模型
class LSTM(nn.Module):
def __init__(self, num_hiddens, num_outputs):
super(LSTM, self).__init__()
self.lstm = nn.LSTM(
input_size=1,
hidden_size=num_hiddens,
num_layers=1,
batch_first=True
)
self.fc = nn.Linear(num_hiddens, num_outputs)
def forward(self, x):
x = x.view(x.shape[0], -1, 1)
r_out, (h_n, h_c) = self.lstm(x, None)
out = self.fc(r_out[:, -1, :]) # 只需要最后一个的output
return out
lstm = LSTM(num_hiddens, num_outputs).to(device)
loss_fn = nn.MSELoss()
optimizer = torch.optim.Adam(lstm.parameters(), lr=1e-3)
# 循环 num_epochs 次
epochs = 200
for epoch in range(epochs):
# 初始化参数
train_l_sum, n = 0.0, 0
# 初始化时间
start = time.time()
lstm.train()
# 对训练数据集的每个 batch 执行
for x, y in train_loader:
x, y = x.to(device), y.to(device)
y_hat = lstm(x)
loss = loss_fn(y_hat, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_l_sum += loss.item() * y.shape[0]
# 计数一共有多少个元素
n += y.shape[0]
# 模型开启预测状态
lstm.eval()
# 对验证集合求指标
# 这里训练集其实可以在循环内高效地直接算出,这里为了代码的可读性牺牲了效率
train_mae, train_mape = evaluate_accuracy(train_loader, lstm)
valid_mae, valid_mape = evaluate_accuracy(valid_loader, lstm)
if (epoch + 1) % 10 == 0:
print(
'epoch %d, train loss %.6f, train mae %.6f, mape %.6f, valid mae %.6f, mape %.6f, time %.2f sec'
% (epoch + 1, train_l_sum / n, train_mae, train_mape, valid_mae, valid_mape, time.time() - start))
torch.save(lstm.state_dict(), 'results/model.pt')