基于pytorch的LSTM预测实现

更新时间记录:2023.6.19 更新内容:代码优化

本人接触时间序列预测有两三个月了(期间有忙其他事情),从学python,再到学pytorch,pandas等数据处理方面的知识,深感知识海洋的广阔!

本人学的不是很系统,一方面原因可能就是知识付费(并不是说知识付费不好,只是付费内容太多、花冤枉钱),另一方面就是本人的知识体系没有搭建好。我在GitHub上面并没有找到好的源码分享(pytorch框架的LSTM项目),有许多基于TensorFlow框架的,但是我本人由于懒,并不愿意花时间再去学新的框架,所以很无奈。偶然看到时间序列预测有Informer模型,我就又去研究了Informer,对于我一个新手来说,代码优点复杂,只能囫囵吞枣的看看(感谢大佬 羽星_s 的分享),不得不佩服提出Self-Attension的人,感谢Haoyi Zhou团队开源项目,代码写的真漂亮!

在学习了大量免费的资料后(非常感谢愿意免费分享资料的人),自己写出了一个简单的LSTM的代码,数据集我用的Haoyi Zhou他们的一个数据集。我决定分享出来,给更多人一个参考,吃水不忘挖井人,赠人玫瑰,手留余香!

代码非常的简单,实现功能也不齐全,有错误的地方欢迎指出,希望能和大家一起交流!

由于我是小白,在学习LSTM网络的时候花了不少时间,最后才把模型与代码关联起来,掌握这个LSTM最重要的就是搞清楚网络的输入(input.shape =(batch_size,seq_len,input_dim) ,我设置LSTM的batch_first=True)与输出(output.shape = (batch-size,seq_len,num_direction * hidden_size))。在下图中,单个LSTM单元,输入量有3个,输出量其实只有两个(output与ht是一样的),我把里面的各种门理解为一个权重,三个输入的权重比。LSTM有3个门,而RNN的另一个变体GRU却是只有两个门(我没研究过GRU)。我的源码里面有GRU的代码(这是我在GitHub上面找到的,具体是谁分享的不太清楚了,这里就没有标出出处。)

我的LSTM代码非常简单,最后的效果,对比于Informer,也非常不好。我以后可能大概率使用Informer去实现预测,也有可能继续研究LSTM,大家如果想进一步研究LSTM,我觉得可以结合Resnet、加入注意力机制等,有一些关于注意力机制的论文(但是很少有源码开源)。

目前我的项目在模型这一块花了不少时间,我已经不太想继续搞这个LSTM模型了,想继续推进项目到下一步了。所以,写个文章对过去的一段付出做个总结。虽然模型还是没有实现真正的预测(不像Informer那样预测未来,我看到过与开源代码实现了单输入单输出的预测,他使用tolist的方法实现,我没有继续研究下去,不知道多输入单输出是否有类似的方法),我只是实现了多输入单输出,没有实现多输入多输出(有几个博主的付费内容有提到这点,但我不知道他们有没有具体实现。我没有花钱买)。

希望我的文章对你有帮助,如果有,可以给我点个关注,谢谢!

数据集预览:3d9c2441799a4b989a8de1c5b6ea178f.png

 

 OT列可视化图

效果展示:

第一张是整个训练集的效果

bec11665145f4892a370d8388a69f643.png

第二张是我截取整个数据集一部分作为测试集的效果

