AI练手系列(二)—— RNN之飞机乘客预测

数据集

数据集来自 flights.csv,从1949年1月到1960年12月,美国航空每月的乘客数量,在我上传的资源里边可以获取,改天给弄到个人的Github里去。
数据集如下:

https://download.csdn.net/download/rongsenmeng2835/12621779

目标

创建RNN模型,对乘客数量进行预测。比如70%的训练集,对未来的30%月份进行预测。

使用RNN模型进行训练

包导入、定义超参、数据加载

先将数据读取进来:

import numpy as np
import pandas as pd
import torch
from torch import nn
import matplotlib.pyplot as plt 

#定义超参数
EPOCH = 1000
LR = 0.01

#数据加载
data = pd.read_csv('flight/flights.csv')
dataset_ori = data['passengers'].values.astype('float32')
#print(data)
#print(dataset_ori)

数据预处理

先将数据进行尺度变换:

max_value = np.max(dataset_ori)
min_value = np.min(dataset_ori)
#尺子
scalar = max_value-min_value
#缩放
dataset = list(map(lambda x:x/scalar,dataset_ori))
print(dataset)

再将数据转化为可被训练的数据集格式,我们认为每个月乘客数量在时间序列上存在先后关系,以前后两个月的乘客数量作为某一时刻的输入和输出,组合成我们要训练的数据集,此处转化后数据还是numpy.ndarray的类型:

#数据转换
#利用前两个月的数据预测后一个月的数据
'''
look_back: 过去多少个月的乘客数
dataX: 生成的数据集X
dataY: 下一个月的乘客数
'''
def create_dataset(dataset,look_back=2):
    dataX,dataY =[],[]
    for i in range(len(dataset)-look_back):
        a = dataset[i:(i+look_back)]
        dataX.append(a)
        dataY.append(dataset[i+look_back])
    return np.array(dataX),np.array(dataY)

#创建输入输出
data_X,data_Y = create_dataset(dataset)
print(data_X,data_Y)
#data_X_shape:(142, 2),data_Y_shape:(142,)

接下来我们开始划分数据集,并将数据集转换为tensor的格式,以变pytorch训练:

#划分数据集
train_size = int(len(data_X) * 0.7)
train_X = data_X[:train_size]
train_Y = data_Y[:train_size]
'''
    改变数据的维度
    RNN读入的数据维度是 (batch, seq, feature)
    只有一个序列,所以 seq 是 1
    feature 代表依据的几个月份,这里定的是两个月份,所以 feature 就是 2
'''
train_X = train_X.reshape(-1,1,2)#batch,seq,feature,-1代表指定后面两个后数据自动切割分配成多少个batch
train_Y = train_Y.reshape(-1,1,1)
#print(train_X)
#print(train_X.shape)=(99, 1, 2)
#print(train_Y)
#print(train_Y.shape)=(99, 1, 1)
#转化为tensor
train_x = torch.from_numpy(train_X)
#type(train_x)=torch.Tensor
train_y = torch.from_numpy(train_Y)

定义网络模型

定义训练要使用的网络模型,使用torch.nn中自带的RNN模型,需要注意各层传入数据的尺寸与维度变换:

class rnn_model(nn.Module):
    def __init__(self):
        #super() 函数是用于调用父类(超类)的一个方法。rnn_model的父类是nn.Module
        super(rnn_model,self).__init__()
        #输入是两个月的数据input_size=2,输出是下一个月的数据output_size=1,中间隐藏层的层数任意		 指定,指定为4
        input_size,hidden_size,output_size =2,4,1
        num_layers = 1
        #此处模型可以从RNN、LSTM、GRU中任选
        self.rnn = nn.RNN(input_size,hidden_size,num_layers)
        #self.rnn = nn.LSTM(input_size,hidden_size,num_layers)
        #self.rnn = nn.GRU(input_size,hidden_size,num_layers)
        self.out = nn.Linear(hidden_size,output_size)
    def forward(self,x):
        x,_ = self.rnn(x)
        #batch,seq,hidden
        b,s,h = x.shape 
        #转化为线性层的输入
        x = x.view(b*s,h)
        x = self.out(x)
        x = x.view(b,s,-1)#(99, 1, 1)
        return x

设置使用GPU/CPU

设置使用GPU,并将模型导入:

#设置使用GPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
#使用定义好的RNN
model = rnn_model()
#注意模型和输入数据都需要to device,训练数据后面传入
mode  = model.to(device)

定义损失函数及优化器

回归问题损失函数使用MSE,优化器我一般会选用Adam:

criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(),lr = LR)

模型训练

模型训练一般包含几个步骤:

  1. 取出数据及标签并均送入GPU或CPU
  2. 前向传播
  3. 计算损失函数
  4. 清空上一轮梯度
  5. 反向传播
  6. 参数更新
  7. 周期打印loss值或tensorboard数据保存绘图
  8. 保存模型参数(非必要)
