注:书中对代码的讲解并不详细,本文对很多细节做了详细注释。另外,书上的源代码是在Jupyter Notebook上运行的,较为分散,本文将代码集中起来,并加以完善,全部用vscode在python 3.9.18下测试通过,同时对于书上部分章节也做了整合。

Chapter8 Recurrent Neural Networks

8.4 Recurrent Neural Networks

n

n

n元语法模型,单词

x

t

x_t

xt​在时间步

t

t

t的条件概率仅取决于前面

n

1

n-1

n−1个单词。对于时间步

t

(

n

1

)

t-(n-1)

t−(n−1)之前的单词,如果我们想将其可能产生的影响合并到

x

t

x_t

xt​上,需要增加

n

n

n,然而模型参数的数量也会随之呈指数增长,因为词表

V

\mathcal{V}

V需要存储

V

n

|\mathcal{V}|^n

∣V∣n个数字(每个可能的序列对应一个概率值),因此不如使用隐变量模型:

P

(

x

t

x

t

1

,

,

x

1

)

P

(

x

t

h

t

1

)

,

P(x_t \mid x_{t-1}, \ldots, x_1) \approx P(x_t \mid h_{t-1}),

P(xt​∣xt−1​,…,x1​)≈P(xt​∣ht−1​),

其中

h

t

1

h_{t-1}

ht−1​是隐状态(hidden state),也称为隐藏变量(hidden variable),它存储了到时间步

t

1

t-1

t−1的序列信息。通常,我们可以基于当前输入

x

t

x_{t}

xt​和先前隐状态

h

t

1

h_{t-1}

ht−1​来计算时间步

t

t

t处的任何时间的隐状态:

h

t

=

f

(

x

t

,

h

t

1

)

.

h_t = f(x_{t}, h_{t-1}).

ht​=f(xt​,ht−1​).

对于上式中的函数

f

f

f,隐变量模型可以不是近似值,因为

h

t

h_t

ht​可以存储到目前为止观察到的所有数据,然而这样的操作可能会使计算和存储的代价都变得昂贵。值得注意的是,隐藏层和隐状态是两个不同的概念,隐藏层是在从输入到输出的路径上(以观测角度来理解)的隐藏的层,而隐状态则是在给定步骤所做的任何事情(以技术角度来定义)的输入,并且这些状态只能通过先前时间步的数据来计算。

8.4.1 Neural Networks without Hidden States

对只有单隐藏层的多层感知机,设隐藏层的激活函数为

ϕ

\phi

ϕ,给定一个小批量样本

X

R

n

×

d

\mathbf{X} \in \mathbb{R}^{n \times d}

X∈Rn×d,其中批量大小为

n

n

n,输入维度为

d

d

d,则隐藏层的输出

H

R

n

×

h

\mathbf{H} \in \mathbb{R}^{n \times h}

H∈Rn×h通过下式计算:

H

=

ϕ

(

X

W

x

h

+

b

h

)

.

\mathbf{H} = \phi(\mathbf{X} \mathbf{W}_{xh} + \mathbf{b}_h).

H=ϕ(XWxh​+bh​).

输出层由下式给出:

O

=

H

W

h

q

+

b

q

,

\mathbf{O} = \mathbf{H} \mathbf{W}_{hq} + \mathbf{b}_q,

O=HWhq​+bq​,

8.4.2 RNN with Hidden States

假设我们在时间步

t

t

t有小批量输入

X

t

R

n

×

d

\mathbf{X}_t \in \mathbb{R}^{n \times d}

Xt​∈Rn×d,换言之,对于

n

n

n个序列样本的小批量,

X

t

\mathbf{X}_t

Xt​的每一行对应于来自该序列的时间步

t

t

t处的一个样本。用

H

t

R

n

×

h

\mathbf{H}_t \in \mathbb{R}^{n \times h}

Ht​∈Rn×h表示时间步

t

t

t的隐藏变量,计算公式如下:

H

t

=

ϕ

(

X

t

W

x

h

+

H

t

1

W

h

h

+

b

h

)

.

(1)

\mathbf{H}_t = \phi(\mathbf{X}_t \mathbf{W}_{xh} + \mathbf{H}_{t-1} \mathbf{W}_{hh} + \mathbf{b}_h).\tag{1}

Ht​=ϕ(Xt​Wxh​+Ht−1​Whh​+bh​).(1)

由于在当前时间步中,隐状态使用的定义与前一个时间步中使用的定义相同,因此称式(1)的计算是循环的(recurrent),基于循环计算的隐状态神经网络称为循环神经网络(recurrent neural network),在循环神经网络中执行循环计算的层称为循环层(recurrent layer)。对于时间步

