使用Pytorch从零开始构建GRU

门控循环单元 (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 timeimport numpy as np
import pandas as pd
import matplotlib.pyplot as pltimport torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoaderfrom 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 usingif file[-4:] != ".csv" or file == "pjm_hourly_est.csv":continue# Store csv file in a Pandas DataFramedf = pd.read_csv('{}/{}'.format(data_dir, file), parse_dates=[0])# Processing the time data into suitable input formatsdf['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 datasc = 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 evaluationlabel_sc.fit(df.iloc[:,0].values.reshape(-1,1))label_scalers[file] = label_sc# Define lookback period and split inputs/labelslookback = 90inputs = 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 arraytest_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_dimself.n_layers = n_layersself.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, hdef init_hidden(self, batch_size):weight = next(self.parameters()).datahidden = weight.new(self.n_layers, batch_size, self.hidden_dim).zero_().to(device)return hiddenclass 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_dimself.n_layers = n_layersself.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, hdef init_hidden(self, batch_size):weight = next(self.parameters()).datahidden = (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 hyperparametersinput_dim = next(iter(train_loader))[0].shape[2]output_dim = 1n_layers = 2# Instantiating the modelsif 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 optimizercriterion = 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 loopfor epoch in range(1,EPOCHS+1):start_time = time.clock()h = model.init_hidden(batch_size)avg_loss = 0.counter = 0for x, label in train_loader:counter += 1if model_type == "GRU":h = h.dataelse: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 modeldef 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 = 0for 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 modelEpoch 1......Step: 200/957....... Average Loss for Epoch: 0.0070570480596506965Epoch 1......Step: 400/957....... Average Loss for Epoch: 0.0039001358837413135Epoch 1......Step: 600/957....... Average Loss for Epoch: 0.0027501484048358784Epoch 1......Step: 800/957....... Average Loss for Epoch: 0.0021489552696584723Epoch 1/5 Done, Total Loss: 0.0018450273993545988Time Elapsed for Epoch: 78.02232400000003 seconds...Total Training Time: 390.52727700000037 secondsStarting Training of LSTM modelEpoch 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.982479000000012sMAPE: 0.28081167222194775%
lstm_outputs, targets, lstm_sMAPE = evaluate(lstm_model, test_x, test_y, label_scalers)
[Out]: Evaluation Time: 2.602886000000126sMAPE: 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.mzph.cn/news/165987.shtml

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

相关文章

安索夫矩阵(ANSOFF)

👉安索夫矩阵是策略管理之父安索夫博士于1957年提出的营销策略模型,该模型以“产品”和“市场”作为两大基本面,提出了4种不同组合下的营销策略,是营销分析中应用最广泛的工具之一。其主要逻辑是通过选择4种不同的成长性策略来实现…

新苹果手机如何导入旧手机数据?解决方案来了,记得收藏!

为了保持其竞争优势,苹果公司不断推出新的产品和服务,因此苹果手机的更新换代速度是比较快的。正巧最近刚出了iPhone15,相信很多小伙伴已经换上了期待已久的新手机。 更换新手机后,大家都会面临一个问题:新苹果手机如…

java 手机商城免费搭建+电商源码+小程序+三级分销+SAAS云平台

【SAAS云平台】打造全行业全渠道全场景的SaaS产品,为店铺经营场景提供一体化解决方案;门店经营区域化、网店经营一体化,本地化、全方位、一站式服务,为多门店提供统一运营解决方案;提供丰富多样的营销玩法覆盖所有经营…

如何预防数据泄露?六步策略帮您打造企业信息安全壁垒

大家好!我是恒小驰,今天我想和大家聊聊一个非常重要的话题——如何预防数据泄露。在这个数字化的时代,数据已经成为了我们生活中不可或缺的一部分。然而,随着数据的价值日益凸显,数据泄露的风险也随之增加。企业应该如…

智能医疗越发周到!新的机器人系统评估中风后的活动能力

原创 | 文 BFT机器人 中风是在医疗界上最难的解决的病例之一,全球每年有超过1500万人中风,四分之三的中风患者的手臂和手部会出现损伤、虚弱和瘫痪。 许多中风患者日常生活是依靠他们强壮的手臂来完成的,从拿一些小东西到梳头,即…

phpstudy和IDEA 配置php debug

1.安装xdebug 扩展,phpinfo() 查看 2.配置php.ini zend_extensionD:/phpstudy_pro/Extensions/php/php7.4.3nts/ext/php_xdebug.dll xdebug.collect_params1 xdebug.collect_return1 xdebug.auto_traceOn xdebug.trace_output_dirD:/phpstudy_pro/Extensions/php_l…

c语言新龟兔赛跑

以下是一个使用C语言编写的新的龟兔赛跑游戏&#xff1a; #include <stdio.h>#include <stdlib.h>#include <time.h>int main() { int distance, turtle_speed, rabbit_speed, turtle_time, rabbit_time, rabbit_lead; srand(time(NULL)); // 随机数种…

Whatweb简单使用

目录 简介 安装 debian/ubtuntu redhat/centos 特性 使用 常用参数如下&#xff1a; whatweb -v whatweb --version whatweb -i 1.txt whatweb -v www.baidu.com 扫描等级 whatweb -a 4 www.baidu.com 扫描网段 whatweb --no-errors -t 255 192.168.71.0/24 导出…

在vscode中添加代码提示

添加配置 run->add_configuration 添加头文件路径 在c_cpp_properties.json中添加头文件路径 效果

时间敏感网络TSN的车载设计实践: 802.1Qbv协议

▎概述 IEEE 802.1Qbv[1]是TSN系列协议中备受关注的技术之一&#xff0c;如图1所示&#xff0c;它定义了一种时间感知整形器&#xff08;Time Aware Shaper&#xff0c;TAS&#xff09;&#xff0c;支持Qbv协议的交换机可以按照配置好的门控列表来打开/关闭交换机出口队列&…

2023软件应用类下载系统平台源码/手机软件应用、新闻资讯下载站/软件库网站源码

源码简介&#xff1a; 这个是最新软件应用类平台源码、手机应用下载系统源码、软件应用市场下载站源码、新闻资讯软件下载。2023软件应用类平台源码/手机软件应用、新闻资讯下载站&#xff0c;它是软件库网站源码。 最新软件应用类平台源码 手机应用下载系统源码 软件应用市场…

NX二次开发UF_CURVE_add_faces_ocf_data 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_add_faces_ocf_data Defined in: uf_curve.h int UF_CURVE_add_faces_ocf_data(tag_t face_tag, UF_CURVE_ocf_data_p_t uf_offset_data ) overview 概述 Add a face col…

JVMj之console Java监视与管理控制台

jconsole Java监视与管理控制台 1、jconsole介绍 jconsole (java monitoring and management console)是一款基于JMX (Java Management Extensions) 的可视化监视和管理工具。 2、启动jconsole 1、在linux和windwos下通过jconsole启动即可。 2、然后会自动搜索本机运行的…

【开源】基于Vue和SpringBoot的独居老人物资配送系统

项目编号&#xff1a; S 045 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S045&#xff0c;文末获取源码。} 项目编号&#xff1a;S045&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询社区4…

Linux(6):文件与文件系统的压缩,打包与备份

压缩文件的用途与技术 由于 1 byte 8 bits &#xff0c;所以每个byte当中会有8个空格&#xff0c;而每个空格可以是0,1。 其实文件里面有相当多的『空间』存在&#xff0c;并不是完全填满的&#xff0c;而『压缩』的技术就是将这些『空间』填满&#xff0c;以让整个文件占用…

微信小程序商城实例mpvue-xbyjShop-master(附精选源码32套,涵盖商城团购等)

mpvue-xbyjShop 基于mpvue的微信小程序商城&#xff08;小程序端&#xff0c;服务端&#xff09; 小程序端 技术栈 mpvue mpvue-router-patch mpvue-entry vuex webpack ES6/7 flyio mpvue-wxparse 项目运行 微信开发中工具选中mpvue-xbyjShop/buyer作为项目目录即可功…

JavaScript的学习

HTML的学习-CSDN博客 从html的学习中 其实我已经用到了 JavaScript的脚本 &#xff08;GPT&#xff09; 例如 echo <script>alert("账号密码错误"); window.location"index.html";</script>; 弹窗 然后定位到 index.html 这里能够让我们更…

品牌线上窜货查的出来吗

如果窜货不治理会出现什么局面&#xff0c;显见的当然是渠道混乱&#xff0c;低价丛生&#xff0c;严重的还会导致真假混卖&#xff0c;最后所有的后果都会由品牌承担&#xff0c;口碑的影响是必然的&#xff0c;那品牌的衰败也会是一种趋势&#xff0c;所以治理窜货是品牌发展…

Windows | 模仿网易云任务栏实现自定义按钮及缩略图

前言 最近更新网易云发现任务栏按钮中除了播放相关的按钮&#xff0c;多了一个喜欢的按钮&#xff1a; 之前我一直以为网易云任务栏的按钮只是 Windows 为音乐软件专门提供的&#xff0c;于是我又看了一眼系统自带的播放器&#xff0c;发现并没有爱心按钮&#xff1a; 这时我就…