Bootstrap

实用机器学习笔记十六:循环神经网络

前言:

本文是个人在 B 站自学李沐老师的实用机器学习课程【斯坦福 2021 秋季中文同步】的学习笔记,感觉沐神讲解的非常棒 yyds。

从多层感知机到循环神经网络(RNN):

上一篇文章讲的是图片分类问题,今天来讲自然语言处理中的语言模型循环神经网络。

  • 语言模型:目的就是预测下一个单词,比如:

  • hello----->world hello world----->!

  • 上面这个预测案例也可以使用MLP来解决

  • hello可以使用one-hot编码作为输入,输出可以使用softmax分类来解决,比如可能有1000个分类,那么输出就是1000维的向量。再对world做预测,使用同样的模型和同样的模型权重参数。不知道大家有没有注意到一个问题:在对world做预测时,模型只看到了world这个词,并没有看到world前面的词hello,也就是说只看到了上一时刻的world,没有看到world之前的信息,信息无法传过来。可能你会说在对world做预测时,把hello也放进输入,那么输入的维度就发生了变化,全连接层的输入维度是不能发生变化的。问题就是如何把长度会变的一个序列表示成一个固定长度的输入?可能你会想到之前介绍的Bag-of-word(词袋包:每一个词的独热编码相加)来解决,但是这种相加的方式并没有把时序信息(比如:hello world 变成world hello后,独热编码相加并不会变化)表示出来。

  • 循环神经网络来解决:

  • 如下图是循环神经网络的语言模型。为了在为world做预测时可以拿到前一时刻的信息,就在两个全连接层之间添加了一条数据通道,把全连接层对hello处理的输出结果拿出来作为隐藏信息H和world做拼接,这样就拿到了前面时序的信息。这个隐藏信息里面包含了hello之前所有时刻的输入信息,因为hello之前的信息也在往hello这里传递。而且最重要的是,不管这个传递有多少步,隐藏状态H的维度是不变的,唯一能决定H维度的就是全连接层的神经元的个数。

RNN和Gated RNN(门RNN):

  • 基础版RNN:具体形式如下:

看上面这个式子,如果去掉激活函数实际上也是一个线性模型。在RNN中,即在下面这张图中,(是有激活函数的),这和上一张图中不太一样,但是我反复听了几遍课程,确实是这样的,不知道是不是沐神说错了还是怎么的,我又查了几篇博客,都是和下面这个图是一个意思。所以这里就认为下下面这个图是正确的。这里的是一个h*h的矩阵,只和RNN单元的输出神经元个数有关,加入输入是一个x维的向量,是h*x维的矩阵。

  • Gated RNN(LSTM,GRU):更好的控制信息流。

  • 输入遗忘门:控制当前时刻输入,流入RNN的量

  • 隐藏遗忘门:控制以往时刻信息流入当前时刻RNN的量

  • 代码:

W_xh = nn.Parameter(torch.randn(num_inputs, num_hiddens)*0.01)
W_hh = nn.Parameter(torch.randn(num_hiddens, num_hiddens)*0.01)
b_h = nn.Parameter(torch.zeros(num_hiddens))

H = torch.zeros(num_hiddens)
outputs = []

for X in inputs: # input shape : (num_steps, batch_size, num_inputs),
  # 这个循环次数就是num_steps,就是有几个时间步
  H = torch.tanh(X @ W_xh,+ H @ W_hh + b_h)
  outputs.append(H)

Bi-RNN(双向RNN) 和 Deep RNN:

  • Bi-RNN:

Bi-RNN的输出既有正向时序的信息,也有反向时序的信息,比如你做完形填空时,就可以根据后面的句子来推导前面的空。

  • Deep RNN: