为什么使用rnn模型时,需要用到滑动窗口预测所有数据:
由于RNN模型,是之前的输入会对后来的输入样本的预测结果有影响。所以训练模型时候,在网络构建中定义的输入尺寸,在这里定义了输入的样本数是 TIME_STEP 个,所以预测时,也要用相同的尺寸,预测出相应尺寸的结果。
如果这段话你听的不太明白你需要在深入理解一下RNN循环神经网络的原理。
参考:史上最详细循环神经网络讲解(RNN/LSTM/GRU) - 知乎
这里是我构建的rnn网络的结构:
class RNN(nn.Module):
def __init__(self):
super(RNN, self).__init__()
self.rnn = nn.RNN(
input_size=INPUT_SIZE,
hidden_size=128,
num_layers=1,
batch_first=True,
)
self.fc1 = nn.Linear(128, 64)
self.fc2 = nn.Linear(64, 1)
# https://zhuanlan.zhihu.com/p/60792047
def forward(self, x, h_state):
r_out, h_state = self.rnn(x, h_state)
# outs = []
# for time_step in range(r_out.size(1)):
# outs.append(self.out(r_out[:, time_step, :]))
# return torch.stack(outs, dim=1), h_state
# instead, for simplicity, you can replace above codes by follows
r_out = r_out.reshape(-1, 128)
outs = self.fc1(r_out)
outs = F.relu(outs)
# outs = outs.reshape(-1, 64)
outs = self.fc2(outs)
outs = outs.view(-1, TIME_STEP, 1)
return outs, h_state
训练模型的过程:
# 训练模型 # 创建rnn网络模型
rnn = RNN()
# 模型加入装置
rnn.to(device)
# 优化器
optimizer = torch.optim.Adam(rnn.parameters(), lr=Lr)
# 损失函数
loss_func = nn.MSELoss()
# 初始化隐藏状态
h_state = None
losses = []
for epoch in range(300):
batch_loss = []
batch_count = batch_size*TIME_STEP
for start in range(0, len(X_data), batch_count):
if start + batch_count < len(X_data):
end = start + batch_count
# print(rf'{start}--->{end}')
# 提取数据变成张量,提取shape(batch, time_step, input_size/output_size)
xx = torch.tensor(X_data[start:end], dtype=torch.float, requires_grad=True).view(
[batch_size, TIME_STEP, INPUT_SIZE])
yy = torch.tensor(y_data[start:end], dtype=torch.float, requires_grad=True).view(
[batch_size, TIME_STEP, 1])
# print(yy.view([10, 10, 22])) # tensor_01.view([2, 3, -1, 2])
# print(yy.shape)
# print(yy)
prediction, h_state = rnn(xx, h_state) # rnn output
# !! next step is important !!
h_state = h_state.data # repack the hidden state, break the connection from last iteration
loss = loss_func(prediction, yy)
optimizer.zero_grad()
loss.backward()
optimizer.step()
try:
batch_loss.append(loss.data.numpy())
except:
batch_loss.append(loss.data.numpy())
if epoch % 10 == 0:
losses.append(np.mean(batch_loss))
print(epoch, losses[-1])
在测试集,用滑动窗口预测全部数据的操作:
# 转化tensor格式
x = torch.tensor(X_data, dtype=torch.float).to(device)
# 滑动窗口预测(十个结果全取的结果)
# 初始化隐藏状态
h_state = None
prediction_ = torch.FloatTensor()
for start in range(0, len(X_data), TIME_STEP):
if start + TIME_STEP < len(X_data):
end = start + TIME_STEP
x_ = x[start:end].view([1, TIME_STEP, 22])
prediction, h_state = rnn(x_, h_state)
h_state = h_state.data # rnn output
prediction_ = torch.cat((prediction_, prediction), 0)
# 预测结果tensor拉平
prediction_ = prediction_.view(-1, 1)
# 添加最后没法与预测的边角料,目的使预测结果长度与真实结果长度一致
last_len = len(X_data) % 10
last_ = torch.rand(last_len, 1).to(device)
prediction_ = torch.cat((prediction_, last_), 0)
# 转化为数组形式
predict = prediction_.data.numpy()
结果展示:
预测了一个风电场7天出力,结果显示还可以,但是比不上xgb,建议在特征量没有很大量的时候,还是慎重使用深度学习去预测。