GRU实现时间序列预测(PyTorch版)

在这里插入图片描述

💥项目专栏:【深度学习时间序列预测案例】零基础入门经典深度学习时间序列预测项目实战(附代码+数据集+原理介绍)

文章目录

  • 前言
  • 一、基于PyTorch搭建GRU模型实现风速时间序列预测
  • 二、时序数据集的制作
  • 三、数据归一化
  • 四、数据集加载器
  • 五、搭建GRU模型
  • 六、定义模型、损失函数、优化器
  • 七、模型训练
  • 八、可视化结果
  • 完整源码


前言

  • 👑 最近很多订阅了🔥《深度学习100例》🔥的用户私信咨询基于深度学习实现时间序列的相关问题,为了能更清晰的说明,所以建立了本专栏专门记录基于深度学习的时间序列预测方法,帮助广大零基础用户达到轻松入门。

  • 👑 本专栏适用人群:🚨🚨🚨深度学习初学者刚刚接触时间序列的用户群体,专栏将具体讲解如何快速搭建深度学习模型用自己的数据集实现时间序列预测,快速让新手小白能够对基于深度学习方法进行时间序列预测有个基本的框架认识

  • 👑 本专栏整理了《深度学习时间序列预测案例》,内包含了各种不同的基于深度学习模型的时间序列预测方法,例如LSTM、GRU、CNN(一维卷积、二维卷积)、LSTM-CNN、BiLSTM、Self-Attention、LSTM-Attention、Transformer等经典模型,💥💥💥包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集

正在更新中~ ✨

🚨 我的项目环境:

  • 平台:Windows10
  • 语言环境:python3.7
  • 编译器:PyCharm
  • PyTorch版本:1.11.0

💥 项目专栏:【深度学习时间序列预测案例】零基础入门经典深度学习时间序列预测项目实战(附代码+数据集+原理介绍)


一、基于PyTorch搭建GRU模型实现风速时间序列预测

高精度、可靠的风速预报是气象学家面临的挑战。由对流风暴引起的强风,造成相当大的破坏(大规模森林破坏、停电、建筑物/房屋损坏等)。雷暴、龙卷风以及大冰雹、强风等对流事件是有可能扰乱日常生活的自然灾害,特别是在有利于对流启动的复杂地形上。即使是普通的对流事件也会产生强风,造成致命和昂贵的损失。因此,风速预测是一项重要的工作。

本篇文章我们采用了经典的循环神经网络 GRU 来对我们的时序数据建模处理,作为该专栏的第一篇文章,本篇将💎 详细介绍项目的每个实现部分以及细节处理,帮助新手小白快速建立起如何处理时序数据的框架

二、时序数据集的制作

对于实现时间序列预测,我们使用的原始数据集往往不能够直接送入模型,需要进行预处理,这里说的预处理并不是处理空值、归一化这种处理方式,而是基于原始数据构建模型需要的时序数据集。

为了介绍什么是时序数据集,我们举个例子:

假设我们的原始数据如下:

第一列为时间刻度,代表每个样本的时间,第二列则为建模数据,由于数据集中只有一列特征,所以 WIND 既是输入的特征,又是模型的输出(预测值),由于是时序预测,那么我们就需要基于以前时间发生的数据来预测未来的数据。

DATE		WIND						
1961-01-01	13.67
1961-01-02	11.50
1961-01-03	11.25
1961-01-04	8.63
1961-01-05	11.92

如果我们设置 timestep 为2的话(timestep就是时间窗口,用户滑动制作时序数据),那么我们将会产生如下的时序样本:

T1		T2 		target
13.67	11.50	11.25
11.50	11.25	8.63
11.25	8.63	11.92

其中 T1 代表前2天的数据,T2代表前1天的数据,该数据维度为【3,2】,代表3个时序样本,2天数据(也就是timestep的值),这里发现时序数据集相对于原始数据集少了2个,这是因为前2个样本没有以前数据的参照,只有从第3个样本开始,他才有前两天的数据,所以我们的时序数据集的个数应该是 len(data)-timestep 个。

这个时序数据集的意思就是利用前2天的风速去预测未来1天的风速,所以我们需要基于原始数据来提取出这种的数据集,进而送入模型进行训练,我们可以根据我们的业务需求来调整 timestep 的大小,有些任务可能定为24,这个意思就是利用前24个样本去预测未来的样本,这种一般时间周期为24小时,这个数据集是针对单特征输入的,也就是整个数据中只有一个特征 WIND,这里说的一个特征是原始特征,也就是每天(每个样本)的特征维度。

如果每天的特征存在多个,这就是多变量输入,其实制作方法是一样的,只不过相对于单特征多了一个维度,举例:

DATE		WIND		TEMPERATURE		RAIN
1961-01-01	13.67		12				134
1961-01-02	11.50		18				234
1961-01-03	11.25		13				157
1961-01-04	8.63		27				192
1961-01-05	11.92		5				260

这个数据集中每天的特征维度为3,分别是 风速温度降雨,也就是用三个维度的向量来表示每个样本数据,假设我们的 timestep 还是2的话,我们的时序数据集如下:

T1					T2 					target
[13.67,12,134]	[11.50,18,234]	11.25
[11.50,18,234]	[11.25,13,157]	8.63
[11.25,13,157]	[8.63,27,192]		11.92

与上面一维的数据一样,只不过多变量的数据维度会多一维,原来T1、T2对应每个位置是一个标量(一个数字),现在是用一个向量表示(将每天的样本特征封装到一个列表里),此时的数据维度为【3,2,3】。

  • 第一个维度的值代表时序数据集的样本数
  • 第二个维度代表窗口大小timestep
  • 第三个维度代表每天的特征数

如果熟悉NLP的朋友,应该很容易理解,第二个维度相当于 seq_len ,也就是序列长度(一句话词的个数),第三个维度相当于 feature_size ,也就是每个词的向量编码长度。

例如我爱你中国这句话就有5个词,所以 seq_len=5,如果我们用一个嵌入向量来表示每个词(假设为10)即 feature_size=10,这时我们就可以用一个【5,10】的矩阵来表示这句话,5个词,每个词向量的大小为10,如果我们有多句话,就可以在第一个位置添加一个维度,代表样本数。

此处的时序数据和NLP文本方式同理,对于新手小白可能不好理解,不过没有关系。

接下来就到了到底如何基于原始数据获得时序数据集呢,接下来基于本项目给出代码示例:

# 形成训练数据,例如12345789 12-3、23-4、34-5
def split_data(data, timestep, input_size):dataX = []  # 保存XdataY = []  # 保存Y# 将整个窗口的数据保存到X中,将未来一天保存到Y中for index in range(len(data) - timestep):dataX.append(data[index: index + timestep][:, 0])dataY.append(data[index + timestep][0])dataX = np.array(dataX)dataY = np.array(dataY)# 获取训练集大小train_size = int(np.round(0.8 * dataX.shape[0]))# 划分训练集、测试集x_train = dataX[: train_size, :].reshape(-1, timestep, input_size)y_train = dataY[: train_size].reshape(-1, 1)x_test = dataX[train_size:, :].reshape(-1, timestep, input_size)y_test = dataY[train_size:].reshape(-1, 1)return [x_train, y_train, x_test, y_test]

本项目使用的数据集是风速预测数据集,特征共有8列,但本专栏只是初探,所以只使用了 WIND 这一列特征,也就单输入,关于数据集的介绍可以查看本专栏的该篇文章 深度学习时间序列预测项目案例数据集介绍 。

有兴趣的小伙伴可以去Github上去下载第三方库采用掉包来处理数据,本代码是采用自己实现方式。

三、数据归一化

数据的 归一化和标准化 是特征缩放(feature scaling)的方法,是数据预处理的关键步骤。不同评价指标往往具有不同的量纲和量纲单位,这样的情况会影响到数据分析的结果,为了消除指标之间的量纲影响,需要进行数据归一化/标准化处理,以解决数据指标之间的可比性。原始数据经过数据归一化/标准化处理后,各指标处于同一数量级,适合进行综合对比评价。

常见的归一化方式有:Min-MaxZ-ScoreL2范数归一化等等,对于本项目使用的是 MIn-Max 归一化,他会将所有的数据缩放到 0-1 区间,对于该操作可以手动实现,代码如下:

(df - df.min()) / (df.max() - df.min())

也可以使用 sklearn 中提供的 MinMaxScaler() 函数实现,代码如下:

