使用Pytorch从零开始构建GRU

news2024/10/5 14:04:41

门控循环单元 (GRU) 是 LSTM 的更新版本。让我们揭开这个网络的面纱并探索这两个兄弟姐妹之间的差异。

您听说过 GRU 吗?门控循环单元(GRU)是更流行的长短期记忆(LSTM)网络的弟弟,也是循环神经网络(RNN)的一种。就像它的兄弟一样,GRU 能够有效地保留顺序数据中的长期依赖性。此外,它们还可以解决困扰普通 RNN 的“短期记忆”问题。

考虑到序列建模和预测中循环架构的遗留问题,GRU 有望因其卓越的速度而超越其前辈,同时实现相似的准确性和有效性。

在本文中,我们将介绍 GRU 背后的概念,并将 GRU 与 LSTM 的机制进行比较。我们还将探讨这两种 RNN 变体的性能差异。如果您不熟悉 RNN 或 LSTM,您可以查看我之前介绍这些主题的文章:

  • 使用Pytorch从零开始构建RNN
  • 使用Pytorch从零开始构建LSTM

什么是 GRU?

门控循环单元(GRU),顾名思义,是RNN 架构的一种变体,它使用门控机制来控制和管理神经网络中单元之间的信息流。Cho 等人于 2014 年才引入 GRU 。可以被认为是一种相对较新的架构,特别是与Sepp Hochreiter 和 Jürgen Schmidhuber于 1997 年提出的广泛采用的 LSTM 相比。
在这里插入图片描述

GRU 的结构使其能够自适应地捕获大型数据序列的依赖性,而不会丢弃序列早期部分的信息。这是通过其门控单元实现的,类似于 LSTM 中的门控单元,它解决了传统 RNN 的梯度消失/爆炸问题。这些门负责调节每个时间步要保留或丢弃的信息。我们将在本文后面深入探讨这些门的工作原理以及它们如何克服上述问题的细节。
在这里插入图片描述
除了其内部门控机制之外,GRU 的功能就像 RNN 一样,其中顺序输入数据由 GRU 单元在每个时间步与内存一起消耗,或者称为隐藏状态。然后,隐藏状态与序列中的下一个输入数据一起重新输入到 RNN 单元中。这个过程像继电器系统一样持续进行,产生所需的输出。

GRU 的内部运作

GRU 保持长期依赖性或记忆的能力源于 GRU 单元内产生隐藏状态的计算。虽然LSTM在单元之间传递两种不同的状态——单元状态和隐藏状态,分别承载长期和短期记忆,但 GRU 在时间步之间仅传递一种隐藏状态。由于隐藏状态和输入数据所经历的门控机制和计算,该隐藏状态能够同时保持长期和短期依赖性。
在这里插入图片描述
GRU 单元仅包含两个门:更新门和重置门。就像 LSTM 中的门一样,GRU 中的这些门经过训练可以有选择地过滤掉任何不相关的信息,同时保留有用的信息。这些门本质上是包含0到1之间的值的向量,这些值将与输入数据和/或隐藏状态相乘。门向量中的0值表示输入或隐藏状态中的相应数据不重要,因此将返回零。另一方面,门向量中的值1意味着相应的数据很重要并将被使用。

在本文的其余部分中,我将交替使用术语“门”和“向量” ,因为它们指的是同一事物。

GRU单元的结构如下所示。
在这里插入图片描述
虽然由于存在大量连接,该结构可能看起来相当复杂,但其背后的机制可以分为三个主要步骤。

重置门

第一步,我们将创建重置门。该门是使用前一时间步的隐藏状态和当前时间步的输入数据导出和计算的。

在这里插入图片描述
从数学上讲,这是通过将先前的隐藏状态和当前输入与其各自的权重相乘并在将总和传递给sigmoid函数之前将它们相加来实现的。sigmoid函数会将值转换为介于0和1之间,从而允许门在后续步骤中在不太重要和更重要的信息之间进行过滤。
g a t e r e s e t = σ ( W i n p u t r e s e t ⋅ x t + W h i d d e n r e s e t ⋅ h t − 1 ) gate_{reset} = \sigma(W_{input_{reset}} \cdot x_t + W_{hidden_{reset}} \cdot h_{t-1}) gatereset=σ(Winputresetxt+Whiddenresetht1)
当整个网络通过反向传播进行训练时,方程中的权重将被更新,使得向量将学会仅保留有用的特征。