#开始训练
for epoch in range(EPOCH):
    #数据送入GPU
    var_x = train_x.to(device)
    var_y = train_y.to(device)
    #前向传播
    out = model(var_x)
    #计算损失函数,传入的是预测值和真实值
    loss = criterion(out,var_y)

    #反向传播之前先清空上一轮的梯度,在pytorch里边需要注意这个
    optimizer.zero_grad()
    #反向传播
    loss.backward()
    #参数更新
    optimizer.step()
    #loss值打印
    if(epoch+1)%100 == 0:
        print('Epoch:{},loss:{:.5f}'.format(epoch+1,loss.item()))

结果如下:

Epoch:100,loss:0.00788

Epoch:200,loss:0.00367

Epoch:300,loss:0.00251

Epoch:400,loss:0.00235

Epoch:500,loss:0.00225

Epoch:600,loss:0.00216

Epoch:700,loss:0.00209

Epoch:800,loss:0.00202

Epoch:900,loss:0.00197

Epoch:1000,loss:0.00192

模型测试与验证

这一块也需要对测试数据进行对应的数据变换,过程一般与模型训练的前半部分类似,由于我们之前对数据进行了尺度变换,测试完成之后为了后面的可视化,我们需要将数据还原回来:

#训练完成后进行测试
# eval()会自动把BN和DropOut固定住,不会取平均,而是用训练好的值
model = model.eval()
#使用全量数据
data_X = data_X.reshape(-1,1,2)
data_X = torch.from_numpy(data_X)
# 测试集的预测结果
pred_test = model(data_X.cuda())
# 改变输出的格式
pred_test = pred_test.view(-1).data.cpu().numpy()

#数据反变换
pred_test = list(map(lambda x: x*scalar,pred_test))

结果可视化

# 画出实际结果和预测的结果
plt.plot(pred_test, 'r', label='prediction')
plt.plot(dataset_ori, 'b', label='real')
#添加图例,可以指定图例名,没有参数时默认上面的标签就是图例名
plt.legend(['Prediction','Real'])

我们得到的结果:

image-20200716082740378

可以看到原始RNN的效果还是很不错的,我们还可以换LSTM和RGU看看效果:

LSTM训练结果:

image-20200716083059316

再看看GRU的:

image-20200716083240171

貌似在我这组超参数(EPOCH=1000,LR=0.01)的情况下,原始RNN训练出来的效果还要更好一点,这里只做简单展示,不作细调。

使用传统线性回归模型

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
#% matplotlib inline

# 加载数据
data = pd.read_csv('flights.csv')
#print(data.head())

dataset_ori = data['passengers'].values.astype('float32')

# 线性回归
from sklearn import linear_model 
model = linear_model.LinearRegression()
y = data['passengers']
x = [[x] for x in range(1, len(y)+1)]
#print(x)
#print(y)
train_size = int(len(x) * 0.7)
train_x = x[:train_size]
train_y = y[:train_size]
model.fit(train_x, train_y)
y = model.predict(x)
#print(y)
plt.plot(x, y)
plt.plot(dataset_ori)
plt.show()

结果如下:

image-20200716084243400

或者我们将模型换成多项式的非线性回归模型:

# 非线性回归
from sklearn.preprocessing import PolynomialFeatures
# 0-3次方
poly_reg = PolynomialFeatures(degree=3)
x_poly = poly_reg.fit_transform(x)
print(x_poly)
train_x_poly = x_poly[:train_size]
model = linear_model.LinearRegression()
model.fit(train_x_poly, train_y)
y = model.predict(x_poly)
#print(len(x))
#print(len(y))
plt.plot(x, y)
plt.plot(dataset_ori)
plt.show()

结果如下

image-20200716084403239

小小总结

通过上面的尝试我们不难发现,在处理序列信息时(如此处的时间先后关系),RNN模型相较于传统线性回归和多项式的非线性回归,其优势都十分明显,多一种招数来适应特征各异的数据,这也许就是我们学习RNN的意义所在吧。

加油!跑起来,就有了风!

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
RNN(循环神经网络)实现多输入单输出预测时,训练数据需要的格式如下: 首先,我们需要将多个输入序列对齐,使得它们具有相同长度。这可以通过补齐(padding)或截断(trimming)来实现,确保所有输入序列具有相同的时间步数。 然后,将这些对齐后的输入序列转换为张量,以便RNN可以处理。通常,我们会创建一个3D张量,其形状为[batch_size, time_steps,input_dim],其中batch_size表示数据的批次大小,time_steps表示时间步数,input_dim表示每个时间步的输入维度。 接下来,我们将设置好相应的目标值。对于单输出预测任务,我们需要为每个输入序列提供相应的目标值。这可以是一个连续值(回归任务)或一个离散值(分类任务)。 最后,我们将通过传递这些输入张量和目标值来训练RNN模型。训练时,我们会将数据划分为多个小批次,每个批次包含一批输入序列及其相应的目标值。每个批次将被输入到RNN模型中进行训练,并根据目标值计算预测结果与真实值之间的损失。通过最小化损失函数,我们可以调整模型的参数,以便提高预测的准确性。 总结而言,RNN实现多输入单输出预测时,需要将多个输入序列对齐并转换为张量形式,同时提供相应的目标值,并通过传递批次数据进行训练。这样可以让RNN模型学习到输入序列与目标值之间的关联,并进行有效的预测

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值