73037c3d5ffb4f59aa8fe2e6825a0d98.png

 下面是完整的代码:


    
    
  1. # Data Development:2023/5/14 17:07
  2. import torch
  3. from datetime import datetime
  4. from torchvision import transforms, datasets
  5. from torch import nn, optim
  6. from torch.autograd import Variable
  7. from torch.utils.data import DataLoader
  8. import torch.utils.data as Data
  9. import numpy as np
  10. import pandas as pd
  11. import torch.nn.functional as F
  12. import matplotlib.pyplot as plt
  13. from sklearn.preprocessing import MinMaxScaler,StandardScaler
  14. from sklearn.metrics import mean_squared_error
  15. from sklearn.metrics import mean_absolute_error
  16. from tqdm import tqdm
  17. import os
  18. os.environ[ 'KMP_DUPLICATE_LIB_OK'] = 'TRUE'
  19. import warnings
  20. warnings.filterwarnings( "ignore")
  21. torch.manual_seed( 10)
  22. plt.style.use( 'ggplot')
  23. class Config():
  24. data_path = './data/ETTm1.csv'
  25. seq_length = 32 #time step
  26. input_size = 7 #input dim
  27. hidden_size = 128
  28. num_layers = 2
  29. num_classes = 1 #输出维度
  30. learning_rate = 0.0001
  31. batch_size = 64
  32. n_iters = 100000
  33. split_ratio = 0.8
  34. iter = 0
  35. t = 0
  36. model_train = True #是否训练
  37. # save_model=True #是否保存模型
  38. # data_path = 'WTH.csv'
  39. best_loss = 1000
  40. model_name = 'lstm'
  41. save_path = './LSTMModel/{}.pth'. format(model_name)
  42. config = Config()
  43. def read_data( data_path):
  44. '''
  45. read original data, show and plot
  46. '''
  47. # data = pd.read_excel(data_path)
  48. # data = pd.read_csv(data_path,parse_dates=['date'],index_col="date")
  49. data = pd.read_csv(data_path,parse_dates=[ 'date'],index_col=[ 'date'])
  50. # data = data[:0.3*len(data)]
  51. # data = data[data.index.year.isin([2016])].copy()
  52. #将'2016-07-01 00:00:00转换成dataframe日期格式
  53. # data["date"]= pd.to_numeric(data["date"],errors="ignore")
  54. # data.drop(['date'],axis=1,inplace=True)
  55. # data = data.values # 将pd的系列格式转换为np的数组格式
  56. data[ 'HUFL']= data[ 'HUFL'].astype( float)
  57. data[ 'HULL']= data[ 'HULL'].astype( float)
  58. data[ 'MUFL']= data[ 'MUFL'].astype( float)
  59. data[ 'MULL']= data[ 'MULL'].astype( float)
  60. data[ 'LUFL']= data[ 'LUFL'].astype( float)
  61. data[ 'LULL']= data[ 'LULL'].astype( float)
  62. data[ 'HUFL']= data[ 'HUFL'].astype( float)
  63. # Remove $ from prices and cast as float
  64. # df['Close'] = df['Close'].str.replace('$', '').astype(float)
  65. # df['Open'] = df['Open'].str.replace('$', '').astype(float)
  66. # df['High'] = df['High'].str.replace('$', '').astype(float)
  67. # df['Low'] = df['Low'].str.replace('$', '').astype(float)
  68. # Convert Date column to numeric data type
  69. # df["Date"] = pd.to_numeric(df["Date"], errors='ignore')
  70. # data = data.iloc[:int(0.1*len(data)),:]
  71. # plt.ylabel('output_temperature')
  72. # plt.autoscale(axis='x', tight=True)
  73. # plt.plot(data.iloc[:, 0])
  74. # plt.legend(['data_temperature'])
  75. # plt.show()
  76. # data = data.dropna()
  77. # data = data.drop(['date'], axis=1,inplace=True)
  78. # data=data[:,:7].values#不包括最后一行 loc包括
  79. # label=data[:,-1].values
  80. # data = data.iloc[:, :6]
  81. label = data.iloc[:,- 1]
  82. # print(data.head())
  83. return data,label
  84. def normalization( data,label):
  85. '''
  86. normalization
  87. '''
  88. # mm_x = MinMaxScaler()
  89. # mm_y = MinMaxScaler()
  90. mm_x = StandardScaler()
  91. mm_y = StandardScaler()
  92. # data = data.values # 将pd的系列格式转换为np的数组格式
  93. # data["HUFL"]= mm_x.fit_transform(data[['HUFL']])
  94. # data["HULL"]= mm_x.fit_transform(data[['HULL']])
  95. # data["MUFL"]= mm_x.fit_transform(data[['MUFD']])
  96. # data["MULL"]= mm_x.fit_transform(data[['MULL']])
  97. #
  98. # data["LUFL"]= mm_x.fit_transform(data[['LUFL']])
  99. # data["LULL"]= mm_x.fit_transform(data[['LULL']])
  100. # data["HUFL"]= mm_x.fit_transform(data[['HUFD']])
  101. data = mm_x.fit_transform(data)
  102. # data = pd.DataFrame(data)
  103. label = mm_y.fit_transform(label.values.reshape(- 1, 1))
  104. # label = pd.DataFrame(label)
  105. return data, label, mm_y
  106. def sliding_windows( data, seq_length):
  107. '''
  108. Output:The data form we can feed GRU
  109. '''
  110. x = []
  111. y = []
  112. for i in range( len(data)-seq_length- 1): #子序列的个数 长度为seq_length
  113. _x = data[i:(i+seq_length),:] #子序列 seq_length行
  114. _y = data[i+seq_length,- 1] #子序列最后一列的下一列最后一个元素 作为标签
  115. # _y = data[i + seq_length-1, -1]
  116. x.append(_x)
  117. y.append(_y)
  118. x, y = np.array(x),np.array(y) #这段代码的作用是为了将x和y转换为NumPy数组
  119. print( 'x.shape,y.shape:\n',x.shape,y.shape)
  120. return x, y
  121. def data_split( x, y, split_ratio):
  122. '''
  123. convert to torch format and split train&test
  124. '''
  125. train_size = int( len(y) * split_ratio)
  126. test_size = int( len(y) *( 1 - split_ratio - 0.1) )
  127. x_data = Variable(torch.FloatTensor(np.array(x)))
  128. y_data = Variable(torch.FloatTensor(np.array(y)))
  129. # y_data = Variable(torch.Tensor(np.array(y)))
  130. x_train = Variable(torch.FloatTensor(np.array(x[ 0:train_size])))
  131. y_train = Variable(torch.FloatTensor(np.array(y[ 0:train_size])))
  132. x_test = Variable(torch.FloatTensor(np.array(x[-test_size:])))
  133. y_test = Variable(torch.FloatTensor(np.array(y[-test_size:])))
  134. print( 'x_train.shape,y_train.shape,x_test.shape,y_test.shape:\n',x_train.shape,y_train.shape,x_test.shape,y_test.shape)
  135. return x_data, y_data, x_train, y_train, x_test, y_test
  136. def data_generator( x_train, y_train, x_test, y_test, n_iters, batch_size):
  137. '''
  138. generate DataLoader
  139. '''
  140. num_epochs = n_iters / ( len(x_train) / batch_size)
  141. num_epochs = int(num_epochs)
  142. train_dataset = Data.TensorDataset(x_train, y_train)
  143. test_dataset = Data.TensorDataset(x_test, y_test)
  144. train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
  145. batch_size=batch_size,
  146. shuffle= False)
  147. test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
  148. batch_size=batch_size,
  149. shuffle= False)
  150. # val_loader = torch.utils.data.DataLoader()
  151. return train_loader, test_loader, num_epochs
  152. data,label = read_data(config.data_path)
  153. data, label, mm_y = normalization(data, label)
  154. x, y = sliding_windows(data, config.seq_length)
  155. x_data, y_data, x_train, y_train, x_test, y_test = data_split(x, y, config.split_ratio)
  156. print( 'x_train.shape,y_train.shape,x_test.shape,y_test.shape:\n',x_train.shape,y_train.shape,x_test.shape,y_test.shape)
  157. # for index,date in enumerate(x_train):
  158. #
  159. # print(index,date)
  160. # for index, date in enumerate(y_train):
  161. # print(index, date)
  162. train_loader, test_loader, num_epochs = data_generator(x_train, y_train, x_test, y_test, config.n_iters, config.batch_size)
  163. # print('train_loader.shape,test_loader.shape,x_test.shape,y_test.shape:\n',train_loader.shape,test_loader.shape)
  164. # for i, (features,targets) in enumerate(train_loader):
  165. # print("Size of the features",features.shape)
  166. # print("Printing features:\n", features)
  167. # print("Size of the features", targets.shape)
  168. # print("Printing targets:\n", targets)
  169. # break
  170. # for i, (features,targets) in enumerate(y_train):
  171. # print("Size of the features",features.shape)
  172. # print("Printing features:\n", features)
  173. # print("Printing targets:\n", targets)
  174. # break
  175. class LSTM2(nn.Module):
  176. def __init__( self,num_classes, input_size, hidden_size, num_layers,seq_length):
  177. '''
  178. Initialize the model by setting the number of layers and other parameters'''
  179. super(LSTM2, self).__init__()
  180. self.num_classes = num_classes
  181. self.num_layers = num_layers
  182. self.input_size = input_size
  183. self.hidden_size = hidden_size
  184. self.seq_length = seq_length
  185. self.lstm = nn.LSTM(input_size=input_size, hidden_size=hidden_size,
  186. num_layers=num_layers, batch_first= True,
  187. dropout= 0.2, bidirectional= False)
  188. self.fc = nn.Linear(hidden_size, num_classes)
  189. def get_hidden( self,batch_size):
  190. '''(num_layers * num_directions, batch_size, hidden_size )'''
  191. h_0 = torch.zeros(self.num_layers,batch_size,self.hidden_size)
  192. c_0 = torch.zeros(self.num_layers,batch_size,self.hidden_size)
  193. hidden = (h_0,c_0)
  194. return hidden
  195. def forward( self, x):
  196. '''(num_layers * num_directions, batch_size, hidden_size )'''
  197. hidden = self.get_hidden(x.size( 0))
  198. # Propagate input through LSTM
  199. outputs, hidden= self.lstm(x, hidden)
  200. # print('output.shape 5', outputs.shape)
  201. #全连接层重新处理输出的tensor shape (batch_size,seq_length,hidden_size)->(seq_length*batch_size,hidden_size)
  202. outputs = outputs.reshape(- 1,self.hidden_size)
  203. # print('output.shape 4', outputs.shape)
  204. outputs = self.fc(outputs)
  205. # print('output.shape 3', outputs.shape)
  206. # outputs.shape= (batch_size, seq_length, num_directions * num_layers)
  207. outputs =outputs.view(- 1,self.seq_length,self.num_classes)
  208. # print('output.shape 1',outputs.shape)
  209. outputs = outputs[:,- 1,:] #outputs.shape 2: torch.Size([55717, 1])
  210. # out2 = outputs[-1]
  211. # print('outputs.shape 2: ',outputs.shape) #outputs.shape 2: torch.Size([32, 1])
  212. return outputs
  213. model = LSTM2(config.num_classes, config.input_size, config.hidden_size, config.num_layers, config.seq_length)
  214. # mymodel =LSTM2(num_classes, input_size, hidden_size, num_layers)
  215. print(model)
  216. criterion = torch.nn.MSELoss() # mean-squared error for regression
  217. optimizer = torch.optim.Adam(model.parameters(), lr=config.learning_rate)
  218. # Training
  219. if config.model_train:
  220. t= config.t
  221. iter = config. iter
  222. model =model
  223. print( 'Start Training')
  224. for epochs in range(num_epochs):
  225. print( '-----------training : {}/{} ----------'. format(t+ 1,num_epochs))
  226. t += 1
  227. for i,(batch_x, batch_y) in enumerate (train_loader):
  228. outputs = model(batch_x)
  229. # clear the gradients
  230. optimizer.zero_grad()
  231. #loss
  232. #print(outputs.shape, batch_y.shape)
  233. loss = criterion(outputs,batch_y)
  234. #backpropagation
  235. loss.backward()
  236. optimizer.step()
  237. iter+= 1
  238. if iter % 100 == 0:
  239. print( "iter: %d, loss: %1.5f" % ( iter, loss.item()))
  240. model. eval()
  241. test_loss= 0
  242. with torch.no_grad():
  243. test_bar = tqdm(test_loader)
  244. for data in test_bar:
  245. batch_x,batch_y = data
  246. y_test_pred = model(batch_x)
  247. test_loss = criterion(y_test_pred,batch_y)
  248. if test_loss < config.best_loss:
  249. config.best_loss = test_loss
  250. torch.save(model.state_dict(), config.save_path)
  251. print( 'Finished Training')
  252. else:
  253. print( '导入已有模型测试:')
  254. def result( x_data, y_data):
  255. model.load_state_dict(torch.load(config.save_path))
  256. print( '模型加载成功!开始测试:')
  257. model. eval()
  258. print( 'y_size:', y_data.shape)
  259. with torch.no_grad():
  260. train_predict = model(x_data)
  261. print( 'output_size:',train_predict.shape)
  262. data_predict = train_predict.data.numpy()
  263. y_data_plot = y_data.data.numpy()
  264. print( 'y_size:', y_data_plot.shape)
  265. #其中-1表示自动计算该维度的大小,1表示将每个元素变为一个单独的列。
  266. # 这样做的目的是为了方便绘制折线图或散点图
  267. y_data_plot = np.reshape(y_data_plot, (- 1, 1))
  268. print( 'y_data_plot.shape',y_data_plot.shape, 'data_predict.shape', data_predict.shape)
  269. print( 'MAE/RMSE')
  270. print(mean_absolute_error(y_data_plot, data_predict))
  271. print(np.sqrt(mean_squared_error(y_data_plot, data_predict)))
  272. data_predict = mm_y.inverse_transform(data_predict)
  273. y_data_plot = mm_y.inverse_transform(y_data_plot)
  274. plt.plot(y_data_plot)
  275. plt.plot(data_predict)
  276. plt.legend(( 'real', 'predict'),fontsize= '15')
  277. plt.show()
  278. # plt.savefig('plots/{}.png',format(len(x_train)))
  279. # result(x_data, y_data)
  280. result(x_train,y_train)
  281. result(x_test,y_test)

