首先说明作者是神经网络纯新手,虽然之前用过神经网络的代码,但基本上都是各种copy,只搞清楚了input_size和output_size,这两天因为工作需要要跑一个lstm的回归预测,在网上找的教程都不太清楚,对新手不是很友好,对新手友好的好像好多都是错的,自己也想了很久才想明白lstm回归预测到低是什么情况,跟大家分享一下,如果有错误也希望大家能指正。
首先是lstm的一张图,引自LSTM神经网络输入输出究竟是怎样的?,建议看本文章之前先找几个教程把lstm搞懂,我个人觉得lstm懂了的标志就是下面这张图你看懂了。
首先,厘清一下本文所针对的任务,多特征的时间序列回归预测,再说直白一点,数据长成这个样子
简单来说就是n*m的ndarray,n表示时间序列的长度,m表示每一个时间点的feature,我图中的数据来自LSTM入门例子:根据前9年的数据预测后3年的客流(PyTorch实现),准确来说,这个数据也不是来自于这个作者,是一个经典的“航空客流预测”任务。这个作者应该是个机器学习的大佬,这篇文章很值得一读,但是估计新手也读不太懂,我纯新手反正一开始是没搞懂,但是非常不建议去看这篇文章的原版,因为原版完全是个错误例子。
因为是小白向我就不先说错误例子怎么做的了,只说一个最关键的点,“航空客流预测”任务中,时间序列很短只有100多个长度,完全可以一次性放进长度为100多的lstm里面训练,但是通常的时间回归预测任务里面,时间长度很多,势必要分成很多个定长的序列,然后再送进去训练。这一点也是最误解我的,我想了半天都想不通time_step到低去哪了,事实上分成训练集之后的时间序列数据本身长度就是那个time_step。(当然也可以不定长,事实上我刚刚转的那篇文章就是这样做的,但是我感觉目前我没啥必要这样搞。)下面我简单把流程说一下。
第一步:定一个time_step,把原数据拆成n个样本,比如原数据是[[1a,1b],[2a,2b],[3a,3b],[4a,4b]],time_step定为2,则原数据被拆成[[[1a,1b],[2a,2b]],[[2a,2b],[3a,3b]],[[3a,3b],[4a,4b]]]。
本来是不想在流程里面加很多字的,但是这点太关键了所以在这里说明。这里为了避免造成误解,我特意用了feature为2的数据!把time_step个数据塞到一起之后并不是直接扔给一个lstm的神经单元,之前说的文章原版本就是返了这个错误,把time_step个数据塞到一个lstm跟用mlp就没什么区别了,一定是一个数据的全部feature扔到一个单元里面,然后有多长就扔给多长的lstm来训练。这里有另外让人感觉迷惑的点,就是pytorch里面的lstm网络并没有固定长度,也就是说你训练的时候可以用10长度的lstm网络,预测的时候用5长度的塞进网络都可以,这个从lstm原理上面的权值共享很容易理解为什么可以,但是会让新手感觉很迷惑,尤其是在时间回归预测里面,我们只需要最后一个值,也就是output[-1],但是lstm是把output整个给输出出来,这个细节一定要注意!
第二步:定一个batch_size,把n个样本分成n/batch_size个小batch,每个batch扔进网络去训练,最后剩下来不足一个batch的也打包好塞进去。
第三步:出结果,预测检验就可以了!
我比较懒,lstm的输入输出,参数我就不再写了,网上介绍的也非常多,可以去看看,下面附上我的代码,同样是用“航天客流预测”任务的数据去做,大家可以试一下,调一下time_step你就会发现1步都够预测了hhh,说明这个航天客流预测数据估计本身就是个sin类数据,规律性太强,当然也可能是知乎那篇文章的三个feature的trick比较牛逼,总之大家可以试试。
ps:我真的很懒,jupyter写的代码直接download成py了,见谅!
#!/usr/bin/env python
# coding: utf-8
# In[1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
get_ipython().run_line_magic('matplotlib', 'inline')
from torch import nn
from torch.autograd import Variable
# In[121]:
batch_size=32
time_step=1
epoch=500
input_size=3
output_size=1
mid_dim=8
mid_layers=1
# In[122]:
class RegLSTM(nn.Module):
def __init__(self, inp_dim, out_dim, mid_dim, mid_layers,batch):
super(RegLSTM, self).__init__()
self.rnn = nn.LSTM(inp_dim, mid_dim, mid_layers,batch_first=batch) # rnn
self.reg = nn.Sequential(
nn.Linear(mid_dim, mid_dim),
nn.Tanh(),
nn.Linear(mid_dim, out_dim),
) # regression
def forward(self, x):
y = self.rnn(x)[0] # y, (h, c) = self.rnn(x)
batch_size, seq_len, hid_dim = y.shape
y = y.reshape(-1, hid_dim)
y = self.reg(y)
y = y.reshape(batch_size, seq_len, -1)
return y
# In[123]:
def load_data():
seq_number=np.array(
[112., 118., 132., 129., 121., 135., 148., 148., 136., 119., 104.,
118., 115., 126., 141., 135., 125., 149., 170., 170., 158., 133.,
114., 140., 145., 150., 178., 163., 172., 178., 199., 199., 184.,
162., 146., 166., 171., 180., 193., 181., 183., 218., 230., 242.,
209., 191., 172., 194., 196., 196., 236., 235., 229., 243., 264.,
272., 237., 211., 180., 201., 204., 188., 235., 227., 234., 264.,
302., 293., 259., 229., 203., 229., 242., 233., 267., 269., 270.,
315., 364., 347., 312., 274., 237., 278., 284., 277., 317., 313.,
318., 374., 413., 405., 355., 306., 271., 306., 315., 301., 356.,
348., 355., 422., 465., 467., 404., 347., 305., 336., 340., 318.,
362., 348., 363., 435., 491., 505., 404., 359., 310., 337., 360.,
342., 406., 396., 420., 472., 548., 559., 463., 407., 362., 405.,
417., 391., 419., 461., 472., 535., 622., 606., 508., 461., 390.,
432.],dtype=np.float32
)
seq_number=seq_number[:,np.newaxis]
seq_year=np.arange(12)
seq_month=np.arange(12)
seq_year_month=np.transpose(
[
np.repeat(seq_year,len(seq_month)),
np.tile(seq_month,len(seq_year)),
]
)
seq=np.concatenate((seq_number,seq_year_month),axis=1)
seq=(seq-seq.mean(axis=0))/seq.std(axis=0)
return(seq)
# In[124]:
data=load_data()
print(data)
train_size=int(len(data)*0.75)
# In[125]:
data_sample=np.zeros((train_size-time_step+1,time_step,input_size))
label_sample=np.zeros((train_size-time_step+1,time_step,output_size))
for i in range(train_size-time_step+1):
data_sample[i]=data[i:i+time_step,:]
label_sample[i]=data[i+1:i+1+time_step,0:1:]
# In[126]:
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
net=RegLSTM(input_size,output_size,mid_dim,mid_layers,True).to(device)
criterion=nn.MSELoss()
optimizer=torch.optim.Adam(net.parameters(),lr=1e-2)
# In[127]:
for i in range(epoch):
for j in range(int((train_size-time_step+1)/batch_size)):
train_X=data_sample[j*batch_size:(j+1)*batch_size,:,:]
train_Y=label_sample[j*batch_size:(j+1)*batch_size,:,:]
var_x=torch.tensor(train_X,dtype=torch.float32,device=device)
var_y=torch.tensor(train_Y,dtype=torch.float32,device=device)
out = net(var_x)
loss=criterion(out,var_y)
#loss = criterion(out[:,-1,:], var_y[:,-1,:])
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_X=data_sample[(j+1)*batch_size:,:,:]
train_Y=label_sample[(j+1)*batch_size:,:,:]
var_x=torch.tensor(train_X,dtype=torch.float32,device=device)
var_y=torch.tensor(train_Y,dtype=torch.float32,device=device)
out = net(var_x)
loss = criterion(out, var_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if i%100==0:
print('Epoch: {:4}, Loss: {:.5f}'.format(i, loss.item()))
# In[128]:
net=net.eval()
test_X=data[train_size:,:]
test_Y=data[train_size+time_step:,0:1:]
test_y=list()
for i in range(test_X.shape[0]-time_step):
test_x=test_X[i:time_step+i,:].reshape(1,time_step,input_size)
test_x=torch.tensor(test_x,dtype=torch.float32,device=device)
tem=net(test_x).cpu().data.numpy()
test_y.append(tem[0][-1])
test_y=np.array(test_y).reshape((-1,1))
diff=test_y-test_Y
l1_loss=np.mean(np.abs(diff))
l2_loss=np.mean(diff**2)
print("L1:{:.3f} L2:{:.3f}".format(l1_loss,l2_loss))
plt.plot(test_y, 'r', label='pred')
plt.plot(test_Y, 'b', label='real', alpha=0.3)
# In[7]:
a=np.array([1,2,3])
b=np.array([2,2,4])
# In[9]:
a=a.reshape(-1,1)
b=b.reshape(-1,1)
# In[8]:
np.corrcoef(a,b)
# In[10]:
a.reshape((-1))
# In[ ]: