[神经网络与深度学习]RNN,GRU,LSTM,GAN,Transformer
一:循环神经网络(RNN)
对隐状态使用循环计算的神经网络称为循环神经网络。
当前时间步隐藏变量计算公式如下:

输出层的输出计算公式如下:

请务必注意,在每一个不同的时间步中(注意:原文中的"即使"已经足够强调这一意思),循环神经网络始终采用相同的模型参数(注意:原文中的"这些模型参数"已经足够明确具体)。由此可见,在处理更多的时间步时(注意:此处"处理"一词比原文中的"使用"更具动态感),循环神经网络的参数开销不会因时间步数的增多而上升(注意:此处"开销"一词比原文中的"消耗"更具技术性)。
RNN的模型如下:

RNN代码实现:
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
# 构建具有256个隐藏单元的单隐藏层RNN
num_hiddens = 256
rnn_layer = nn.RNN(len(vocab), num_hiddens)
class RNNModel(nn.Module):
"""循环神经网络模型"""
def __init__(self, rnn_layer, vocab_size, **kwargs):
super(RNNModel, self).__init__(**kwargs)
self.rnn = rnn_layer
self.vocab_size = vocab_size
self.num_hiddens = self.rnn.hidden_size
# 如果RNN是双向的(之后将介绍),num_directions应该是2,否则应该是1
if not self.rnn.bidirectional:
self.num_directions = 1
self.linear = nn.Linear(self.num_hiddens, self.vocab_size)
else:
self.num_directions = 2
self.linear = nn.Linear(self.num_hiddens * 2, self.vocab_size)
def forward(self, inputs, state):
X = F.one_hot(inputs.T.long(), self.vocab_size)
X = X.to(torch.float32)
Y, state = self.rnn(X, state)
# 全连接层首先将Y的形状改为(时间步数*批量大小,隐藏单元数)
# 它的输出形状是(时间步数*批量大小,词表大小)。
output = self.linear(Y.reshape((-1, Y.shape[-1])))
return output, state
def begin_state(self, device, batch_size=1):
if not isinstance(self.rnn, nn.LSTM):
# nn.GRU以张量作为隐状态
return torch.zeros((self.num_directions * self.rnn.num_layers,
batch_size, self.num_hiddens),
device=device)
else:
# nn.LSTM以元组作为隐状态
return (torch.zeros((
self.num_directions * self.rnn.num_layers,
batch_size, self.num_hiddens), device=device),
torch.zeros((
self.num_directions * self.rnn.num_layers,
batch_size, self.num_hiddens), device=device))
device = d2l.try_gpu()
net = RNNModel(rnn_layer, vocab_size=len(vocab))
net = net.to(device)
num_epochs, lr = 500, 1
d2l.train_ch8(net, train_iter, vocab, lr, num_epochs, device)
python

二:门控循环单元(GRU)
改写说明
GRU的结构如下:

代码实现GRU模型:
gru_layer = nn.GRU(num_inputs, num_hiddens)
python
三:长短期记忆网络(LSTM)
改写说明
如下图所示,在LSTM架构中包含了输入门、遗忘门以及输出门这三个关键组成部分;这些门均通过sigmoid激活函数进行处理;因此其取值范围在(0,1)区间内。

计算公式如下:

输入门中的it确定传送带

的哪些值被更新,更新之后的新值

加到

上,然后更新传送带。

从网络结构来看,LSTM的参数量是RNN的四倍,输入和输出都与RNN相同。
LSTM的代码实现:
lstm_layer = nn.LSTM(num_inputs, num_hiddens)
python
四:生成式对抗网络(GAN)
基于对抗网络(GAN)包含两个神经网络:一个是用于推断出真实数据集的概率分布模型(即生成模型),另一个则是用于判定一个给定样本是否来源于真实数据而非由生成模型产生的假数据(即判别模型)。经过训练后能够判断的数据特征会在两种不同的概率空间中得到映射;而通过对抗学习策略,在对抗过程中不断优化自身性能:最终使得生成模型能够在某种程度上欺骗判别模型完成任务目标。
GAN的网络架构:

生成器 G 接收隐空间中的随机向量作为输入,并通过深度学习网络架构产生与真实数据相似的样本。鉴别器 D 接收来自训练数据集的真数据样本以及由 G 生成的假数据样本,并评估这些输入是否来自于真实训练集的概率分布。当输入的实际训练样本时返回 1;当输入生成的数据时返回 0。
五:Transformer
Transformer架构完全依赖于注意力机制,并没有使用任何卷积层或循环神经网络层。
Transformer模型架构:

Transformer主要由编码器与解码器这两个主要组件构成;其中编码部分与解码部分均基于自注意力机制通过模块化的方式进行堆叠;而编码部分则由多个相同的编码层通过堆叠的方式构成;每个编码层又包含两个子模块:一个是多头自注意机制以及基于位置的信息传递网络;另一个则辅以_(此处应保留原有符号)_;而对于解码部分同样由多个相同的层次结构构成;并且在每一层次中都采用了残差连接与归一化处理

计算注意力机制:
通过每个编码器的输入向量创建三个子空间:Q为查询空间,K为键空间,V为值空间。
确定分数,在编码单词的过程中有多关注句子的其他部分是由这些分数决定的。
3.将分数除以8,然后通过softmax传递结果,使得到的分数都是正值且和为1。
4.每个值向量乘以softmax分数。
5.对加权值向量求和然后即得到自注意力层在该位置的输出。
所有步骤合并为如下计算公式:

多头注意力机制增强了模型对不同位置信息的捕捉能力,并为注意力层提供了多个子空间表达。计算多头注意力机制涉及八个独立的权重矩阵计算,并最终产生八个不同的Z矩阵。
多头注意力机制代码实现:
import math
import torch
from torch import nn
class MultiHeadAttention(nn.Module):
"""多头注意力"""
def __init__(self, key_size, query_size, value_size, num_hiddens,
num_heads, dropout, bias=False, **kwargs):
super(MultiHeadAttention, self).__init__(**kwargs)
self.num_heads = num_heads
self.attention = d2l.DotProductAttention(dropout)
self.W_q = nn.Linear(query_size, num_hiddens, bias=bias)
self.W_k = nn.Linear(key_size, num_hiddens, bias=bias)
self.W_v = nn.Linear(value_size, num_hiddens, bias=bias)
self.W_o = nn.Linear(num_hiddens, num_hiddens, bias=bias)
def forward(self, queries, keys, values, valid_lens):
# queries,keys,values的形状:
# (batch_size,查询或者“键-值”对的个数,num_hiddens)
# valid_lens 的形状:
# (batch_size,)或(batch_size,查询的个数)
# 经过变换后,输出的queries,keys,values 的形状:
# (batch_size*num_heads,查询或者“键-值”对的个数,
# num_hiddens/num_heads)
queries = transpose_qkv(self.W_q(queries), self.num_heads)
keys = transpose_qkv(self.W_k(keys), self.num_heads)
values = transpose_qkv(self.W_v(values), self.num_heads)
if valid_lens is not None:
# 在轴0,将第一项(标量或者矢量)复制num_heads次,
# 然后如此复制第二项,然后诸如此类。
valid_lens = torch.repeat_interleave(
valid_lens, repeats=self.num_heads, dim=0)
# output的形状:(batch_size*num_heads,查询的个数,
# num_hiddens/num_heads)
output = self.attention(queries, keys, values, valid_lens)
# output_concat的形状:(batch_size,查询的个数,num_hiddens)
output_concat = transpose_output(output, self.num_heads)
return self.W_o(output_concat)
def transpose_qkv(X, num_heads):
"""为了多注意力头的并行计算而变换形状"""
# 输入X的形状:(batch_size,查询或者“键-值”对的个数,num_hiddens)
# 输出X的形状:(batch_size,查询或者“键-值”对的个数,num_heads,
# num_hiddens/num_heads)
X = X.reshape(X.shape[0], X.shape[1], num_heads, -1)
# 输出X的形状:(batch_size,num_heads,查询或者“键-值”对的个数,
# num_hiddens/num_heads)
X = X.permute(0, 2, 1, 3)
# 最终输出的形状:(batch_size*num_heads,查询或者“键-值”对的个数,
# num_hiddens/num_heads)
return X.reshape(-1, X.shape[2], X.shape[3])
#@save
def transpose_output(X, num_heads):
"""逆转transpose_qkv函数的操作"""
X = X.reshape(-1, num_heads, X.shape[1], X.shape[2])
X = X.permute(0, 2, 1, 3)
return X.reshape(X.shape[0], X.shape[1], -1)
num_hiddens, num_heads = 100, 5
attention = MultiHeadAttention(num_hiddens, num_hiddens, num_hiddens,
num_hiddens, num_heads, 0.5)
attention.eval()
python