本人QQ:1632401994 

这些代码是我在文件里面复制出来的,有些删减,可能有错误,大家可以在百度网盘下载源码运行源码:链接: https://pan.baidu.com/s/1Navs9PidgAsAtXYiLokmJw?pwd=lstm 提取码: lstm 

文章知识点与官方知识档案匹配,可进一步学习相关知识
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyTorch是一个基于Python的开源机器学习库,它提供了丰富的工具和函数来构建和训练神经网络模型。LSTM(长短期记忆)是一种特殊类型的循环神经网络(RNN),在处理序列数据时表现出色。 使用PyTorch进行股票预测的一种常见方法是使用LSTM模型。LSTM模型可以学习和捕捉时间序列数据中的长期依赖关系,因此非常适合用于股票价格预测。 在PyTorch中,可以使用torch.nn模块来构建LSTM模型。首先,需要定义一个LSTM类,继承自torch.nn.Module,并在其中定义LSTM的结构。通常,LSTM模型由一个或多个LSTM层和一些全连接层组成。 以下是一个简单的示例代码,展示了如何使用PyTorch中的LSTM模型进行股票预测: ```python import torch import torch.nn as nn # 定义LSTM模型 class LSTMModel(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(LSTMModel, self).__init__() self.hidden_size = hidden_size self.lstm = nn.LSTM(input_size, hidden_size) self.fc = nn.Linear(hidden_size, output_size) def forward(self, input): lstm_out, _ = self.lstm(input) output = self.fc(lstm_out[-1]) return output # 定义输入数据和标签 input_size = 1 hidden_size = 32 output_size = 1 input_data = torch.randn(10, 1, input_size) # 输入数据形状为(序列长度, batch大小, 特征数) labels = torch.randn(10, output_size) # 标签形状为(序列长度, 输出特征数) # 创建模型实例 model = LSTMModel(input_size, hidden_size, output_size) # 定义损失函数和优化器 criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # 训练模型 for epoch in range(100): optimizer.zero_grad() output = model(input_data) loss = criterion(output, labels) loss.backward() optimizer.step() # 使用训练好的模型进行预测 test_input = torch.randn(1, 1, input_size) # 测试输入数据形状为(1, batch大小, 特征数) predicted_output = model(test_input) ``` 这是一个简单的示例,实际应用中可能需要更复杂的模型和更多的数据预处理步骤。你可以根据自己的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值