先前的隐藏状态将首先乘以可训练权重,然后与重置向量进行逐元素乘法(哈达玛乘积) 。 此操作将决定将先前时间步骤中的哪些信息与新输入一起保留。同时,当前输入还将乘以可训练权重,然后与上面的重置向量和先前隐藏状态的乘积相加。最后,将非线性激活tanh函数应用于最终结果,以获得下面等式中的r 。
r = t a n h ( g a t e r e s e t ⊙ ( W h 1 ⋅ h t − 1 ) + W x 1 ⋅ x t ) r = tanh(gate_{reset} \odot (W_{h_1} \cdot h_{t-1}) + W_{x_1} \cdot x_t) r=tanh(gatereset(Wh1ht1)+Wx1xt)

更新门

接下来,我们必须创建更新门。就像重置门一样,门是使用先前的隐藏状态和当前输入数据来计算的。
在这里插入图片描述
更新和重置门向量都是使用相同的公式创建的,但是,与输入和隐藏状态相乘的权重对于每个门来说是唯一的,这意味着每个门的最终向量是不同的。这使得大门能够满足其特定目的。
g a t e u p d a t e = σ ( W i n p u t u p d a t e ⋅ x t + W h i d d e n u p d a t e ⋅ h t − 1 ) gate_{update} = \sigma(W_{input_{update}} \cdot x_t + W_{hidden_{update}} \cdot h_{t-1}) gateupdate=σ(Winputupdatext+Whiddenupdateht1)
然后,更新向量将与之前的隐藏状态进行逐元素相乘,以获得下面等式中的u ,该值将用于稍后计算我们的最终输出。
u = g a t e u p d a t e ⊙ h t − 1 u = gate_{update} \odot h_{t-1} u=gateupdateht1

稍后在获得最终输出时,更新向量还将用于另一个操作。这里更新门的目的是帮助模型确定存储在先前隐藏状态中的过去信息有多少需要保留以供将来使用。

组合输出

在最后一步中,我们将重用更新门并获取更新的隐藏状态。
在这里插入图片描述
这次,我们将采用同一更新向量(1 -更新 门)的逐元素逆版本,并与重置门的输出r进行逐元素乘法。此操作的目的是让更新门确定新信息的哪一部分应存储在隐藏状态中。

最后,上述操作的结果将与上一步更新门的输出u相加。这将为我们提供新的和更新的隐藏状态。
h t = r ⊙ ( 1 − g a t e u p d a t e ) + u h_t = r \odot (1-gate_{update}) + u ht=r(1gateupdate)+u

我们也可以通过将这个新的隐藏状态传递给线性激活层来将其用作该时间步的输出。

解决梯度消失/爆炸问题

我们已经看到了大门的运作。我们知道它们如何转换我们的数据。现在让我们回顾一下它们在管理网络内存方面的整体作用,并讨论它们如何解决梯度消失/爆炸问题。

正如我们在上面的机制中所看到的,重置门负责决定将先前隐藏状态的哪些部分与当前输入组合以提出新的隐藏状态。

更新门负责确定要保留多少先前的隐藏状态,以及将新提出的隐藏状态(源自重置门)的哪一部分添加到最终隐藏状态。当更新门首先与先前的隐藏状态相乘时,网络会选择将先前隐藏状态的哪些部分保留在内存中,同时丢弃其余部分。随后,当它使用更新门的逆来从重置门过滤提议的新隐藏状态时,它会修补信息的缺失部分。

这使得网络能够保留​​长期的依赖关系。如果更新向量值接近 1,则更新门可以选择保留隐藏状态中的大部分先前记忆,而无需重新计算或更改整个隐藏状态。

在训练 RNN 时,反向传播过程中会出现梯度消失/爆炸问题,特别是当 RNN 处理长序列或具有多层时。训练期间计算的误差梯度用于以正确的方向和正确的幅度更新网络的权重。然而,这个梯度是用链式法则从网络末端开始计算的。因此,在反向传播过程中,梯度将连续进行矩阵乘法,并且对于长序列,梯度会呈指数收缩(消失)或爆炸(爆炸) 。梯度太小意味着模型无法有效更新其权重,而梯度太大会导致模型不稳定。

由于更新门的附加组件,LSTM 和 GRU 中的门有助于解决这个问题。传统 RNN 总是在每个时间步替换隐藏状态的全部内容,而 LSTM 和 GRU 保留大部分现有隐藏状态,同时在其上添加新内容。这允许误差梯度反向传播,而不会由于加法运算而消失或爆炸太快。

虽然 LSTM 和 GRU 是解决上述问题最广泛使用的方法,但梯度爆炸问题的另一种解决方案是梯度裁剪。Clipping 在梯度上设置了一个定义的阈值,这意味着即使在训练过程中梯度增加超过预定义值,其值仍然会被限制在设定的阈值内。这样,梯度的方向不受影响,仅梯度的大小发生变化。
在这里插入图片描述

GRU 与 LSTM

我们已经掌握了 GRU 的机制。但它与它的老兄弟(也更流行)的 LSTM 相比如何呢?

两者都是为了解决标准 RNN 面临的梯度消失/爆炸问题而创建的,并且这两种 RNN 变体都利用门控机制来控制网络内长期和短期依赖关系的流动。

但它们有什么不同呢?

  1. 结构性差异
    虽然 GRU 和 LSTM 都包含门,但这两种结构之间的主要区别在于门的数量及其具体作用。GRU 中的更新门的作用与LSTM 中的输入门和遗忘门非常相似。然而,这两者对添加到网络的新内存内容的控制有所不同。
    在这里插入图片描述
    在 LSTM 中,遗忘门决定保留先前单元状态的哪一部分,而输入门则决定要添加的新内存量。这两个门是相互独立的,这意味着通过输入门 添加的新信息量完全独立于通过遗忘门保留的信息量。
    对于GRU来说,更新门负责决定保留之前内存中的哪些信息,并 负责控制新内存的添加。这意味着 GRU 中先前内存的保留和向内存中添加新信息不是独立的。
    如前所述,这些结构之间的另一个主要区别是 GRU 中缺少单元状态。LSTM 将其长期依赖关系存储在单元状态中,并将短期记忆存储在隐藏状态中,而 GRU 将两者存储在单个隐藏状态中。然而,就保留长期信息的有效性而言,两种架构都已被证明可以有效地实现这一目标。

  2. 速度差异
    与 LSTM 相比,GRU 的训练速度更快,因为训练期间需要更新的权重和参数数量较少。这可以归因于与 LSTM 的三个门相比,GRU 单元中的门数量较少(两个门)。
    在本文后面的代码演练中,我们将直接比较在完全相同的任务上训练 LSTM 和 GRU 的速度。

  3. 性能评估
    模型的准确性,无论是通过误差幅度还是正确分类的比例来衡量,通常是决定使用哪种类型的模型来完成任务时的主要​​因素。GRU 和 LSTM 都是 RNNs 的变体,可以互换插入以获得类似的结果。

项目:使用 GRU 和 LSTM 进行时间序列预测

我们已经了解了 GRU 背后的理论概念。现在是时候将所学知识付诸实践了。

我们将在代码中实现 GRU 模型。为了进一步进行 GRU-LSTM 比较,我们还将使用 LSTM 模型来完成相同的任务。我们将根据一些指标评估这两个模型的性能。我们将使用的数据集是每小时能源消耗数据集,可以在Kaggle上找到。该数据集包含按小时记录的美国不同地区的电力消耗数据,你可以访问 GitHub 代码库。

该项目的目标是创建一个模型,可以根据历史使用数据准确预测下一小时的能源使用情况。我们将使用 GRU 和 LSTM 模型来训练一组历史数据,并在未见过的测试集上评估这两个模型。为此,我们将从特征选择和数据预处理开始,然后定义、训练并最终评估模型。

这将是我们项目的流程。
在这里插入图片描述
我们将使用 PyTorch 库以及数据分析中使用的其他常见 Python 库来实现这两种类型的模型。

import os
import time

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader

from tqdm import tqdm_notebook
from sklearn.preprocessing import MinMaxScaler

# Define data root directory
data_dir = "./data/"
# Visualise how our data looks
pd.read_csv(data_dir + 'AEP_hourly.csv').head()

