动手学深度学习(四十二)——双向循环神经网络(bi-RNN)

双向循环神经网络

  在序列学习中,我们以往假设的目标是:到目前为止,在给定观测的情况下对下一个输出进行建模。例如,在时间序列的上下文中或在语言模型的上下文中。虽然这是一个典型的情况,但这并不是我们可能遇到的唯一情况。为了说明这个问题,考虑以下三个在文本序列中填空的任务:

  • ___(高兴、饿了、很好…)。
  • ___ 饿了(非常、一般、已经…)。
  • ___ 饿了,我可以吃半头猪(非常)。

  根据可获得的信息量,我们可以用不同的词填空,如“很高兴”(“happy”)、“不”(“not”)和“非常”(“very”)。很明显,短语的结尾(如果有的话)传达了重要信息,而这些信息关乎到选择哪个词来填空,所以不能利用这一点的序列模型将在相关任务上表现不佳。例如,如果要做好命名实体识别(例如,识别“Green”指的是“格林先生”还是绿色),不同长度的上下文范围重要性是相同的。为了获得一些解决问题的灵感,让我们先迂回到概率图模型。

  • 取决于过去和未来的上下文,可以填很不一样的词
  • 目前为止RNN只看过去
  • 在填空的时候,我们可以看到未来

一、隐马尔可夫模型中的动态规划

  这一小节是用来说明动态规划问题的,具体的技术细节对于理解深度学习模型并不重要,但它们有助于人们思考为什么要使用深度学习,以及为什么要选择特定的结构。(看着看着就看不懂系列,建议看不懂直接跳过,不影响阅读,不过补一下概率论也是好的

  如果我们想用概率图模型来解决这个问题,可以设计一个隐变量模型,如下图所示。在任意时间步 t t t,假设存在某个隐变量 h t h_t ht,通过概率 P ( x t ∣ h t ) P(x_t \mid h_t) P(xtht) 控制我们观测到的发射 x t x_t xt。此外,任何转移 h t → h t + 1 h_t \to h_{t+1} htht+1 都是由一些状态转移概率 P ( h t + 1 ∣ h t ) P(h_{t+1} \mid h_{t}) P(ht+1ht) 给出。这个概率图模型就是一个 隐马尔可夫模型(hidden Markov model,HMM),如图所示。

因此,对于有 T T T 个观测值的序列,我们在观测状态和隐藏状态上具有以下联合概率分布:

P ( x 1 , … , x T , h 1 , … , h T ) = ∏ t = 1 T P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ,  where  P ( h 1 ∣ h 0 ) = P ( h 1 ) . P(x_1, \ldots, x_T, h_1, \ldots, h_T) = \prod_{t=1}^T P(h_t \mid h_{t-1}) P(x_t \mid h_t), \text{ where } P(h_1 \mid h_0) = P(h_1). P(x1,,xT,h1,,hT)=t=1TP(htht1)P(xtht), where P(h1h0)=P(h1).

  现在假设我们观测到所有的 x i x_i xi,除了 x j x_j xj恭喜你进入了完形填空界面),并且我们的目标是计算 P ( x j ∣ x − j ) P(x_j \mid x_{-j}) P(xjxj),其中 x − j = ( x 1 , … , x j − 1 , x j + 1 , … , x T ) x_{-j} = (x_1, \ldots, x_{j-1}, x_{j+1}, \ldots, x_{T}) xj=(x1,,xj1,xj+1,,xT)。由于 P ( x j ∣ x − j ) P(x_j \mid x_{-j}) P(xjxj) 中没有隐变量,因此我们考虑对 h 1 , … , h T h_1, \ldots, h_T h1,,hT 的选择构成的所有可能的组合进行求和。如果任何 h i h_i hi 可以接受 k k k 个不同的值(有限的状态数),这意味着我们需要对 k T k^T kT 个项求和,这个任务是不可能完成的!幸运的是,有一个优雅的解决方案——动态规划(dynamic programming)。

