【PyTorch深度学习项目实战100例】—— 使用GRU进行天气变化的时间序列预测 | 第11例

1,310 阅读6分钟

前言

大家好,我是阿光。

本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。

正在更新中~ ✨

🚨 我的项目环境:

  • 平台:Windows10

前言

大家好,我是阿光。

本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。

正在更新中~ ✨

🚨 我的项目环境:

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

💥 项目专栏:【PyTorch深度学习项目实战100例】


一、GRU进行天气变化的时间序列预测

由于大气运动极为复杂,影响天气的因素较多,而人们认识大气本身运动的能力极为有限,因此天气预报水平较低.预报员在预报实践中,每次预报的过程都极为复杂,需要综合分析,并预报各气象要素,比如温度、降水等.现阶段,以往极少出现的极端天气现象越来越多,这极大地增加了预报的难度。 本项目使用循环神经网络GRU训练一个网络模型,来预测在给定天气因素下,城市的温度变化。

二、数据集介绍

一个天气时间序列数据集,它由德国耶拿的马克思 • 普朗克生物地球化学研究所的气象站记录。在这个数据集中,每 10 分钟记录 14 个不同的量(比如气温、气压、湿度、风向等),其中包含2009-2016多年的记录。 数据集下载地址

在这里插入图片描述

在这里插入图片描述

三、定义网络结构

GRU(Gate Recurrent Unit)是循环神经网络(Recurrent Neural Network, RNN)的一种。和LSTM(Long-Short Term Memory)一样,也是为了解决长期记忆和反向传播中的梯度等问题而提出来的。

在这里插入图片描述

在这里插入图片描述

  • 更新门:定义了前面记忆保存到当前时间步的量。如果我们将重置门设置为 1,更新门设置为 0
  • 重置门:本质上来说,重置门主要决定了到底有多少过去的信息需要遗忘
# 7.定义LSTM网络
class GRU(nn.Module):
    def __init__(self, input_dim, hidden_dim, num_layers, output_dim):
        super(GRU, self).__init__()
        self.hidden_dim = hidden_dim  # 隐层大小
        self.num_layers = num_layers  # LSTM层数
        # input_dim为特征维度,就是每个时间点对应的特征数量,这里为14
        self.gru = nn.GRU(input_dim, hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim)

    def forward(self, x):
        output, h_n = self.gru(x)  # output为所有时间片的输出,形状为:16,1,4
        # print(output.shape) torch.Size([16, 1, 64]) batch_size,timestep,hidden_dim
        # print(h_n.shape) torch.Size([3, 16, 64]) num_layers,batch_size,hidden_dim
        # print(c_n.shape) torch.Size([3, 16, 64]) num_layers,batch_size,hidden_dim
        batch_size, timestep, hidden_dim = output.shape  
            
        # 将output变成 batch_size * timestep, hidden_dim
        output = output.reshape(-1, hidden_dim)
        output = self.fc(output)  # 形状为batch_size * timestep, 1
        output = output.reshape(timestep, batch_size, -1)
        return output[-1]  # 返回最后一个时间片的输出

四、训练网络

在这里插入图片描述

在这里插入图片描述

model = GRU(input_dim, hidden_dim, num_layers, output_dim)  # 定义LSTM网络
loss_function = nn.MSELoss()  # 定义损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)  # 定义优化器

# 8.模型训练
for epoch in range(epochs):
    model.train()
    running_loss = 0
    train_bar = tqdm(train_loader)  # 形成进度条
    for data in train_bar:
        x_train, y_train = data  # 解包迭代器中的X和Y
        optimizer.zero_grad()
        y_train_pred = model(x_train)
        loss = loss_function(y_train_pred, y_train.reshape(-11))
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                 epochs,
                                                                 loss)

    # 模型验证
    model.eval()
    test_loss = 0
    with torch.no_grad():
        test_bar = tqdm(test_loader)
        for data in test_bar:
            x_test, y_test = data
            y_test_pred = model(x_test)
            test_loss = loss_function(y_test_pred, y_test.reshape(-11))

    if test_loss < best_loss:
        best_loss = test_loss
        torch.save(model.state_dict(), save_path)

print('Finished Training')

完整源码

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
from torch.utils.data import TensorDataset
from tqdm import tqdm

timestep = 1  # 时间步长,就是利用多少时间窗口
batch_size = 16  # 批次大小
input_dim = 14  # 每个步长对应的特征数量,就是使用每天的4个特征,最高、最低、开盘、落盘
hidden_dim = 64  # 隐层大小
output_dim = 1  # 由于是回归任务,最终输出层大小为1
num_layers = 3  # LSTM的层数
epochs = 10
best_loss = 0
model_name = 'gru'
save_path = './{}.pth'.format(model_name)

# 1.加载时间序列数据
df = pd.read_csv('./data.csv', index_col = 0)

# 2.将数据进行标准化
scaler = StandardScaler()
scaler_model = StandardScaler()
data = scaler_model.fit_transform(np.array(df))
scaler.fit_transform(np.array(df['T (degC)']).reshape(-1, 1))

# 形成训练数据,例如12345变成12-3,23-4,34-5
def split_data(data, timestep, input_dim):
    dataX = []  # 保存X
    dataY = []  # 保存Y

    # 将整个窗口的数据保存到X中,将未来一天保存到Y中
    for index in range(len(data) - timestep):
        dataX.append(data[index: index + timestep])
        dataY.append(data[index + timestep][1])

    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_dim)
    y_train = dataY[: train_size]

    x_test = dataX[train_size:, :].reshape(-1, timestep, input_dim)
    y_test = dataY[train_size:]

    return [x_train, y_train, x_test, y_test]

# 3.获取训练数据   x_train: 1700,1,4
x_train, y_train, x_test, y_test = split_data(data, timestep, input_dim)

# 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,
                                           batch_size,
                                           True)

test_loader = torch.utils.data.DataLoader(test_data,
                                          batch_size,
                                          False)

# 7.定义GRU网络
class GRU(nn.Module):
    def __init__(self, input_dim, hidden_dim, num_layers, output_dim):
        super(GRU, self).__init__()
        self.hidden_dim = hidden_dim  # 隐层大小
        self.num_layers = num_layers  # LSTM层数
        # input_dim为特征维度,就是每个时间点对应的特征数量,这里为14
        self.gru = nn.GRU(input_dim, hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim)

    def forward(self, x):
        output, h_n = self.gru(x)  # output为所有时间片的输出,形状为:16,1,4
        # print(output.shape) torch.Size([16, 1, 64]) batch_size,timestep,hidden_dim
        # print(h_n.shape) torch.Size([3, 16, 64]) num_layers,batch_size,hidden_dim
        # print(c_n.shape) torch.Size([3, 16, 64]) num_layers,batch_size,hidden_dim
        batch_size, timestep, hidden_dim = output.shape  
            
        # 将output变成 batch_size * timestep, hidden_dim
        output = output.reshape(-1, hidden_dim)
        output = self.fc(output)  # 形状为batch_size * timestep, 1
        output = output.reshape(timestep, batch_size, -1)
        return output[-1]  # 返回最后一个时间片的输出

model = GRU(input_dim, hidden_dim, num_layers, output_dim)  # 定义LSTM网络
loss_function = nn.MSELoss()  # 定义损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)  # 定义优化器

# 8.模型训练
for epoch in range(epochs):
    model.train()
    running_loss = 0
    train_bar = tqdm(train_loader)  # 形成进度条
    for data in train_bar:
        x_train, y_train = data  # 解包迭代器中的X和Y
        optimizer.zero_grad()
        y_train_pred = model(x_train)
        loss = loss_function(y_train_pred, y_train.reshape(-11))
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                 epochs,
                                                                 loss)

    # 模型验证
    model.eval()
    test_loss = 0
    with torch.no_grad():
        test_bar = tqdm(test_loader)
        for data in test_bar:
            x_test, y_test = data
            y_test_pred = model(x_test)
            test_loss = loss_function(y_test_pred, y_test.reshape(-11))

    if test_loss < best_loss:
        best_loss = test_loss
        torch.save(model.state_dict(), save_path)

print('Finished Training')

# 9.绘制结果
plt.figure(figsize=(128))
plt.plot(scaler.inverse_transform((model(x_train_tensor).detach().numpy()).reshape(-1, 1)), "b")
plt.plot(scaler.inverse_transform(y_train_tensor.detach().numpy().reshape(-1, 1)), "r")
plt.legend()
plt.show()

y_test_pred = model(x_test_tensor)
plt.figure(figsize=(128))
plt.plot(scaler.inverse_transform(y_test_pred.detach().numpy()), "b")
plt.plot(scaler.inverse_transform(y_test_tensor.detach().numpy().reshape(-1, 1)), "r")
plt.legend()
plt.show()
  • 语言环境:python3.7
  • 编译器:PyCharm
  • PyTorch版本:1.8.1

💥 项目专栏:【PyTorch深度学习项目实战100例】


一、使用LSTM实现文本匹配任务

在NLP自然语言处理领域,有时我们需要计算不同文本之间的相似度,将不同文本进行编码,然后处理成Embedding定长表示向量,然后使用LSTM进行输出文本表示,定义多个多输入源数据进行计算。

在这里插入图片描述

在这里插入图片描述

句子1:我不爱吃剁椒鱼头,但是我爱吃鱼头

句子2:我爱吃土豆,但是不爱吃地瓜

同样使用LSTM网络,把每个句子抽象成一个向量表示,通过计算这两个向量之间的相似度,就可以快速完成文本相似度计算任务。在实际场景里,我们也通常使用LSTM网络的最后一步hidden结果,将一个句子抽象成一个向量,然后通过向量点积,或者cosine相似度的方式,去衡量两个句子的相似度。

image-20220114191557152

image-20220114191557152

二、数据集介绍

本项目使用的是2018年蚂蚁金服的文本匹配竞赛数据集数据集来自text matching。由于训练集、验证集、测试集中的正负样本比例都是1:1,因此我使用了准确率这一评价指标来衡量了这些模型的性能,数据如下:

在这里插入图片描述

在这里插入图片描述

三、网络介绍

  • 输入层:输入层负责读入两个语句信息
  • 嵌入层:使用embedding将句子进行嵌入表达
  • 表示层:采用lstm获取语义信息,使用最后一个隐层进行表达语义信息
  • 匹配层:使用余弦相似度计算两个表示层向量的相似度
  • 得分层:使用全连接层将相似度向量映射成概率 在这里插入图片描述

四、定义网络

# 定义网络结构
class LSTM(nn.Module):
    def __init__(self, vocab_size, hidden_dim, num_layers, embedding_dim, output_dim):
        super(LSTMself).__init__()
        # 嵌入层,会对所有词形成一个连续型嵌入向量,该向量的维度为embedding_dim
        # 然后利用这个向量来表示该字,而不是用索引继续表示
        # 定义LSTM层,第一个参数为每个时间步的特征大小,这里就是每个字的维度
        # 第二个参数为隐层大小
        # 第三个参数为lstm的层数
        
        self.cos_sim = nn.CosineSimilarity(dim=1, eps=1e-6)
        # 利用全连接层将其映射为2维,即0和1的概率
        self.fc = nn.Linear(1, output_dim)

    def forward(self, x_input, y_input):
        # 1.首先形成嵌入向量

        # 2.将嵌入向量导入到lstm层

        # 3.获取lstm最后一个隐层表示向量
        output_x = output_x[-1]
        output_y = output_y[-1]
        
        # 4.计算两个向量的余弦相似度
        
        # 5.形成最终输出结果
        output = self.fc(sim)
        
        return output

五、模型训练