scaler = MinMaxScaler()
data = scaler_model.fit_transform(np.array(df))

有兴趣的小伙伴可以多多尝试不同的归一化方式,可以参照这篇文章进行尝试 归一化 (Normalization)、标准化 (Standardization)和中心化/零均值化 (Zero-centered) 。

四、数据集加载器

上面已经处理好了数据,获得了模型的输入数据,但是对于PyTorch的输入需要是Tensor类型数据,所以此时需要将上面获得的numpy.array类型数据转成Tensor类型数据。

x_train, y_train, x_test, y_test = split_data(data, config.timestep, config.input_size)# 将数据转为tensor
x_train_tensor = torch.from_numpy(x_train).to(torch.float32)
y_train_tensor = torch.from_numpy(y_train).to(torch.float32)
x_test_tensor = torch.from_numpy(x_test).to(torch.float32)
y_test_tensor = torch.from_numpy(y_test).to(torch.float32)

现在数据集已经全部转成了Tensor类型数据,此时已经可以直接喂入模型进行使用,但是我们常常使用数据加载器,数据集如果小直接使用Tensor也都OK,但是如果我们的数据集过大,如果不适用数据加载器直接加载到内存中会导致内存爆炸,所以要分批次一点点加载数据。

# 形成训练数据集
train_data = TensorDataset(x_train_tensor, y_train_tensor)
test_data = TensorDataset(x_test_tensor, y_test_tensor)# 将数据加载成迭代器
train_loader = torch.utils.data.DataLoader(train_data,config.batch_size,False)test_loader = torch.utils.data.DataLoader(test_data,config.batch_size,False)

五、搭建GRU模型

接下来就到了该项目的重中之重,构建我们的深度学习模型,本项目使用的模型是经典的循环神经网络GRU,由于此专栏注重实战讲解,所以关于GRU模型的原理词处就不赘述了,有兴趣的小伙伴可以去百度进行了解,这里你只需要知道GRU是一个深度学习模型,能够处理时序数据,获取时间维度上的关联信息。

该图为PyTorch官方文档给出的GRU模型解释:

在这里插入图片描述

为了能够使用GRU搭建模型处理我们的时序数据,我们需要GRU模型的输入和输出是什么,对于初学者了解这个很重要,它能够快速帮助我们在自己的数据集上调试运行。

该图为GRU模型的参数:

在这里插入图片描述

  • input_size:每个时间点的特征维度,就是对应上面我们说的每天的特征维度是3还是1
  • hidden_size:GRU内部隐层的维度大小
  • num_layers:GRU的层数,默认为1
  • bias:是否在隐层中添加偏置bias,默认为True
  • batch_first:如果设置为True,GRU的输入第一个维度为批次大小,也就是【batch_size,seq_len,feature_size】,如果为False,则模型的输入Tensor的维度为【seq_len,batch_size,feature_size】,默认为False
  • dropout:是否采用dropout
  • bidirectional:是否采用双向GRU模型,默认单向为False

该图为GRU模型的输入:

在这里插入图片描述

GRU的模型输入Tensor的维度为【batch_size,seq_len,feature_size】,其实也可以是【seq_len,batch_size,feature_size】,但是我们常常将批次大小作为第一个维度传入,容易理解,本项目采用的输入维度为第一种方式,批次为先。

此处有小伙伴会存在一个问题,模型的输入还有一个 h_0 作为输入,如果了解GRU原理的同学就可以知道这个输入变量是干嘛的,就是模型初始的隐层状态,对于这个变量可传可不传,如果不传则默认为0,有兴趣了解这个参数到底传入还是不传入可以参考这篇文章 对LSTM中每个batch都初始化隐含层的理解 ,本项目传入的隐层状态是传入的,但是传入的参数是以0进行填充,和默认不传入一致,只是为了让小伙伴了解这个参数是怎么传入的。

if hidden is None:h_0 = x.data.new(self.num_layers, batch_size, self.hidden_size).fill_(0).float()
else:h_0 = hidden
output, h_0 = self.gru(x, h_0)

该图为GRU模型的输出:

在这里插入图片描述
GRU模型的输出有两个,一个输出的是模型的最终输出,也就是我们想要的输出,另外一个输出是模型的隐藏状态,对于本项目来说我们并不需要他。

对于这两个输出的维度一定要知道,首先是我们需要的输出 output,该输出的维度为【batch_size,seq_len,D * hidden_size】,此处的D就是我们是否采用双向GRU,如果设置 bidirectional=True,则D=2,否则D=1,hidden_size 就是GRU中间隐层的维度大小。

对于h_n的输出我们简答了解一下就好,因为我们不会对他进行处理。

为了能够理解GRU的输入和输出,举个例子说明:

model = nn.GRU(input_size=3, hidden_size=10, num_layers=2, batch_first=True)x = torch.randn(32, 5, 3)output, h_0 = model(x)

我们定义了输入向量,该向量的维度为【32,5,3】,分别代表【批次大小,时间片,特征大小】,用语言叙述就是32个样本,然后用5天的数据去未来1天的数据,每天的特征维度为3。

print(output.shape)
>>>torch.Size([32, 5, 10])print(h_0.shape)
>>>torch.Size([2, 32, 10])

我们可以看到 output 的输出维度为【batch_size,seq_len,D * hidden_size】,由于我们的GRU是单向的,所以D=1。

如果设置为双向:

model = nn.GRU(input_size=3, hidden_size=10, num_layers=2, batch_first=True, bidirectional=True)x = torch.randn(32, 5, 3)output, h_0 = model(x)print(output.shape)
>>>torch.Size([32, 5, 20])

接下来就到了使用PyTorch搭建GRU模型,代码如下:

# 定义GRU网络
class GRU(nn.Module):def __init__(self, feature_size, hidden_size, num_layers, output_size):super(GRU, self).__init__()self.hidden_size = hidden_size  # 隐层大小self.num_layers = num_layers  # gru层数# feature_size为特征维度,就是每个时间点对应的特征数量,这里为1self.gru = nn.GRU(feature_size, hidden_size, num_layers, batch_first=True)self.fc = nn.Linear(hidden_size, output_size)def forward(self, x, hidden=None):batch_size = x.shape[0] # 获取批次大小# 初始化隐层状态if hidden is None:h_0 = x.data.new(self.num_layers, batch_size, self.hidden_size).fill_(0).float()else:h_0 = hidden# GRU运算output, h_0 = self.gru(x, h_0)# 获取GRU输出的维度信息batch_size, timestep, hidden_size = output.shape  # 将output变成 batch_size * timestep, hidden_dimoutput = output.reshape(-1, hidden_size)# 全连接层output = self.fc(output)  # 形状为batch_size * timestep, 1# 转换维度,用于输出output = output.reshape(timestep, batch_size, -1)# 我们只需要返回最后一个时间片的数据即可return output[-1]

本项目的模型采用的是GRU+全连接层,有小伙伴可能有一个问题就是搭建模型的过程中有句代码就是 output = output.reshape(timestep, batch_size, -1),它的目的就是将我们的输出数据的第一个维度变成时间片。

如果我们设置 timestep=5,那么我们的 output 的输出就为【5,32,1】,作为模型输出我们只需要最后一个时间片的数据作为输出即可,因为GRU是处理时序数据的,最后一个时间片包含了前面所有时间片的信息(T1,T2…)。

感兴趣的小伙伴可以尝试不返回最后一个时间片的数据作为输出,可以将每个时间片的数据池化再进行输出(例如将每个时间片的数据取平均、取最大等操作),也就是将5个【32,1】维度的张量取平均再进行输出,不过这样会提高运算时间,至于模型效果会不会提高,需要自己在自己的数据集上面尝试,希望小伙伴初学时可以多多尝试。

六、定义模型、损失函数、优化器

为了训练数据,首先定义GRU模型,然后再定义对应的损失函数,由于我们这里是风速预测,显然是个回归问题,所以采用回归问题常用的 MESLoss(),如果可以的话,可以自定义损失函数,针对自己的项目需求定义对应的损失函数。

对于优化器来讲,使用的也是目前常用的 Adam 优化器,对于新手来讲也可以多多尝试其它的优化器,比如 SGDRMSprop等,对于优化器的选择,可以参考这篇文章 Pytorch 30种优化器总结 。