在这里插入图片描述
我们总共有12 个 .csv文件,其中包含上述格式的每小时能源趋势数据(未使用“est_hourly.paruqet”和“pjm_hourly_est.csv” )。在下一步中,我们将按以下顺序读取这些文件并预处理这些数据:

  • 获取每个单独时间步的时间数据并对它们进行概括
    • 一天中的某个小时,即 0 - 23
    • 一周中的某一天,即。1 - 7
    • 月份,即 1 - 12
    • 一年中的某一天,即 1 - 365
  • 将数据缩放到 0 到 1 之间的值
    • 当特征具有相对相似的规模和/或接近正态分布时,算法往往会表现更好或收敛得更快
    • 缩放保留了原始分布的形状并且不会降低异常值的重要性
  • 将数据分组为序列,用作模型的输入并存储其相应的标签
    • 序列长度或回顾周期是模型用于进行预测的历史数据点的数量
    • 标签将是输入序列中最后一个数据点之后的下一个数据点
  • 将输入和标签拆分为训练集和测试集
# The scaler objects will be stored in this dictionary so that our output test data from the model can be re-scaled during evaluation
label_scalers = {}

train_x = []
test_x = {}
test_y = {}

for file in tqdm_notebook(os.listdir(data_dir)): 
    # Skipping the files we're not using
    if file[-4:] != ".csv" or file == "pjm_hourly_est.csv":
        continue
    
    # Store csv file in a Pandas DataFrame
    df = pd.read_csv('{}/{}'.format(data_dir, file), parse_dates=[0])
    # Processing the time data into suitable input formats
    df['hour'] = df.apply(lambda x: x['Datetime'].hour,axis=1)
    df['dayofweek'] = df.apply(lambda x: x['Datetime'].dayofweek,axis=1)
    df['month'] = df.apply(lambda x: x['Datetime'].month,axis=1)
    df['dayofyear'] = df.apply(lambda x: x['Datetime'].dayofyear,axis=1)
    df = df.sort_values("Datetime").drop("Datetime",axis=1)
    
    # Scaling the input data
    sc = MinMaxScaler()
    label_sc = MinMaxScaler()
    data = sc.fit_transform(df.values)
    # Obtaining the Scale for the labels(usage data) so that output can be re-scaled to actual value during evaluation
    label_sc.fit(df.iloc[:,0].values.reshape(-1,1))
    label_scalers[file] = label_sc
    
    # Define lookback period and split inputs/labels
    lookback = 90
    inputs = np.zeros((len(data)-lookback,lookback,df.shape[1]))
    labels = np.zeros(len(data)-lookback)
    
    for i in range(lookback, len(data)):
        inputs[i-lookback] = data[i-lookback:i]
        labels[i-lookback] = data[i,0]
    inputs = inputs.reshape(-1,lookback,df.shape[1])
    labels = labels.reshape(-1,1)
    
    # Split data into train/test portions and combining all data from different files into a single array
    test_portion = int(0.1*len(inputs))
    if len(train_x) == 0:
        train_x = inputs[:-test_portion]
        train_y = labels[:-test_portion]
    else:
        train_x = np.concatenate((train_x,inputs[:-test_portion]))
        train_y = np.concatenate((train_y,labels[:-test_portion]))
    test_x[file] = (inputs[-test_portion:])
    test_y[file] = (labels[-test_portion:])

我们总共有 980,185 个训练数据序列。

为了提高训练速度,我们可以批量处理数据,这样模型就不需要频繁更新权重。Torch Dataset和DataLoader类对于将数据拆分为批次并对其进行混洗非常有用。

batch_size = 1024
train_data = TensorDataset(torch.from_numpy(train_x), torch.from_numpy(train_y))
train_loader = DataLoader(train_data, shuffle=True, batch_size=batch_size, drop_last=True)

我们还可以检查是否有 GPU 来加快训练时间。如果您使用带有 GPU 的 FloydHub 来运行此代码,训练时间将显着减少。

# torch.cuda.is_available() checks and returns a Boolean True if a GPU is available, else it'll return False
is_cuda = torch.cuda.is_available()

# If we have a GPU available, we'll set our device to GPU. We'll use this device variable later in our code.
if is_cuda:
    device = torch.device("cuda")
else:
    device = torch.device("cpu")

接下来,我们将定义 GRU 和 LSTM 模型的结构。两种模型具有相同的结构,唯一的区别是循环层(GRU/LSTM)和隐藏状态的初始化。LSTM 的隐藏状态是包含单元状态和隐藏状态 的元组,而 GRU 仅具有单个隐藏状态。

class GRUNet(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim, n_layers, drop_prob=0.2):
        super(GRUNet, self).__init__()
        self.hidden_dim = hidden_dim
        self.n_layers = n_layers
        
        self.gru = nn.GRU(input_dim, hidden_dim, n_layers, batch_first=True, dropout=drop_prob)
        self.fc = nn.Linear(hidden_dim, output_dim)
        self.relu = nn.ReLU()
        
    def forward(self, x, h):
        out, h = self.gru(x, h)
        out = self.fc(self.relu(out[:,-1]))
        return out, h
    
    def init_hidden(self, batch_size):
        weight = next(self.parameters()).data
        hidden = weight.new(self.n_layers, batch_size, self.hidden_dim).zero_().to(device)
        return hidden

class LSTMNet(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim, n_layers, drop_prob=0.2):
        super(LSTMNet, self).__init__()
        self.hidden_dim = hidden_dim
        self.n_layers = n_layers
        
        self.lstm = nn.LSTM(input_dim, hidden_dim, n_layers, batch_first=True, dropout=drop_prob)
        self.fc = nn.Linear(hidden_dim, output_dim)
        self.relu = nn.ReLU()
        
    def forward(self, x, h):
        out, h = self.lstm(x, h)
        out = self.fc(self.relu(out[:,-1]))
        return out, h
    
    def init_hidden(self, batch_size):
        weight = next(self.parameters()).data
        hidden = (weight.new(self.n_layers, batch_size, self.hidden_dim).zero_().to(device),
                  weight.new(self.n_layers, batch_size, self.hidden_dim).zero_().to(device))
        return hidden

训练过程在下面的函数中定义,以便我们可以为两个模型重现它。两个模型在隐藏状态和层中将具有相同数量的维度,在相同数量的epoch和学习率上进行训练,并在完全相同的数据集上进行训练和测试。

为了比较两个模型的性能,我们将跟踪模型训练所需的时间,并最终比较两个模型在测试集上的最终准确性。对于我们的准确性测量,我们将使用对称平均绝对百分比误差(sMAPE)来评估模型。sMAPE是预测值和实际值之间的绝对差值除以预测值和实际值的平均值的总和,从而给出衡量误差量的百分比。这是sMAPE的公式:
$$

$$

def train(train_loader, learn_rate, hidden_dim=256, EPOCHS=5, model_type="GRU"):
    
    # Setting common hyperparameters
    input_dim = next(iter(train_loader))[0].shape[2]
    output_dim = 1
    n_layers = 2
    # Instantiating the models
    if model_type == "GRU":
        model = GRUNet(input_dim, hidden_dim, output_dim, n_layers)
    else:
        model = LSTMNet(input_dim, hidden_dim, output_dim, n_layers)
    model.to(device)
    
    # Defining loss function and optimizer
    criterion = nn.MSELoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=learn_rate)
    
    model.train()
    print("Starting Training of {} model".format(model_type))
    epoch_times = []
    # Start training loop
    for epoch in range(1,EPOCHS+1):
        start_time = time.clock()
        h = model.init_hidden(batch_size)
        avg_loss = 0.
        counter = 0
        for x, label in train_loader:
            counter += 1
            if model_type == "GRU":
                h = h.data
            else:
                h = tuple([e.data for e in h])
            model.zero_grad()
            
            out, h = model(x.to(device).float(), h)
            loss = criterion(out, label.to(device).float())
            loss.backward()
            optimizer.step()
            avg_loss += loss.item()
            if counter%200 == 0:
                print("Epoch {}......Step: {}/{}....... Average Loss for Epoch: {}".format(epoch, counter, len(train_loader), avg_loss/counter))
        current_time = time.clock()
        print("Epoch {}/{} Done, Total Loss: {}".format(epoch, EPOCHS, avg_loss/len(train_loader)))
        print("Total Time Elapsed: {} seconds".format(str(current_time-start_time)))
        epoch_times.append(current_time-start_time)
    print("Total Training Time: {} seconds".format(str(sum(epoch_times))))
    return model

def evaluate(model, test_x, test_y, label_scalers):
    model.eval()
    outputs = []
    targets = []
    start_time = time.clock()
    for i in test_x.keys():
        inp = torch.from_numpy(np.array(test_x[i]))
        labs = torch.from_numpy(np.array(test_y[i]))
        h = model.init_hidden(inp.shape[0])
        out, h = model(inp.to(device).float(), h)
        outputs.append(label_scalers[i].inverse_transform(out.cpu().detach().numpy()).reshape(-1))
        targets.append(label_scalers[i].inverse_transform(labs.numpy()).reshape(-1))
    print("Evaluation Time: {}".format(str(time.clock()-start_time)))
    sMAPE = 0
    for i in range(len(outputs)):
        sMAPE += np.mean(abs(outputs[i]-targets[i])/(targets[i]+outputs[i])/2)/len(outputs)
    print("sMAPE: {}%".format(sMAPE*100))
    return outputs, targets, sMAPE
lr = 0.001
gru_model = train(train_loader, lr, model_type="GRU")
Lstm_model = train(train_loader, lr, model_type="LSTM")
[Out]: Starting Training of GRU model
       Epoch 1......Step: 200/957....... Average Loss for Epoch: 0.0070570480596506965
       Epoch 1......Step: 400/957....... Average Loss for Epoch: 0.0039001358837413135
       Epoch 1......Step: 600/957....... Average Loss for Epoch: 0.0027501484048358784
       Epoch 1......Step: 800/957....... Average Loss for Epoch: 0.0021489552696584723
       Epoch 1/5 Done, Total Loss: 0.0018450273993545988
       Time Elapsed for Epoch: 78.02232400000003 seconds
        .
        .
        .
       Total Training Time: 390.52727700000037 seconds
       
       Starting Training of LSTM model
       Epoch 1......Step: 200/957....... Average Loss for Epoch: 0.013630141295143403
        .
        .
        .
       Total Training Time: 462.73371699999984 seconds

从两个模型的训练时间可以看出,我们的弟弟妹妹在速度方面绝对击败了哥哥。GRU 模型在这个维度上是明显的赢家;它比 LSTM 模型快 72 秒完成了 5 个训练周期。

继续测量两个模型的准确性,我们现在将使用评估()函数和测试数据集。

gru_outputs, targets, gru_sMAPE = evaluate(gru_model, test_x, test_y, label_scalers)
[Out]: Evaluation Time: 1.982479000000012
       sMAPE: 0.28081167222194775%
lstm_outputs, targets, lstm_sMAPE = evaluate(lstm_model, test_x, test_y, label_scalers)
[Out]: Evaluation Time: 2.602886000000126
       sMAPE: 0.27014616762377464%

有趣,对吧?虽然 LSTM 模型的误差可能较小,并且在性能准确度方面略微领先于 GRU 模型,但差异并不显着,因此尚无定论。

比较这两个模型的其他测试同样没有得出明显的胜利者,无法确定哪个是更好的整体架构。

最后,让我们对预测输出与实际消耗数据的随机集进行一些可视化。

plt.figure(figsize=(14,10))
plt.subplot(2,2,1)
plt.plot(gru_outputs[0][-100:], "-o", color="g", label="Predicted")
plt.plot(targets[0][-100:], color="b", label="Actual")
plt.ylabel('Energy Consumption (MW)')
plt.legend()

plt.subplot(2,2,2)
plt.plot(gru_outputs[8][-50:], "-o", color="g", label="Predicted")
plt.plot(targets[8][-50:], color="b", label="Actual")
plt.ylabel('Energy Consumption (MW)')
plt.legend()

plt.subplot(2,2,3)
plt.plot(gru_outputs[4][:50], "-o", color="g", label="Predicted")
plt.plot(targets[4][:50], color="b", label="Actual")
plt.ylabel('Energy Consumption (MW)')
plt.legend()

plt.subplot(2,2,4)
plt.plot(lstm_outputs[6][:100], "-o", color="g", label="Predicted")
plt.plot(targets[6][:100], color="b", label="Actual")
plt.ylabel('Energy Consumption (MW)')
plt.legend()
plt.show()

在这里插入图片描述
看起来这些模型在预测能源消耗趋势方面基本上是成功的。虽然他们可能仍然会犯一些错误,例如延迟预测消耗下降,但预测非常接近测试集上的实际线。这是由于能源消耗数据的性质以及模型可以解释的模式和周期性变化这一事实造成的。更困难的时间序列预测问题,例如股票价格预测或销量预测,可能具有很大程度上随机的数据或没有可预测的模式,在这种情况下,准确性肯定会较低。