# 6.模型训练
model = LSTM(vocab_size=vocab_size, hidden_dim=hidden_dim, num_layers=num_layers,
             embedding_dim=embedding_dim, output_dim=output_dim)

Configimizer = optim.Adam(model.parameters(), lr=lr) # 优化器
criterion = nn.CrossEntropyLoss() # 多分类损失函数

model.to(device)
loss_meter = meter.AverageValueMeter()

best_acc = 0 # 保存最好准确率
best_model = None # 保存对应最好准确率的模型参数

for epoch in range(epochs):
    model.train() # 开启训练模式
    epoch_acc = 0 # 每个epoch的准确率
    epoch_acc_count = 0 # 每个epoch训练的样本数
    train_count = 0 # 用于计算总的样本数,方便求准确率
    loss_meter.reset()
    
    train_bar = tqdm(train_loader)  # 形成进度条
    for data in train_bar:
        x_input, y_input, label = data  # 解包迭代器中的X和Y

        x_input = x_input.long().transpose(10).contiguous()
        x_input = x_input.to(device)
        
        y_input = y_input.long().transpose(10).contiguous()
        y_input = y_input.to(device)
        
        Configimizer.zero_grad()
        
        # 形成预测结果
        output_ = model(x_input, y_input)
        
        # 计算损失
        loss = criterion(output_, label.long().view(-1))
        loss.backward()
        Configimizer.step()
        
        loss_meter.add(loss.item())
        
        # 计算每个epoch正确的个数
        epoch_acc_count += (output_.argmax(axis=1) == label.view(-1)).sum()
        train_count += len(x_input)
        
    # 每个epoch对应的准确率
    epoch_acc = epoch_acc_count / train_count
    
    # 打印信息
    print("【EPOCH: 】%s" % str(epoch + 1))
    print("训练损失为%s" % (str(loss_meter.mean)))
    print("训练精度为%s" % (str(epoch_acc.item() * 100)[:5]) + '%')

    # 保存模型及相关信息
    if epoch_acc > best_acc:
        best_acc = epoch_acc
        best_model = model.state_dict()
    
    # 在训练结束保存最优的模型参数
    if epoch == epochs - 1:
        # 保存模型
        torch.save(best_model, './best_model.pkl')

六、测试语句

try:
    # 数据预处理
    input_shape = 20 # 序列长度,就是时间步大小,也就是这里的每句话中的词的个数
    # 用于测试的话
    sentence1 = "我不爱吃剁椒鱼头,但是我爱吃鱼头"
    sentence2 = "我爱吃土豆,但是不爱吃地瓜"
    # 将对应的字转化为相应的序号
    x_input = [[word2idx[word] for word in sentence1]]
    x_input = pad_sequences(maxlen=input_shape, sequences=x_input, padding='post', value=0)
    x_input = torch.from_numpy(x_input)

    y_input = [[word2idx[word] for word in sentence2]]
    y_input = pad_sequences(maxlen=input_shape, sequences=y_input, padding='post', value=0)
    y_input = torch.from_numpy(y_input)
    
    # 加载模型
    model_path = './best_model.pkl'
    model = LSTM(vocab_size=vocab_size, hidden_dim=hidden_dim, num_layers=num_layers,
             embedding_dim=embedding_dim, output_dim=output_dim)
    model.load_state_dict(torch.load(model_path, 'cpu'))

    # 模型预测,注意输入的数据第一个input_shape
    y_pred = model(x_input.long().transpose(10), y_input.long().transpose(10))
    idx2label = {0:"匹配失败!"1:"匹配成功!"}
    print('输入语句: %s \t %s' % (sentence1, sentence2))
    print('文本匹配结果: %s' % idx2label[y_pred.argmax().item()])

except KeyError as err:
    print("您输入的句子有汉字不在词汇表中,请重新输入!")
    print("不在词汇表中的单词为:%s." % err)

完整源码

【PyTorch深度学习项目实战100例】—— 使用GRU进行天气变化的时间序列预测 | 第11例_咕 嘟的博客-CSDN博客_gru预测