model = GRU(config.feature_size, config.hidden_size, config.num_layers, config.output_size)  # 定义GRU网络
loss_function = nn.MSELoss()  # 定义损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=config.learning_rate)  # 定义优化器

在定义优化器的同时,我们需要将模型的参数传入,可以通过 model.parameters() 获得,如果模型中有些层不需要训练,我们可以将参数冻结,这时使用优化器训练模型时,该层参数就不会被修改,实现该目的可以使用以下函数:

# 冻结模型参数
for param in model.parameters():param.requires_grad = False

另外多说一点,有些复杂的模型需要定义多个优化器,也就是模型中不同的层或者参数需要使用不同的优化策略进行优化,例如本项目中我们的模型包含了GRU层和全连接层,我们可以定义 optimizer_gru = torch.optim.Adam()optimizer_fc = torch.optim.RMSprop(),这时两个层可以使用不同的学习率或者权重衰减方式进行训练,但这是后话, 对于新手不需要搞这么复杂,用一个优化器训练模型即可,如果有能力可以自己尝试以下,使用多个优化器来训练自己的模型。

另外一点,如果我们需要进行权重衰减,可以在定义优化器的同时传入 weight_decay ,有兴趣的同学可以参考这篇文章进行了解 权重衰退 。

七、模型训练

下面代码是相对标准的模型训练框架,涵盖训练集和测试集的验证,该部分用户可以自己DIY设计,比如统计相关的指标信息(整体损失、epoch损失等)或者打印信息等,这些都可以按照自己的喜好进行调整。

对于模型验证部分,添不添加都OK,但是一般我们是会加上的,因为防止过拟合,让模型有更好的泛化性、鲁棒性,模型训练完成需要在测试集上进行验证,如果损失下降,则保留此轮训练的模型。

# 模型训练
for epoch in range(config.epochs):model.train()running_loss = 0train_bar = tqdm(train_loader)  # 形成进度条for data in train_bar:x_train, y_train = data  # 解包迭代器中的X和Yoptimizer.zero_grad()y_train_pred = model(x_train)loss = loss_function(y_train_pred, y_train.reshape(-1, 1))loss.backward()optimizer.step()running_loss += loss.item()train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,config.epochs,loss)# 模型验证model.eval()test_loss = 0with torch.no_grad():test_bar = tqdm(test_loader)for data in test_bar:x_test, y_test = datay_test_pred = model(x_test)test_loss = loss_function(y_test_pred, y_test.reshape(-1, 1))if test_loss < config.best_loss:config.best_loss = test_losstorch.save(model.state_dict(), save_path)print('Finished Training')

上述代码理解相对简单,就不多赘述了,有同学会好奇 train_bar = tqdm(train_loader) 这句代码是做什么的,对于模型训练来将不是必须的,他只是用来形成进度条的,帮助用户了解当前模型的训练进度,效果如下:

train epoch[1/10] loss:0.017: 100%|██████████████████████████████████████████████████| 141/141 [00:01<00:00, 83.10it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 360.01it/s]
train epoch[2/10] loss:0.014: 100%|██████████████████████████████████████████████████| 141/141 [00:02<00:00, 62.06it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 404.49it/s]
train epoch[3/10] loss:0.014: 100%|██████████████████████████████████████████████████| 141/141 [00:01<00:00, 80.92it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 276.92it/s]
train epoch[4/10] loss:0.014: 100%|██████████████████████████████████████████████████| 141/141 [00:01<00:00, 84.16it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 375.53it/s]
train epoch[5/10] loss:0.014: 100%|██████████████████████████████████████████████████| 141/141 [00:01<00:00, 77.95it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 399.98it/s]
train epoch[6/10] loss:0.014: 100%|██████████████████████████████████████████████████| 141/141 [00:01<00:00, 81.74it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 404.50it/s]
train epoch[7/10] loss:0.014: 100%|██████████████████████████████████████████████████| 141/141 [00:01<00:00, 72.54it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 370.81it/s]
train epoch[8/10] loss:0.014: 100%|██████████████████████████████████████████████████| 141/141 [00:02<00:00, 68.67it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 237.74it/s]
train epoch[9/10] loss:0.014: 100%|██████████████████████████████████████████████████| 141/141 [00:02<00:00, 64.72it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 303.73it/s]
train epoch[10/10] loss:0.014: 100%|█████████████████████████████████████████████████| 141/141 [00:02<00:00, 66.28it/s]
100%|█████████████████████████████████████████████████████████████████████████████████| 36/36 [00:00<00:00, 292.70it/s]
Finished Training

对于模型损失的指标,用户可以手动保存,就是将每轮的损失值保存到列表中或者其它处理,也可以掉包来实现,在pytorch中有个 meter 库可以实现这个目的,有兴趣的同学可以参考这篇文章 pytorch中meter.ClassErrorMeter()使用方法 。

八、可视化结果

为了查看模型的训练效果,我们采用可视化的方式来对比真实值和预测值的差距,对于绘图,我们采用了经典的可视化库 matplotlib ,可视化代码如下:

# 绘制结果
plot_size = 200 # 绘制前200个样本
plt.figure(figsize=(12, 8))
plt.plot(scaler.inverse_transform((model(x_train_tensor).detach().numpy()[: plot_size]).reshape(-1, 1)), "b")
plt.plot(scaler.inverse_transform(y_train_tensor.detach().numpy().reshape(-1, 1)[: plot_size]), "r")
plt.legend()
plt.show()y_test_pred = model(x_test_tensor)
plt.figure(figsize=(12, 8))
plt.plot(scaler.inverse_transform(y_test_pred.detach().numpy()[: plot_size]), "b")
plt.plot(scaler.inverse_transform(y_test_tensor.detach().numpy().reshape(-1, 1)[: plot_size]), "r")
plt.legend()
plt.show()

解释下上述代码,首先定义了 plot_size ,这个变量是用来绘制样本数的,因为我们的数据集中存在几千个样本,如果全部绘制,会导致曲线过于拥挤,为了更好的观察拟合效果,所以只绘制其中一小部分。

还有一处需要说明的是 scaler.inverse_transform() ,由于我们的数据集在训练之前进行了归一化,所以在绘制曲线时需要将预测结果进行反归一化,恢复到原来的量纲区间。

训练集效果:

在这里插入图片描述

测试集效果:

在这里插入图片描述

完整源码

注意🚨🚨🚨:由于是针对于新手小白入门的系列专栏,所以代码并没有采用开发大型项目的方式,而是python单文件实现,这样能够帮助新人一键复制调试运行,不需要理解复杂的项目构造,另外一点就是由于是帮助新人理解时间序列预测基本过程,所以源码仅包含了时间序列预测的基本框架结构,有些地方实现略有简陋,有能力的小伙伴可以根据自己的能力在此基础上进行修改,例如尝试更深层次的模型结构,尝试更多的参数,以及进行分文件编写(模型训练、模型测试、定义模型、绘制图像)达到项目开发流程。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import tushare as ts
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from torch.utils.data import TensorDataset
from tqdm import tqdmclass Config():data_path = './data/wind_dataset.csv'timestep = 1  # 时间步长,就是利用多少时间窗口batch_size = 32  # 批次大小feature_size = 1  # 每个步长对应的特征数量,这里只使用1维,每天的风速hidden_size = 256  # 隐层大小output_size = 1  # 由于是单输出任务,最终输出层大小为1,预测未来1天风速num_layers = 2  # gru的层数epochs = 10 # 迭代轮数best_loss = 0 # 记录损失learning_rate = 0.0003 # 学习率model_name = 'gru' # 模型名称save_path = './{}.pth'.format(model_name) # 最优模型保存路径config = Config()# 1.加载时间序列数据
df = pd.read_csv(config.data_path, index_col = 0)# 2.将数据进行标准化
scaler = MinMaxScaler()
scaler_model = MinMaxScaler()
data = scaler_model.fit_transform(np.array(df))
scaler.fit_transform(np.array(df['WIND']).reshape(-1, 1))# 形成训练数据,例如12345789 12-3456789
def split_data(data, timestep, feature_size):dataX = []  # 保存XdataY = []  # 保存Y# 将整个窗口的数据保存到X中,将未来一天保存到Y中for index in range(len(data) - timestep):dataX.append(data[index: index + timestep][:, 0])dataY.append(data[index + timestep][0])dataX = np.array(dataX)dataY = np.array(dataY)# 获取训练集大小train_size = int(np.round(0.8 * dataX.shape[0]))# 划分训练集、测试集x_train = dataX[: train_size, :].reshape(-1, timestep, feature_size)y_train = dataY[: train_size].reshape(-1, 1)x_test = dataX[train_size:, :].reshape(-1, timestep, feature_size)y_test = dataY[train_size:].reshape(-1, 1)return [x_train, y_train, x_test, y_test]# 3.获取训练数据   x_train: 170000,30,1   y_train:170000,7,1
x_train, y_train, x_test, y_test = split_data(data, config.timestep, config.feature_size)# 4.将数据转为tensor
x_train_tensor = torch.from_numpy(x_train).to(torch.float32)
y_train_tensor = torch.from_numpy(y_train).to(torch.float32)
x_test_tensor = torch.from_numpy(x_test).to(torch.float32)
y_test_tensor = torch.from_numpy(y_test).to(torch.float32)# 5.形成训练数据集
train_data = TensorDataset(x_train_tensor, y_train_tensor)
test_data = TensorDataset(x_test_tensor, y_test_tensor)# 6.将数据加载成迭代器
train_loader = torch.utils.data.DataLoader(train_data,config.batch_size,False)test_loader = torch.utils.data.DataLoader(test_data,config.batch_size,False)# 7.定义GRU网络
class GRU(nn.Module):def __init__(self, feature_size, hidden_size, num_layers, output_size):super(GRU, self).__init__()self.hidden_size = hidden_size  # 隐层大小self.num_layers = num_layers  # gru层数# feature_size为特征维度,就是每个时间点对应的特征数量,这里为1self.gru = nn.GRU(feature_size, hidden_size, num_layers, batch_first=True)self.fc = nn.Linear(hidden_size, output_size)def forward(self, x, hidden=None):batch_size = x.shape[0] # 获取批次大小# 初始化隐层状态if hidden is None:h_0 = x.data.new(self.num_layers, batch_size, self.hidden_size).fill_(0).float()else:h_0 = hidden# GRU运算output, h_0 = self.gru(x, h_0)# 获取GRU输出的维度信息batch_size, timestep, hidden_size = output.shape  # 将output变成 batch_size * timestep, hidden_dimoutput = output.reshape(-1, hidden_size)# 全连接层output = self.fc(output)  # 形状为batch_size * timestep, 1# 转换维度,用于输出output = output.reshape(timestep, batch_size, -1)# 我们只需要返回最后一个时间片的数据即可return output[-1]model = GRU(config.feature_size, config.hidden_size, config.num_layers, config.output_size)  # 定义GRU网络
loss_function = nn.MSELoss()  # 定义损失函数
optimizer = torch.optim.AdamW(model.parameters(), lr=config.learning_rate)  # 定义优化器# 8.模型训练
for epoch in range(config.epochs):model.train()running_loss = 0train_bar = tqdm(train_loader)  # 形成进度条for data in train_bar:x_train, y_train = data  # 解包迭代器中的X和Yoptimizer.zero_grad()y_train_pred = model(x_train)loss = loss_function(y_train_pred, y_train.reshape(-1, 1))loss.backward()optimizer.step()running_loss += loss.item()train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,config.epochs,loss)# 模型验证model.eval()test_loss = 0with torch.no_grad():test_bar = tqdm(test_loader)for data in test_bar:x_test, y_test = datay_test_pred = model(x_test)test_loss = loss_function(y_test_pred, y_test.reshape(-1, 1))if test_loss < config.best_loss:config.best_loss = test_losstorch.save(model.state_dict(), save_path)print('Finished Training')# 9.绘制结果
plot_size = 200
plt.figure(figsize=(12, 8))
plt.plot(scaler.inverse_transform((model(x_train_tensor).detach().numpy()[: plot_size]).reshape(-1, 1)), "b")
plt.plot(scaler.inverse_transform(y_train_tensor.detach().numpy().reshape(-1, 1)[: plot_size]), "r")
plt.legend()
plt.show()y_test_pred = model(x_test_tensor)
plt.figure(figsize=(12, 8))
plt.plot(scaler.inverse_transform(y_test_pred.detach().numpy()[: plot_size]), "b")
plt.plot(scaler.inverse_transform(y_test_tensor.detach().numpy().reshape(-1, 1)[: plot_size]), "r")
plt.legend()
plt.show()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/573645.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

踩坑uniapp中打包Andiord app,在真机调试时地图以及定位功能可以正常使用,打包成app后失效的问题

首先看到这是uni官网提出的&#xff0c;app上建议使用高德地图。 下面就用高德地图进行配置。 步骤一&#xff1a;登陆高德地图控制台 名称和类型根据自己情况填写选择即可 步骤二&#xff1a; 添加key 步骤三&#xff1a;取到SHA1 进入uniapp开发官网 点击应用名称&#…

深度好文:解决Ubuntu 18.04安装nvidia显卡驱动,导致内核不匹配:无需重装系统修复内核

深度好文&#xff1a;解决Ubuntu 18.04安装nvidia显卡驱动&#xff0c;导致内核不匹配&#xff1a;无需重装系统修复内核 目录 一、问题描述二、尝试修复三、安装Nvidia驱动和CUDA并配置cuDNN四、总结 一、问题描述 昨天打算更新一下Ubuntu 18.04的显卡驱动&#xff0c;以支持…

使用OpenXlab下载数据集(推荐)

OpenXLab浦源面向人工智能领域开发者和使用者,提供一站式AI开发平台。提供包括应用开发,模型免费托管,数据集下载等服务 官方网址:https://openxlab.org.cn/datasets?lang=zh-CN 提供了6622种数据集, 涉及计算机视觉、自然语言处理、多模态、通用机器学习、音频识别以及其他…

互联网公司裁员现象调查:探寻背后原因与应对策略

近年来&#xff0c;随着互联网行业的快速发展&#xff0c;裁员现象日益引起广泛关注。本文通过对互联网公司裁员现象的调查&#xff0c;分析背后原因&#xff0c;探讨应对策略&#xff0c;以期为互联网企业和求职者提供参考。 近年来&#xff0c;我国互联网行业历经高速发展&am…

软件项目的上线和维护流程

软件项目的上线和维护流程是软件开发生命周期中至关重要的阶段&#xff0c;它确保了软件产品能够在实际环境中稳定运行&#xff0c;并根据用户反馈进行持续改进。以下是软件项目上线和维护的一般流程&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业…

【一】TensorFlow神经网络模型构建之神经元函数及优化方法

TensorFlow神经网络模型构建主要涉及如下几块&#xff1a;神经元函数、卷积函数、池化函数、分类函数、优化方法。下面分别对这几块进行展开说明&#xff1a; 神经元函数及优化方法 神经网络之所以能解决非线性问题&#xff08;如语音、图像识别等&#xff09;&#xff0c;本…

《计算机工程与应用》投稿经验2024

要按照官网格式写论文&#xff0c;这会节省很多时间。审稿费120元&#xff0c;本人计算机视觉方向&#xff0c;9页&#xff0c;没有打折&#xff0c;版面费5000&#xff0c;彩图和表格过多的原因。版权协议等论文录用之后再交即可&#xff0c;一审二审的时候不用交&#xff0c;…

如何使用Python进行网络安全与密码学【第149篇—密码学】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 用Python进行网络安全与密码学&#xff1a;技术实践指南 随着互联网的普及&#xff0c;网络…

基于javaweb(springboot)汽车配件管理系统设计和实现以及文档报告

基于javaweb(springboot)汽车配件管理系统设计和实现以及文档报告 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐…

LinkedList讲解指南

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

Java编程语言全面指南:历史、特性、环境搭建步骤

一、背景 在数字化的时代浪潮中&#xff0c;Java显得尤为璀璨&#xff0c;它不仅仅是由Sun Microsystems公司孕育而出的一种编程语言&#xff0c;更是一种融汇简洁性、面向对象的设计、分布式编程能力、稳健与安全性、平台独立性、可移植性、多线程处理能力和动态性于一体的…

Python图像处理——计算机视觉中常用的图像预处理

概述 在计算机视觉项目中&#xff0c;使用样本时经常会遇到图像样本不统一的问题&#xff0c;比如图像质量&#xff0c;并非所有的图像都具有相同的质量水平。在开始训练模型或运行算法之前&#xff0c;通常需要对图像进行预处理&#xff0c;以确保获得最佳的结果。图像预处理…