要了解它的工作方式,请考虑对隐变量 h 1 , … , h T h_1, \ldots, h_T h1,,hT 的依次求和。可以得出:

P ( x 1 , … , x T ) = ∑ h 1 , … , h T P ( x 1 , … , x T , h 1 , … , h T ) = ∑ h 1 , … , h T ∏ t = 1 T P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) = ∑ h 2 , … , h T [ ∑ h 1 P ( h 1 ) P ( x 1 ∣ h 1 ) P ( h 2 ∣ h 1 ) ] ⏟ π 2 ( h 2 ) = d e f P ( x 2 ∣ h 2 ) ∏ t = 3 T P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) = ∑ h 3 , … , h T [ ∑ h 2 π 2 ( h 2 ) P ( x 2 ∣ h 2 ) P ( h 3 ∣ h 2 ) ] ⏟ π 3 ( h 3 ) = d e f P ( x 3 ∣ h 3 ) ∏ t = 4 T P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) = … = ∑ h T π T ( h T ) P ( x T ∣ h T ) . \begin{aligned} &P(x_1, \ldots, x_T) \\ =& \sum_{h_1, \ldots, h_T} P(x_1, \ldots, x_T, h_1, \ldots, h_T) \\ =& \sum_{h_1, \ldots, h_T} \prod_{t=1}^T P(h_t \mid h_{t-1}) P(x_t \mid h_t) \\ =& \sum_{h_2, \ldots, h_T} \underbrace{\left[\sum_{h_1} P(h_1) P(x_1 \mid h_1) P(h_2 \mid h_1)\right]}_{\pi_2(h_2) \stackrel{\mathrm{def}}{=}} P(x_2 \mid h_2) \prod_{t=3}^T P(h_t \mid h_{t-1}) P(x_t \mid h_t) \\ =& \sum_{h_3, \ldots, h_T} \underbrace{\left[\sum_{h_2} \pi_2(h_2) P(x_2 \mid h_2) P(h_3 \mid h_2)\right]}_{\pi_3(h_3)\stackrel{\mathrm{def}}{=}} P(x_3 \mid h_3) \prod_{t=4}^T P(h_t \mid h_{t-1}) P(x_t \mid h_t)\\ =& \dots \\ =& \sum_{h_T} \pi_T(h_T) P(x_T \mid h_T). \end{aligned} ======P(x1,,xT)h1,,hTP(x1,,xT,h1,,hT)h1,,hTt=1TP(htht1)P(xtht)h2,,hTπ2(h2)=def [h1P(h1)P(x1h1)P(h2h1)]P(x2h2)t=3TP(htht1)P(xtht)h3,,hTπ3(h3)=def [h2π2(h2)P(x2h2)P(h3h2)]P(x3h3)t=4TP(htht1)P(xtht)hTπT(hT)P(xThT).

通常,我们将“前向递归”(forward recursion)写为:

π t + 1 ( h t + 1 ) = ∑ h t π t ( h t ) P ( x t ∣ h t ) P ( h t + 1 ∣ h t ) . \pi_{t+1}(h_{t+1}) = \sum_{h_t} \pi_t(h_t) P(x_t \mid h_t) P(h_{t+1} \mid h_t). πt+1(ht+1)=htπt(ht)P(xtht)P(ht+1ht).

递归被初始化为 π 1 ( h 1 ) = P ( h 1 ) \pi_1(h_1) = P(h_1) π1(h1)=P(h1)。符号简化,也可以写成 π t + 1 = f ( π t , x t ) \pi_{t+1} = f(\pi_t, x_t) πt+1=f(πt,xt),其中 f f f 是一些可学习的函数。这看起来就像我们在循环神经网络中讨论的隐变量模型中的更新方程。

与前向递归一样,我们也可以使用后向递归对同一组隐变量求和。这将得到:

P ( x 1 , … , x T ) = ∑ h 1 , … , h T P ( x 1 , … , x T , h 1 , … , h T ) = ∑ h 1 , … , h T ∏ t = 1 T − 1 P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ⋅ P ( h T ∣ h T − 1 ) P ( x T ∣ h T ) = ∑ h 1 , … , h T − 1 ∏ t = 1 T − 1 P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ⋅ [ ∑ h T P ( h T ∣ h T − 1 ) P ( x T ∣ h T ) ] ⏟ ρ T − 1 ( h T − 1 ) = d e f = ∑ h 1 , … , h T − 2 ∏ t = 1 T − 2 P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ⋅ [ ∑ h T − 1 P ( h T − 1 ∣ h T − 2 ) P ( x T − 1 ∣ h T − 1 ) ρ T − 1 ( h T − 1 ) ] ⏟ ρ T − 2 ( h T − 2 ) = d e f = … = ∑ h 1 P ( h 1 ) P ( x 1 ∣ h 1 ) ρ 1 ( h 1 ) . \begin{aligned} & P(x_1, \ldots, x_T) \\ =& \sum_{h_1, \ldots, h_T} P(x_1, \ldots, x_T, h_1, \ldots, h_T) \\ =& \sum_{h_1, \ldots, h_T} \prod_{t=1}^{T-1} P(h_t \mid h_{t-1}) P(x_t \mid h_t) \cdot P(h_T \mid h_{T-1}) P(x_T \mid h_T) \\ =& \sum_{h_1, \ldots, h_{T-1}} \prod_{t=1}^{T-1} P(h_t \mid h_{t-1}) P(x_t \mid h_t) \cdot \underbrace{\left[\sum_{h_T} P(h_T \mid h_{T-1}) P(x_T \mid h_T)\right]}_{\rho_{T-1}(h_{T-1})\stackrel{\mathrm{def}}{=}} \\ =& \sum_{h_1, \ldots, h_{T-2}} \prod_{t=1}^{T-2} P(h_t \mid h_{t-1}) P(x_t \mid h_t) \cdot \underbrace{\left[\sum_{h_{T-1}} P(h_{T-1} \mid h_{T-2}) P(x_{T-1} \mid h_{T-1}) \rho_{T-1}(h_{T-1}) \right]}_{\rho_{T-2}(h_{T-2})\stackrel{\mathrm{def}}{=}} \\ =& \ldots \\ =& \sum_{h_1} P(h_1) P(x_1 \mid h_1)\rho_{1}(h_{1}). \end{aligned} ======P(x1,,xT)h1,,hTP(x1,,xT,h1,,hT)h1,,hTt=1T1P(htht1)P(xtht)P(hThT1)P(xThT)h1,,hT1t=1T1P(htht1)P(xtht)ρT1(hT1)=def [hTP(hThT1)P(xThT)]h1,,hT2t=1T2P(htht1)P(xtht)ρT2(hT2)=def hT1P(hT1hT2)P(xT1hT1)ρT1(hT1) h1P(h1)P(x1h1)ρ1(h1).

因此,我们可以将“后向递归”(backward recursion)写为:

ρ t − 1 ( h t − 1 ) = ∑ h t P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ρ t ( h t ) , \rho_{t-1}(h_{t-1})= \sum_{h_{t}} P(h_{t} \mid h_{t-1}) P(x_{t} \mid h_{t}) \rho_{t}(h_{t}), ρt1(ht1)=htP(htht1)P(xtht)ρt(ht),

  初始化 ρ T ( h T ) = 1 \rho_T(h_T) = 1 ρT(hT)=1。前向和后向递归都允许我们对 T T T 个隐变量在 O ( k T ) \mathcal{O}(kT) O(kT)(线性而不是指数)时间内对 ( h 1 , … , h T ) (h_1, \ldots, h_T) (h1,,hT) 的所有值求和。这是使用图模型进行概率推理的巨大好处之一。它也是通用消息传递算法 Aji.McEliece.2000 的一个非常特殊的例子。结合前向和后向递归,我们能够计算

P ( x j ∣ x − j ) ∝ ∑ h j π j ( h j ) ρ j ( h j ) P ( x j ∣ h j ) . P(x_j \mid x_{-j}) \propto \sum_{h_j} \pi_j(h_j) \rho_j(h_j) P(x_j \mid h_j). P(xjxj)hjπj(hj)ρj(hj)P(xjhj).

  注意到因为符号简化的需要,后向递归也可以写为 ρ t − 1 = g ( ρ t , x t ) \rho_{t-1} = g(\rho_t, x_t) ρt1=g(ρt,xt),其中 g g g 是一个可以学习的函数。同样,这看起来非常像一个更新方程,只是不像我们在循环神经网络中看到的那样前向运算,而是后向计算。事实上,知道未来数据何时可用对隐马尔可夫模型是有益的。信号处理科学家将是否知道未来观测这两种情况区分为内插和外推。有关更多详细信息,请参阅Doucet.De Freitas.Gordon.2001

二、双向模型

  如果我们希望在循环神经网络中拥有一种机制,使之能够提供与隐马尔可夫模型类似的前瞻能力,我们就需要修改循环神经网络的设计。幸运的是,这在概念上很容易,只需要增加一个从最后一个标记开始从后向前运行的循环神经网络,而不是只有一个在前向模式下从第一个标记开始运行的循环神经网络。
  双向循环神经网络(bidirectional RNNs)添加了反向传递信息的隐藏层,以便更灵活地处理此类信息。下图中描述了具有单个隐藏层的双向循环神经网络的结构。

  事实上,这与隐马尔可夫模型中的动态规划的前向和后向递归没有太大区别。其主要区别是,在隐马尔可夫模型中的方程具有特定的统计意义。双向循环神经网络没有这样容易理解的解释,我们只能把它们当作通用的、可学习的函数。这种转变集中体现了现代深度网络的设计原则:首先使用的是经典统计模型的函数依赖类型,然后将其参数化为通用形式。

2.1 定义

  双向循环神经网络是由 :Schuster.Paliwal.1997 提出的,关于各种结构的详细讨论请参阅 :Graves.Schmidhuber.2005。让我们看看这样一个网络的细节。

  对于任意时间步 t t t,给定一个小批量的输入数据 X t ∈ R n × d \mathbf{X}_t \in \mathbb{R}^{n \times d} XtRn×d(样本数: n n n,每个示例中的输入数: d d d),并且令隐藏层激活函数为 ϕ \phi ϕ。在双向结构中,我们设该时间步的前向和反向隐藏状态分别为 H → t ∈ R n × h \overrightarrow{\mathbf{H}}_t \in \mathbb{R}^{n \times h} H tRn×h H ← t ∈ R n × h \overleftarrow{\mathbf{H}}_t \in \mathbb{R}^{n \times h} H tRn×h,其中 h h h 是隐藏单元的数目。前向和反向隐藏状态的更新如下:

H → t = ϕ ( X t W x h ( f ) + H → t − 1 W h h ( f ) + b h ( f ) ) , H ← t = ϕ ( X t W x h ( b ) + H ← t + 1 W h h ( b ) + b h ( b ) ) , \begin{aligned} \overrightarrow{\mathbf{H}}_t &= \phi(\mathbf{X}_t \mathbf{W}_{xh}^{(f)} + \overrightarrow{\mathbf{H}}_{t-1} \mathbf{W}_{hh}^{(f)} + \mathbf{b}_h^{(f)}),\\ \overleftarrow{\mathbf{H}}_t &= \phi(\mathbf{X}_t \mathbf{W}_{xh}^{(b)} + \overleftarrow{\mathbf{H}}_{t+1} \mathbf{W}_{hh}^{(b)} + \mathbf{b}_h^{(b)}), \end{aligned} H tH t=ϕ(XtWxh(f)+H t1Whh(f)+bh(f)),=ϕ(XtWxh(b)+H t+1Whh(b)+bh(b)),

其中,权重 W x h ( f ) ∈ R d × h , W h h ( f ) ∈ R h × h , W x h ( b ) ∈ R d × h , W h h ( b ) ∈ R h × h \mathbf{W}_{xh}^{(f)} \in \mathbb{R}^{d \times h}, \mathbf{W}_{hh}^{(f)} \in \mathbb{R}^{h \times h}, \mathbf{W}_{xh}^{(b)} \in \mathbb{R}^{d \times h}, \mathbf{W}_{hh}^{(b)} \in \mathbb{R}^{h \times h} Wxh(f)Rd×h,Whh(f)Rh×h,Wxh(b)Rd×h,Whh(b)Rh×h 和偏置 b h ( f ) ∈ R 1 × h , b h ( b ) ∈ R 1 × h \mathbf{b}_h^{(f)} \in \mathbb{R}^{1 \times h}, \mathbf{b}_h^{(b)} \in \mathbb{R}^{1 \times h} bh(f)R1×h,bh(b)R1×h 都是模型参数。

  接下来,将前向隐藏状态 H → t \overrightarrow{\mathbf{H}}_t H t 和反向隐藏状态 H ← t \overleftarrow{\mathbf{H}}_t H t 连续起来,获得需要送入输出层的隐藏状态 H t ∈ R n × 2 h \mathbf{H}_t \in \mathbb{R}^{n \times 2h} HtRn×2h。在具有多个隐藏层的深度双向循环神经网络中,该信息作为输入传递到下一个双向层。最后,输出层计算得到的输出为 O t ∈ R n × q \mathbf{O}_t \in \mathbb{R}^{n \times q} OtRn×q q q q 是输出单元的数目):

O t = H t W h q + b q . \mathbf{O}_t = \mathbf{H}_t \mathbf{W}_{hq} + \mathbf{b}_q. Ot=HtWhq+bq.

这里,权重矩阵 W h q ∈ R 2 h × q \mathbf{W}_{hq} \in \mathbb{R}^{2h \times q} WhqR2h×q 和偏置 b q ∈ R 1 × q \mathbf{b}_q \in \mathbb{R}^{1 \times q} bqR1×q 是输出层的模型参数。事实上,这两个方向可以拥有不同数量的隐藏单元。

正向和反向的weight是concate在一起的,不是相加或者其他操作

2.2 模型的计算成本及其应用

  双向循环神经网络的一个关键特性是,使用来自序列两端的信息来估计输出。也就是说,我们使用来自过去和未来的观测信息来预测当前的观测。但是在对下一个标记进行预测的案例中,这样的行为并不是我们想要的。因为在预测下一个标记时,我们终究是无法知道下一个标记的下一个是什么。因此,如果我们幼稚地基于双向循环神经网络进行预测时,将不会得到很好的准确性:因为在训练期间,我们能够利用过去和未来的数据来估计现在,而在测试期间,我们只有过去的数据,因此准确性将会很差。下面,我们将在实验中说明这一点。

  另一个严重问题是,双向循环神经网络的计算速度非常慢。其主要原因是网络的前向传播需要在双向层中进行前向和后向递归,并且网络的反向传播还依赖于前向传播的结果。因此,梯度求解将有一个非常长的依赖链。

  双向层的使用在实践中非常少,并且仅仅应用于部分场合。例如,填充缺失的单词、标记注释(例如,用于命名实体识别)以及作为序列处理工作流中的一个步骤对序列进行编码(例如,用于机器翻译)。

三、双向循环神经网络的错误应用

  众所周知,双向循环神经网络使用了过去的和未来的数据这是事实,而如果我们忽略基于事实的那些建议,并将其应用于语言模型,那么我们也能得到令人可以接受的困惑度估计。尽管如此,如下面的实验所示,该模型预测未来标记的能力也存在严重的缺陷。尽管困惑度是合理的,但是经过多次迭代模型也只能生成乱码。我们用下面的代码引以为戒,以防在错误的环境中使用它们。(做完形填空,而非推理的,其实也挺好理解的,就是因为训练的时候都是知道并利用了未来信息的,而实际推理中未来信息是不知道的)

'''
warning !!! 下面这是一个错误例子
    用于说明双向LSTM不能用于预测!其困惑度结果确实是很好,但是几乎预测不出未来
'''
import torch
from torch import nn
from d2l import torch as d2l 

# 加载数据
batch_size,num_steps,device = 32,25,d2l.try_gpu()
train_iter,vocab = d2l.load_data_time_machine(batch_size,num_steps)

# 通过设置‘bidirective=True’定义双向LSTM
vocab_size, num_hiddens, num_layers = len(vocab), 256, 2
num_inputs = vocab_size
lstm_layer = nn.LSTM(num_inputs, num_hiddens, num_layers, bidirectional=True)
model = d2l.RNNModel(lstm_layer, len(vocab))
model = model.to(device)
# 训练模型
num_epochs, lr = 500, 1
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
perplexity 1.1, 77803.5 tokens/sec on cuda:0
time travellerererererererererererererererererererererererererer
travellerererererererererererererererererererererererererer
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
PyTorch是一个开源的机器习框架,它提供了丰富的工具和库,用于构建和训练神经网络模型。双向循环神经网络Bidirectional Recurrent Neural Network,BiRNN)是一种常用的循环神经网络(Recurrent Neural Network,RNN)变体,它能够同时考虑过去和未来的上下文信息。 在PyTorch中,可以使用torch.nn模块来构建双向循环神经网络。首先,需要定义一个RNN模型,并指定输入维度、隐藏层维度和输出维度等参数。然后,可以通过torch.nn.RNN类来创建一个RNN层,并将其作为模型的一部分。 接下来,可以使用torch.nn.utils.rnn.pad_sequence函数将输入序列进行填充,使其长度一致。然后,可以将填充后的序列作为输入传递给RNN层。为了实现双向性,可以使用torch.nn.RNN类的bidirectional参数设置为True。 最后,可以通过调用模型的forward方法来进行前向传播计算。双向循环神经网络将分别计算正向和反向的隐藏状态,并将它们连接起来作为最终的输出。 下面是一个简单的示例代码,展示了如何在PyTorch中构建和训练一个双向循环神经网络: ```python import torch import torch.nn as nn # 定义双向循环神经网络模型 class BiRNN(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(BiRNN, self).__init__() self.hidden_size = hidden_size self.rnn = nn.RNN(input_size, hidden_size, bidirectional=True) self.fc = nn.Linear(hidden_size * 2, output_size) # *2是因为双向RNN有两个方向的隐藏状态 def forward(self, input_seq): output, hidden = self.rnn(input_seq) hidden_cat = torch.cat((hidden[-2, :, :], hidden[-1, :, :]), dim=1) # 将正向和反向的隐藏状态连接起来 output = self.fc(hidden_cat) return output # 定义输入序列和标签 input_seq = torch.randn(5, 3, 10) # 输入序列的维度为(序列长度, batch大小, 输入维度) labels = torch.tensor([1, 0, 1, 0, 1]) # 创建双向循环神经网络模型 model = BiRNN(input_size=10, hidden_size=20, output_size=2) # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr=0.1) # 训练模型 for epoch in range(100): optimizer.zero_grad() output = model(input_seq) loss = criterion(output, labels) loss.backward() optimizer.step() # 使用训练好的模型进行预测 test_input = torch.randn(1, 3, 10) prediction = model(test_input) ``` 这是一个简单的双向循环神经网络的示例,你可以根据自己的需求进行修改和扩展。希望对你有帮助!
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

留小星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值