首先说明作者是神经网络纯新手,虽然之前用过神经网络的代码,但基本上都是各种copy,只搞清楚了input_size和output_size,这两天因为工作需要要跑一个lstm的回归预测,在网上找的教程都不太清楚,对新手不是很友好,对新手友好的好像好多都是错的,自己也想了很久才想明白lstm回归预测到低是什么情况,跟大家分享一下,如果有错误也希望大家能指正。

首先是lstm的一张图,引自LSTM神经网络输入输出究竟是怎样的?,建议看本文章之前先找几个教程把lstm搞懂,我个人觉得lstm懂了的标志就是下面这张图你看懂了。


DNN pytorch 回归 pytorch回归预测_回归


首先,厘清一下本文所针对的任务,多特征的时间序列回归预测,再说直白一点,数据长成这个样子

DNN pytorch 回归 pytorch回归预测_数据_02

 简单来说就是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[ ]: