pytorch框架LSTM(RNN)实现预测

一、环境配置

        1.python 3.8

        2.torch 1.9.1+cu102

        3.visdom (画图工具)

二、数据准备(模拟数据)

        (目标任务:根据现有数据,预测未来数据)

        生成y=i*2+b的数据(i的取值范围0-499,b的取值范围randint(1,10))。

# -*- coding: UTF-8 -*-
# 请看下面代码
# y=x*2+b
import random

with open('data\\data.txt', 'w', encoding='utf-8') as fp:
    for i in range(500):
        b = random.randint(1, 10)
        fp.write(f'{i},{b},{i * 2 + b}\n')

        生成的data.txt:

pytorch框架LSTM(RNN)实现预测_第1张图片

三、数据加载

        这里将data.txt中的x,b,y值分别存入列表之中。这里虽然继承了Dataset,但是后面训练代码中的数据加载部分我没有使用Dataloader,其中batch_size设为1,所以这里继承Dataset与否都行,我只是打个版。

# -*- coding: UTF-8 -*-
# 请看下面代码
from torch.utils.data import Dataset

class MyDataset(Dataset):
    def __init__(self):
        super(MyDataset, self).__init__()
        self.x = []
        self.b = []
        self.y = []
        with open('data\\data.txt', 'r', encoding='utf-8') as fp:
            for i in fp.readlines():
                temp = i.split(',')  # ['x','b','y\n']
                self.x.append(float(temp[0])/10)
                self.b.append(float(temp[1])/10)
                self.y.append(float(temp[2].rsplit()[0])/10)

    def __getitem__(self, item):
        return [self.x[item], self.b[item]], self.y[item]

    def __len__(self):
        return len(self.x)


# if __name__ == '__main__':
#     a = MyDataset()
#     # 点位分布图
#     from visdom import Visdom
#
#     vis = Visdom()
#     vis.line([0], [0], win='win', opts=dict(title='title'))
#     for i in range(len(a.x)):
#         vis.line([a.x[i] * 2 + a.b[i]], [i], win='win', update='append')

        加载x,b,y除以10主要是缩小数的大小,也类似于归一化了吧(主要这里我偷懒没做归一化了,可以自己实现归一化)。不归一化会减缓模型收敛,也会影响训练速度,所以能归一化还是归一化吧,不要像我一样。

四、模型搭建

        模型采用LSTM网络,它也是属于RNN的一种。

# -*- coding: UTF-8 -*-
# 请看下面代码
import torch
from torch import nn


class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()

        self.lstm = nn.LSTM(2, 5, 2, batch_first=True, bidirectional=True)
        self.linear1 = nn.Linear(5 * 2, 1)
        self.linear2 = nn.Linear(30, 1)

    def forward(self, x):
        # x [b,seq,2]
        output, hidden = self.lstm(x)
        # output [b,seq,5]
        output = self.linear1(output)
        # output [b,seq,1]
        output = output.view(1, -1)
        # output [b,seq*1]
        output = self.linear2(output)
        # output [b,1]
        return output

# if __name__ == '__main__':
#     model = MyModel()
#     x = torch.randn(1, 30, 2)
#     output = model(x)
#     print(output.shape)

        本次任务中设置bidirectional=True定义双向LSTM貌似比单向的时候,收敛更快。

五、训练代码

        这里设置的时间步长为30,根据前三十个数据预测下一个数据,也就是说根据1-30个数据、预测第31个数据,根据2-31个数据预测第32个数据,以此类推。

# -*- coding: UTF-8 -*-
# 请看下面代码
import torch
import visdom
from torch import optim, nn
from model import MyModel
from dataset import MyDataset

device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
lr = 1e-3
seq = 30

mydataset = MyDataset()
x_all = mydataset.x
b_all = mydataset.b
y_all = mydataset.y
max_len = len(y_all)

data_x = [
    [x_all[i], b_all[i]] for i in range(len(x_all))
]
data_y = y_all

model = MyModel().to(device)
opti = optim.AdamW(model.parameters(), lr=lr)
lossfun = nn.MSELoss().to(device)

vis = visdom.Visdom()
vis.line([0], [0], win='loss', opts=dict(title='loss-win'))
global_index = 1

for i in range(70):
    for item in range(max_len - seq):
        x = data_x[item:item + seq]
        y = data_y[item + seq]
        x, y = torch.tensor(x, dtype=torch.float), torch.tensor(y, dtype=torch.float)
        x, y = x.view(1, seq, -1), y.view(1, 1)
        x, y = x.to(device), y.to(device)
        opti.zero_grad()
        pred = model(x)
        loss = lossfun(pred, y)
        loss.backward()
        opti.step()

        if item == max_len - seq - 1:
            print(f'预测值:{pred.item():.6f},真实值:{y.item():.6f},差值为:{pred.item() - y.item():.6f}')
            vis.line([loss.item()], [global_index], win='loss', update='append')
            global_index += 1

torch.save(model.state_dict(), 'best.pt')

六、测试代码

# -*- coding: UTF-8 -*-
# 请看下面代码
import random

import torch
from visdom import Visdom
from model import MyModel

if __name__ == '__main__':
    model = MyModel()
    model.load_state_dict(torch.load('best.pt'))
    vis = Visdom()

    # 预测值和真实值分开画图
    # vis.line([0], [0], win='pred', opts=dict(title='预测'))
    # vis.line([0], [0], win='real', opts=dict(title='真实'))
    # for e in range(31, 501):
    #     sss = e / 10
    #     l = [[sss - float(i) / 10, random.randint(1, 10) / 10] for i in range(1, 31)]
    #     l.sort(reverse=False)
    #     print(l)
    #     x = torch.tensor(l, dtype=torch.float)
    #     x = x.view(1, 30, 2)
    #     out = model(x)
    #     print(out.item() * 10)
    #     vis.line([out.item() * 10], [e], win='pred', update='append')
    # with open('data\\data.txt', 'r', encoding='utf-8') as fp:
    #     for i in fp.readlines():
    #         temp = i.split(',')
    #         x = float(temp[0])
    #         y = float(temp[2].strip())
    #         vis.line([y], [x], win='real', update='append')

    # 预测值和真实值画在一个图
    vis.line([[0, 0]], [0], win='pred', opts=dict(title='预测-真实', legend=['pred', 'real']))
    with open('data\\data.txt', 'r', encoding='utf-8') as fp:
        for step, i in enumerate(fp.readlines()):
            temp = i.split(',')
            y = float(temp[2].strip())
            l = [[(step - float(j)) / 10, random.randint(1, 10) / 10] for j in range(1, 31)]
            l.sort(reverse=False)
            print(l)
            x = torch.tensor(l, dtype=torch.float)
            x = x.view(1, 30, 2)
            out = model(x)
            print(out.item() * 10)
            vis.line([[out.item() * 10, y]], [step], win='pred', update='append')

        预测-真实值图示:

pytorch框架LSTM(RNN)实现预测_第2张图片

        项目文档结构:

pytorch框架LSTM(RNN)实现预测_第3张图片

 

 七、疑问

        本人新手,在实验的过程中有些许疑问,想和大家一块讨论,也请大佬答疑。

        1、此次任务目标说是预测,但与其说是预测,本人觉得更像是拟合函数,所以这次的任务能叫做是预测吗?使用训练好的模型进行test测试,因为数据只用了x~[0-499]来作为训练集,如果使用x范围之外的数据进行test的输入,那就会表现的很不准确,所以这到底算是有预测功能呢(只是不能预测较远的数据),还是仅仅只是实现拟合函数功能罢了。(如果有预测功能,可以通过加大时间步来提升预测精确度吗?)

        2、把数据输入进网络,通过RNN\LSTM和线性层后得到[bacth,seq,1]格式的数据,之后我应该是取[:,-1,:],也就是最后一个时间步作为最终预测结果呢,还是将通过RNN\LSTM和线性层后得到[bacth,seq,1]格式的数据变成[batch,seq*1]然后放入线性层将得到的[batch,1]格式的数据作为最后的预测结果呢?

        3、如果此次任务算是实现拟合函数功能,那过程有什么不标准或可改进的吗?如果此次任务不算是预测,那么该如何使用已知数据实现未来数据的预测呢?

        欢迎大家讨论,也希望大佬答疑,谢谢!

你可能感兴趣的:(笔记,python,人工智能,rnn,lstm)