前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >循环神经网络——中篇【深度学习】【PyTorch】【d2l】

循环神经网络——中篇【深度学习】【PyTorch】【d2l】

作者头像
来杯Sherry
发布2023-09-19 10:24:25
3410
发布2023-09-19 10:24:25
举报
文章被收录于专栏:第一专栏

6、循环神经网络

6.4、循环神经网络(RNN

6.4.1、理论部分

原理图

更新隐藏状态

H_t = Φ(W_{hh}h_{t-1}+W_{hx}X_t+b_h)

H_t:

当前隐层,

X_t:

输入

W_{hh}:

上层隐层分配到的权重

W_{hx}:

输入分配到的权重 b:偏移

循环指的是什么? 隐状态使用的定义与前一个时间步中使用的定义相同, 因此 上式计算是循环的(recurrent)。 于是基于循环计算的隐状态神经网络被命名为 循环神经网络(recurrent neural network)。 在循环神经网络中执行如上计算的层 称为循环层(recurrent layer)。

输出

Ot = Φ(W_{ho}H_t+b_o)

文本预测展示

困惑度

用来衡量一个语言模型好坏的标准,可以用平均交叉熵,如下:

Π = \frac{1}{n}\sum_{t=1}^n -log \ p(x_t|x_{t-1},...x_1)

其中,p为预测概率,

x_t

为真实词。

历史原因,NLP使用困惑度

exp(Π)

来衡量,是平均每次可能选项,1为完美,最差为∞。

梯度裁剪

用于抑制RNN梯度爆炸,如果梯度长度超过

Θ

,那么

g

长度将拖回

Θ

,反之任由

g

变化。

g ← min(1,\frac{Θ}{||g||})g

6.4.2、代码实现

代码语言:javascript
复制
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)

定义模型

代码语言:javascript
复制
num_hiddens = 256
rnn_layer = nn.RNN(len(vocab), num_hiddens)

隐状态:(隐藏层数,批量大小,隐藏单元数)

代码语言:javascript
复制
state = torch.zeros((1, batch_size, num_hiddens))
state.shape

代码语言:javascript
复制
torch.Size([1, 32, 256])
代码语言:javascript
复制
#@save
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))

训练&预测

这里是,随即权重预测(效果不好)

代码语言:javascript
复制
device = d2l.try_gpu()
net = RNNModel(rnn_layer, vocab_size=len(vocab))
net = net.to(device)
d2l.predict_ch8('time traveller', 10, net, vocab, device)

代码语言:javascript
复制
'time travellermkkkkkkkkk'

高级API训练预测

代码语言:javascript
复制
num_epochs, lr = 500, 1
d2l.train_ch8(net, train_iter, vocab, lr, num_epochs, device)

调试报错,未解决

6.5、长短期记忆网络(LSTM

6.5.1、理论部分

原理图

遗忘门

将值朝0减少。

F_t = σ(X_tW_{xf} + H_{t-1}W_{hf}+b_f)

输入门

决定是否忽略输入数据。

I_t = σ(X_tW_{xi} + H_{t-1}W_{hi}+b_i)

输出门

决定是否使用隐状态。

O_t = σ(X_tW_{xo} + H_{t-1}W_{ho}+b_o)

候选记忆单元

\tilde{C} = tanh(X_tW_{xc}+H_{t-1}W_{hc}+b_c)

记忆单元

C_t = F_t ⊚ C_{t-1}+I_t ⊚ \tilde{C}_t

隐状态

H_t = O_t ⊚ tanh(\tilde{C})

6.5.2、代码实现

1)手写实现

代码语言:javascript
复制
import torch
from torch import nn
from d2l import torch as d2l

batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)

初始化模型参数

代码语言:javascript
复制
def get_lstm_params(vocab_size, num_hiddens, device):
    num_inputs = num_outputs = vocab_size

    def normal(shape):
        return torch.randn(size=shape, device=device)*0.01

    def three():
        return (normal((num_inputs, num_hiddens)),
                normal((num_hiddens, num_hiddens)),
                torch.zeros(num_hiddens, device=device))

    W_xi, W_hi, b_i = three()  # 输入门参数
    W_xf, W_hf, b_f = three()  # 遗忘门参数
    W_xo, W_ho, b_o = three()  # 输出门参数
    W_xc, W_hc, b_c = three()  # 候选记忆元参数
    # 输出层参数
    W_hq = normal((num_hiddens, num_outputs))
    b_q = torch.zeros(num_outputs, device=device)
    # 附加梯度
    params = [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc,
              b_c, W_hq, b_q]
    for param in params:
        param.requires_grad_(True)
    return params

定义模型

代码语言:javascript
复制
def init_lstm_state(batch_size, num_hiddens, device):
    return (torch.zeros((batch_size, num_hiddens), device=device),
            torch.zeros((batch_size, num_hiddens), device=device))
代码语言:javascript
复制
def lstm(inputs, state, params):
    [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c,
     W_hq, b_q] = params
    (H, C) = state
    outputs = []
    for X in inputs:
        I = torch.sigmoid((X @ W_xi) + (H @ W_hi) + b_i)
        F = torch.sigmoid((X @ W_xf) + (H @ W_hf) + b_f)
        O = torch.sigmoid((X @ W_xo) + (H @ W_ho) + b_o)
        C_tilda = torch.tanh((X @ W_xc) + (H @ W_hc) + b_c)
        C = F * C + I * C_tilda
        H = O * torch.tanh(C)
        Y = (H @ W_hq) + b_q
        outputs.append(Y)
    return torch.cat(outputs, dim=0), (H, C)

训练&预测

代码语言:javascript
复制
vocab_size, num_hiddens, device = len(vocab), 256, d2l.try_gpu()
num_epochs, lr = 500, 1
model = d2l.RNNModelScratch(len(vocab), num_hiddens, device, get_lstm_params,
                            init_lstm_state, lstm)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)

2)简洁实现

代码语言:javascript
复制
num_inputs = vocab_size
lstm_layer = nn.LSTM(num_inputs, num_hiddens)
model = d2l.RNNModel(lstm_layer, len(vocab))
model = model.to(device)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)

测试中,比手写实现快,甚至更准的原因? 深度学习框架的高级API对代码进行了更多的优化, 该模型在较短的时间内达到了较低的困惑度。

6.6、门控循环单元(GRU

6.6.1、理论部分

直白理解:不是每个观察都重要,更新门实现关注机制,重置门实现遗忘机制。

原理图

重置门

R_t =σ(X_{tW}x_r+H_{t-1}W_{hr}+b_r)

更新门

Z_t =σ(X_tW_{xz}+H_{t-1}W_{hz}+b_z)

候选隐状态

\tilde{H}_t = tanh(X_tW_{xh}+(R_t ⊚ H_{t-1})W_{hh}+b_h)

隐状态

H_t = Z_t ⊚ H_{t-1}+(1-Z_t)⊚\tilde{H}_t

6.6.2、代码实现

1)手写实现

代码语言:javascript
复制
import torch
from torch import nn
from d2l import torch as d2l

batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)

初始化模型参数

代码语言:javascript
复制
def get_params(vocab_size, num_hiddens, device):
    num_inputs = num_outputs = vocab_size

    def normal(shape):
        return torch.randn(size=shape, device=device)*0.01

    def three():
        return (normal((num_inputs, num_hiddens)),
                normal((num_hiddens, num_hiddens)),
                torch.zeros(num_hiddens, device=device))

    W_xz, W_hz, b_z = three()  # 更新门参数
    W_xr, W_hr, b_r = three()  # 重置门参数
    W_xh, W_hh, b_h = three()  # 候选隐状态参数
    # 输出层参数
    W_hq = normal((num_hiddens, num_outputs))
    b_q = torch.zeros(num_outputs, device=device)
    # 附加梯度
    params = [W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q]
    for param in params:
        param.requires_grad_(True)
    return params

定义模型

代码语言:javascript
复制
def init_gru_state(batch_size, num_hiddens, device):
    return (torch.zeros((batch_size, num_hiddens), device=device), )
代码语言:javascript
复制
def gru(inputs, state, params):
    W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
    H, = state
    outputs = []
    for X in inputs:
        Z = torch.sigmoid((X @ W_xz) + (H @ W_hz) + b_z)
        R = torch.sigmoid((X @ W_xr) + (H @ W_hr) + b_r)
        H_tilda = torch.tanh((X @ W_xh) + ((R * H) @ W_hh) + b_h)
        H = Z * H + (1 - Z) * H_tilda
        Y = H @ W_hq + b_q
        outputs.append(Y)
    return torch.cat(outputs, dim=0), (H,)

训练&预测

代码语言:javascript
复制
vocab_size, num_hiddens, device = len(vocab), 256, d2l.try_gpu()
num_epochs, lr = 500, 1
model = d2l.RNNModelScratch(len(vocab), num_hiddens, device, get_params,
                            init_gru_state, gru)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)

2)简洁实现

代码语言:javascript
复制
num_inputs = vocab_size
gru_layer = nn.GRU(num_inputs, num_hiddens)
model = d2l.RNNModel(gru_layer, len(vocab))
model = model.to(device)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 6、循环神经网络
    • 6.4、循环神经网络(RNN)
      • 6.4.1、理论部分
      • 6.4.2、代码实现
    • 6.5、长短期记忆网络(LSTM)
      • 6.5.1、理论部分
      • 6.5.2、代码实现
    • 6.6、门控循环单元(GRU)
      • 6.6.1、理论部分
      • 6.6.2、代码实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档