t

t

t,输出层的输出类似于多层感知机中的计算:

O

t

=

H

t

W

h

q

+

b

q

.

\mathbf{O}_t = \mathbf{H}_t \mathbf{W}_{hq} + \mathbf{b}_q.

Ot​=Ht​Whq​+bq​. 循环神经网络的参数包括隐藏层的权重

W

x

h

R

d

×

h

,

W

h

h

R

h

×

h

\mathbf{W}_{xh} \in \mathbb{R}^{d \times h}, \mathbf{W}_{hh} \in \mathbb{R}^{h \times h}

Wxh​∈Rd×h,Whh​∈Rh×h和偏置

b

h

R

1

×

h

\mathbf{b}_h \in \mathbb{R}^{1 \times h}

bh​∈R1×h,以及输出层的权重

W

h

q

R

h

×

q

\mathbf{W}_{hq} \in \mathbb{R}^{h \times q}

Whq​∈Rh×q和偏置

b

q

R

1

×

q

\mathbf{b}_q \in \mathbb{R}^{1 \times q}

bq​∈R1×q。值得一提的是,即使在不同的时间步,循环神经网络也总是使用这些模型参数,因此循环神经网络的参数开销不会随着时间步的增加而增加。

下图展示了循环神经网络在三个相邻时间步的计算逻辑。

import torch

from d2l import torch as d2l

#循环计算(部分)的两种计算方法

X, W_xh = torch.normal(0, 1, (3, 1)), torch.normal(0, 1, (1, 4))

H, W_hh = torch.normal(0, 1, (3, 4)), torch.normal(0, 1, (4, 4))

torch.matmul(X, W_xh) + torch.matmul(H, W_hh)

torch.matmul(torch.cat((X, H), 1), torch.cat((W_xh, W_hh), 0))

8.4.3 Character-Level Language Models Based on RNN

接下来介绍如何使用循环神经网络来构建语言模型。设小批量大小为1,批量中的文本序列为"machine"。为了简化后续部分的训练,考虑使用字符级语言模型(character-level language model),将文本词元化为字符而不是单词。下图演示了如何通过基于字符级语言建模的循环神经网络,使用当前的和先前的字符预测下一个字符。

在实践中,我们使用的批量大小为

n

>

1

n>1

n>1,每个词元都由一个

d

d

d维向量表示。因此,在时间步

t

t

t输入

X

t

\mathbf X_t

Xt​将是一个

n

×

d

n\times d

n×d矩阵。

8.4.4 Perplexity

我们可以通过计算序列的似然概率来度量模型的质量,然而这难以理解、难以比较,因为较短的序列比较长的序列更有可能出现。为了解决这个问题,我们可以运用信息论。如果想要压缩文本,我们可以根据当前词元集预测的下一个词元。一个更好的语言模型应该能让我们更准确地预测下一个词元,即它应该允许我们在压缩序列时花费更少的比特,所以我们可以通过一个序列中所有的

n

n

n个词元的交叉熵损失的平均值来衡量模型的质量:

1

n

t

=

1

n

log

P

(

x

t

x

t

1

,

,

x

1

)

\frac{1}{n} \sum_{t=1}^n -\log P(x_t \mid x_{t-1}, \ldots, x_1)

n1​t=1∑n​−logP(xt​∣xt−1​,…,x1​)

其中

P

P

P由语言模型给出,

x

t

x_t

xt​是在时间步

t

t

t从该序列中观察到的实际词元。这使得不同长度的文档的性能具有了可比性。困惑度(perplexity)是上式的指数:

exp

(

1

n

t

=

1

n

log

P

(

x

t

x

t

1

,

,

x

1

)

)

.

\exp\left(-\frac{1}{n} \sum_{t=1}^n \log P(x_t \mid x_{t-1}, \ldots, x_1)\right).

exp(−n1​t=1∑n​logP(xt​∣xt−1​,…,x1​)).

困惑度的最好的理解是“下一个词元的实际选择数的调和平均数”。

在最好的情况下,模型总是完美地估计标签词元的概率为1。在这种情况下,模型的困惑度为1。在最坏的情况下,模型总是预测标签词元的概率为0。在这种情况下,困惑度是正无穷大。在基线上,该模型的预测是词表的所有可用词元上的均匀分布。在这种情况下,困惑度等于词表中唯一词元的数量。事实上,如果我们在没有任何压缩的情况下存储序列,这将是我们能做的最好的编码方式。因此,这种方式提供了一个重要的上限,而任何实际模型都必须超越这个上限。

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: