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:
这里将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')
预测-真实值图示:
项目文档结构:
本人新手,在实验的过程中有些许疑问,想和大家一块讨论,也请大佬答疑。
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、如果此次任务算是实现拟合函数功能,那过程有什么不标准或可改进的吗?如果此次任务不算是预测,那么该如何使用已知数据实现未来数据的预测呢?
欢迎大家讨论,也希望大佬答疑,谢谢!