超越 GRU

正如我在LSTM 文章中提到的,多年来,RNN 及其变体在各种 NLP 任务中已被取代,不再是NLP 标准架构。预训练的Transformer 模型(例如 Google 的 BERT、OpenAI 的 GPT和最近推出的 XLNet)已经产生了最先进的基准和结果,并将下游任务的迁移学习引入到 NLP。

至此,GRU 的所有事情就暂时结束了。这篇文章完成了我涵盖 RNN 基础知识的系列文章;未来,我们将探索更先进的概念,例如注意力机制、Transformers 和 NLP 中最先进的技术。

本博客译自 Gabriel Loye的博文。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HCIP-七、IS-IS 综合实验

七、IS-IS 综合实验 实验拓扑实验需求及解法1.如图所示,配置所有路由器的接口IP地址。2.运行IS-IS,进程号13.IS-IS优化4.路径优化 实验拓扑 实验需求及解法 本实验模拟IS-IS综合网络,完成以下需求: 1.如图所示,配置所…

Python模块之yaml:简化配置与数据解析

更多Python学习内容:ipengtao.com YAML(YAML Aint Markup Language)是一种人类可读的数据序列化格式,常用于配置文件和数据传输。在Python中,可以使用PyYAML模块来处理YAML格式的数据。本文将深入介绍PyYAML的基础用法…

C++学习之路(二)C++如何实现一个超简单的学生信息管理系统?C++示例和小项目实例

这个示例实现了一个简单的学生信息管理系统。它包括了学生类的定义,可以添加学生信息、显示所有学生信息,将学生信息保存到文件并从文件加载信息。通过这个示例,你可以了解到如何使用类、函数和文件操作来构建一个基本的信息管理系统。 一个简…

基于springboot实现大学生就业服务平台系统项目【项目源码】计算机毕业设计

基于springboot实现大学生就业服务平台系统演示 Java技术 Java是由SUN公司推出,该公司于2010年被oracle公司收购。Java本是印度尼西亚的一个叫做爪洼岛的英文名称,也因此得来java是一杯正冒着热气咖啡的标识。Java语言在移动互联网的大背景下具备了显著…

OSG编程指南<九>:坐标系统及坐标系变换

1、坐标系统 坐标系是一个精确定位对象位置的框架,所有的图形变换都是基于一定的坐标系进行的。对于从事计算机图形学的研究者,掌握图形变换是不可或缺的,因此,理解坐标系非常重要。一个三维图形工作者可以认为自己站在一定的坐标…

【初始前后端交互+原生Ajax+Fetch+axios+同源策略+解决跨域】

初始前后端交互原生AjaxFetchaxios同源策略解决跨域 1 初识前后端交互2 原生Ajax2.1 Ajax基础2.2 Ajax案例2.3 ajax请求方式 3 Fetch3.1 fetch基础3.2 fetch案例 4 axios4.1 axios基础4.2 axios使用4.2.1 axios拦截器4.2.2 axios中断器 5 同源策略6 解决跨域6.1 jsonp6.2 其他技…

【每日一题】2824. 统计和小于目标的下标对数目-2023.11.24

题目&#xff1a; 2824. 统计和小于目标的下标对数目 给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 target &#xff0c;请你返回满足 0 < i < j < n 且 nums[i] nums[j] < target 的下标对 (i, j) 的数目。 示例 1&#xff1a; 输入&#xff1…

Digicert通配符证书:满足你的所有需求

通配符证书是一种可以保护一个主域名及其所有子域名的SSL证书。这意味着&#xff0c;如果你有一个主域名&#xff0c;比如www.example.com&#xff0c;并且你有多个子域名&#xff0c;比如blog.example.com、store.example.com等&#xff0c;那么只需要一个通配符证书&#xff…

ubuntu22.04系统下载程序和依赖,并拷贝到指定路径下

脚本1 apt install aptitude apt-get -d install xxx #xxx是待下载的安装包 mv /var/cache/apt/archives/* /home/tuners/1apt install aptitude apt-get -d install xxx mv /var/cache/apt/archives/*.deb /home/tuners/1 xxx 为程序包名称 /home/tuners/1为保存程序包的…

阿里云windwos 安装oracle数据库,外部用工具连接不上,只能在服务器本机通过127.0.0.1 连接

1. 首先检查阿里云服务器安全组端口是否开放 oracle 数据库端口 2. 其次找到oracle 安装的目录&#xff0c;打开这俩个文件&#xff0c;将localhost 修改为 服务器本机名称 3.重启oracle 监听服务&#xff0c;就可以连接了

IBM SPSS Statistics 27 Mac(统计分析软件)

IBM SPSS Statistics是一款数据分析和统计建模软件&#xff0c;它专为研究人员、分析师和商业用户设计。该软件能够帮助用户对大量数据进行分析和预测&#xff0c;帮助用户制定决策和解决问题。 IBM SPSS Statistics提供了丰富的统计分析功能&#xff0c;包括描述性统计、相关性…

【鸿蒙应用ArkTS开发系列】- 云开发入门实战二 实现省市地区三级联动地址选择器组件(下)

文章目录 概述端云调用流程端侧集成AGC SDK端侧省市地区联动的地址选择器组件开发创建省市数据模型创建省市地区视图UI子组件创建页面UI视图Page文件 打包测试总结 概述 我们在前面的课程&#xff0c;对云开发的入门做了介绍&#xff0c;以及使用一个省市地区联动的地址选择器…

IDM(Internet Download Manager)PC版提升下载速度与效率的利器

你是否曾经因为下载速度慢而感到烦恼&#xff1f;或者在下载大型文件时&#xff0c;经历了长时间的等待&#xff1f;如果你有这样的困扰&#xff0c;那么IDM&#xff08;Internet Download Manager&#xff09;就是你的救星&#xff01; IDM是一款高效、实用的下载管理器&…

PS 计数工具 基础使用方式讲解

上文PS 注释工具 基础使用方法讲解 中 我们讲了注释工具 解析来 我们来看这个计数工具 这里 我们换一张图像 如果 我要你数清楚 这个图上有几个咖啡豆 你能数清楚吗&#xff1f; 哈哈 其实也不难 不是特别大 但是 例如很多 且无规则物品时 我们可能就会数乱 左上角属性的话 我…

vue2【组件的构成】

目录 1&#xff1a;什么是组件化开发 2&#xff1a;vue中的组件化开发 3&#xff1a;vue组件的三个组成部分 4&#xff1a;组件中定义方法&#xff0c;监听器&#xff0c;过滤器&#xff0c;计算属性节点。 5&#xff1a;template中只允许唯一根节点&#xff0c;style默认…

arp报文及使用go实现

一、ARP协议报文格式及ARP表 ARP&#xff08;Address Resolution Protocal&#xff0c;地址解析协议&#xff09;是将IP地址解析为以太网的MAC地址&#xff08;或者称为物理地址&#xff09;的协议。在局域网中&#xff0c;当主机或其他网络设备有数据要发送给另一个主机或设备…

【2023年APMCM亚太杯C题】完整数据与解题思路

2023年亚太杯C题 数据下载与搜集重点数据其余数据第一问第二问第三问第四问第五问第六问 数据与思路获取 数据下载与搜集 该题并没有提供数据集&#xff0c;对所需数据进行收集整理是对题目进行求解的基础。在本题中&#xff0c;主要需要以下数据&#xff1a;新能源汽车历史销…

(免费领源码)java#springboot#mysql流浪动物救助系统78174-计算机毕业设计项目选题推荐

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

知行之桥EDI系统HTTP签名验证

本文简要概述如何在知行之桥EDI系统中使用 HTTP 签名身份验证&#xff0c;并将使用 CyberSource 作为该集成的示例。 API 概述 首字母缩略词 API 代表“应用程序编程接口”。这听起来可能很复杂&#xff0c;但真正归结为 API 是一种允许两个不同实体相互通信的软件。自开发以…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于外部性理论的网侧储能成本疏导机制研究》

这个标题涉及到一个关于储能的研究&#xff0c;主要聚焦在基于外部性理论的网侧&#xff08;电网侧&#xff09;储能成本疏导机制上。 基于外部性理论&#xff1a; 这表明研究的框架或者理论基础是"外部性理论"。外部性是指某个经济活动的影响不仅限于直接参与者&…