Advertisement

循环神经网络模型

阅读量:

循环神经网络


文章目录

  • 循环神经网络

  • 一、序列模型

    • 统计工具
  • 二、语言模型

    • 1.语言模型简介
    • 2.使用计数来建模
    • 2.N元语法
  • 三、RNN模型

    • 使用循环神经网络的语言模型
    • 循环神经网络
    • 困惑度(perplexity)
    • 梯度裁剪
    • 上面的过程如何在代码上实现?
  • 四、门控循环单元(GRU)

  • 五、长期记忆网络(LSTM)



提示:以下是本篇文章正文内容,下面案例可供参考

一、序列模型

统计工具

  1. 在时间t观察到x_t,那么得到T个不独立的随机变量(x_1,x_2,...,x_T) \backsim p(\bf{X})

  2. 使用条件概率展开 p(a,b)=p(a)p(b|a)=p(b)p(a|b)
    推广到T个随机变量有
    p(\bf{X})=p(x_1)\cdot p(x_2|x_1)\cdot p(x_3|x_1,x_2)\cdot ...p(x_T|x_1,...x_{T-1})
    在这里插入图片描述
    或者反过来
    p(\bf{X})=p(x_T)\cdot p(x_{T-1}|x_T)\cdot p(x_{T-2}|x_T,x_{T-1})\cdot ...p(x_T|x_2,...x_T)
    在这里插入图片描述
    反序的意义,例如知道未来的事情退过去的事情,物理上不一定可行是指如果未来的事情依赖于过去的事情而产生,那么就没法反推(这一点我还没有理解,既然未来的事情是作为已知条件,过去有和未来有联系,怎么就无法反推出过去的事情呢?)

  3. 对条件概率建模
    p(x_T|x_1,...x_{t-1}) = p(x_t|h(x_1,...,x_{t-1}))
    给定t-1个数据,求第t个数据,假设用前t-1个数据建立一个函数h,h就是一个模型,通过h_{t-1}h_t。通过对前面的数据建模,预测后一个数据,称为自回归模型,核心是求h

  4. 如何求解f
    方案A-马尔科夫假设
    假设当前数据只跟\tau个过去数据点相关
    P(x_T|x_1,...x_{t-1}) =P(x_T|x_{t-\tau},...x_{t-1})= P(x_t|h(x_{t-\tau},...,x_{t-1}))
    方案B-潜变量模型
    引入潜变量h_t来表示过去的信息h_t=g(h_{t-1},x_{t-1})
    这样\hat{x_t}=P(x_t|h_t,x_{t-1})
    在这里插入图片描述

二、语言模型

1.语言模型简介

  • 给定文本序列x_1,...,x_T, 语言模型的目标是估计联合概率p(x_1,...x_T)
  • 它的作用包括:做预训练模型(egBERT,GPT-3);生成文本,给定前面几个词,不断的使用x_t \backsim p(x_t|x_1,...,x_{t-1}) ; 判断多个序列中那个更常见,e.g “to recognize speech” vs “to wreck a nice beach”

2.使用计数来建模

  • 假设序列长度为2,我们预测 p(x, x^\prime) = p(x)p(x^\prime|x) = \frac{n(x)}{n}\frac{n(x,x^\prime)}{n(x)}
  • 这里n是总词数,n(x),n(x,x^\prime)是单个单词和连续单词对的出现次数
  • 很容易扩展到3的情况 p(x, x^\prime,x^{\prime\prime}) = p(x)p(x^\prime|x)p(^{\prime\prime}|x,x^\prime)=\frac{n(x)}{n}\frac{n(x,x^\prime)}{n(x)}\frac{n(x,x^\prime,x^{\prime\prime})}{n(x,x^\prime)}
  • 注意,这里的序列(x_,x^{\prime})是一个有序的元组

2.N元语法

  • 当序列很长时,因为文本量不够大,很可能n(x_1,...x_T)\le1
  • 使用马尔科夫假设可以缓解这个问题
  • 一元语法 p(x_1,x_2,x_3,x_4) = p(x_1)p(x_2)p(x_3)p(x_4)=\frac{n(x_1)}{n}\frac{n(x_2)}{n}\frac{n(x_3)}{n}\frac{n(x_4)}{n}
  • 二元语法 p(x_1,x_2,x_3,x_4) = p(x_1)p(x_2|x_1)p(x_3|x_2)p(x_4|x_3)=\frac{n(x_1)}{n}\frac{n(x_1,x_2)}{n(x_1)}\frac{n(x_2,x_3)}{n(x_2)}\frac{n(x_3,x_4)}{n(x_3)}
  • 三元语法 p(x_1,x_2,x_3,x_4) = p(x_1)p(x_2|x_1)p(x_3|x_1,x_2)p(x_4|x_2,x_3)=\frac{n(x_1)}{n}\frac{n(x_1,x_2)}{n(x_1)}\frac{n(x_1,x_2,x_3)}{n(x_1,x_2)}\frac{n(x_2,x_3,x_4)}{n(x_2,x_3}

三、RNN模型

使用循环神经网络的语言模型

在这里插入图片描述
训练的过程是输入‘你’ 预测 ‘好’,输入‘好’预测 ‘,’,以此类推,然后通过输出与观察值做损失函数来学习权重

循环神经网络

在这里插入图片描述

困惑度(perplexity)

  • 衡量一个语言模型的好坏可以用平均交叉熵

\pi=\frac{1}{t}\sum_{i=1}^{t}-logp(x_t|x_{t-1})

  • p是语言模型的预测概率,x_{t}是真实词
  • 历史原因NLP使用困惑度exp(\pi)来衡量,1表示完美,无穷大是最差情况

梯度裁剪

  • 迭代 中计算这T个时间步上的梯度,在反向传播过程中产生长度为O(T)的矩阵乘法链,导致数值不稳定
  • 梯度裁剪能有效预防梯度爆炸
  • 如果梯度长度超过\theta,那么把梯度长度拖回长度\theta
    g \longleftarrow min(1,\frac{\theta}{\lVert g\rVert})g

上面的过程如何在代码上实现?

模型的输入是什么?
假设我们现在有一个训练的数据集“The Time Machine”这篇小说,我们取其中连续的10000个词元(这里一个词元代表一个英文字符)作为训练的数据集,我们先进行文本处理(NLP)

复制代码
    batch_size, num_steps = 32, 35 
    train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
    
    
    python
    
    

其中num_steps 就是上文中的T也就是循环神经网络图中绿色圆圈的个数
得到一个词汇表vocab如下

复制代码
    ['<unk>', ' ', 'e', 't', 'a', 'i', 'n', 'o', 's', 'h', 'r', 'd', 'l', 'm', 'u', 'c', 'f', 'w', 'g', 'y', 'p', 'b', 'v', 'k', 'x', 'z', 'j', 'q']
    
    
    python
    
    

vocab是一个28维的向量,26个英文字母加上一个空格和用来表示其他符号的 ‘< unk >’ 。

很多读者可能不理解batch_size和num_steps。我们下面用一个简单的例子来理解
假设我们有一个用于训练的序列,如果我们每次只输入一个字符,那么是非常浪费时间的,如何能高效的使用训练数据呢?
我们定义num_steps = 5,那么就按照5这个长度将这个序列划分为下面的5块,然后我么将这5块装进一个盒子里。现在我们定义batch_size = 2,那么我就每次从盒子中取两块放在一起,你可以按顺序取,也可以随机取。
在这里插入图片描述
假设我们随机取得“the t”,“e by ”这两块,那么我们就得到了一个2X5的矩阵X
X=\left[ \begin{array}{c} ‘t’ & ‘h’ & ‘e’ & ‘\;’ & ‘t’\\ ‘e’ & ‘\;’ & ‘b’ & ‘y’ & ‘\;’\\ \end{array} \right]
如果用Y表示训练数据的标签的话,就是取与X的每一个元素在序列中的后一个元素,因为我们是用前一个字符预测下一个字符,得到的Y就如下:
Y=\left[ \begin{array}{c} ‘h’ & ‘e’ & ‘\;’ & ‘t’ & ‘i’\\ ‘\;’ & ‘b’ & ‘y’ & ‘\;’ & ‘h’\\ \end{array} \right]

回到我们刚才的代码中的train_iter,其结构如下:

复制代码
    (tensor([[ 3,  9,  2,  ...,  2,  1,  3],
        [15,  4, 14,  ...,  4,  6, 11],
        ...,
        [ 3,  1,  4,  ..., 12,  2, 13],
        [ 9,  2,  1,  ...,  4,  5, 10]]),
        tensor([[ 9,  2,  1,  ...,  1,  3,  5],
        [ 4, 14, 18,  ...,  6, 11, 20],
        ...,
        [ 1,  4,  1,  ...,  2, 13,  1],
        [ 2,  1, 13,  ...,  5, 10,  1]]))
    
    
    python
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Pt4xgjY9ov5IRVBNmeJQ6WnfH1FZ.png)

可以看到它是(tensor,tensor)这样的结构,你可能已经发现了,(tensor,tensor) = (X, Y),他们都是一个32X35的矩阵,矩阵中的每一个元素都是一个英文字母在vocab里面的映射。这就是我们输入RNN模型循环一次的输入数据。现在 我们来计算一下train_iter中有多少个(tensor, tensor),我们有10000个词元,num_steps=35,那么我们得到的分块为10000/35 = 285 ,batch_size = 32, 那么我们最终得到了285/32 = 8个(tensor, tensor)

但是如果使用上面的矩阵作为输入,神经网络是无法学习到好的模式的,我们通常会使用one-hot编码来实现

复制代码
    F.one_hot(torch.tensor([0, 2]), len(vocab))
    给定一个序列[0,2] 和 vocab向量,函数会把 0 生成为一个维度大小与vocab相同的向量,
    向量中的1代表的是0在vocab中的位置,其他全为0
    tensor([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0],
        [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0]])
    
    
    python
    
    

模型细节
在这里插入图片描述
每一个时间步包含了n个神经元

四、门控循环单元(GRU)

关注一个序列

  • 不是每一个观察值都同等重要
  • 只记住相关的观察需要:能关注的机制(更新门);能遗忘的机制(重置门)


R_t = \sigma(X_tW_{xr} + H_{t-1}W_{hr} + b_r)\\ Z_t = \sigma(X_tW_{xz} + H_{t-1}W_{hz} + b_r)
在这里插入图片描述
候选隐藏状态
\tilde{H_t} = tanh(X_tW_{xh} + (R_t \bigodot H_{t-1}) W_{hh} + b_h)
在这里插入图片描述
隐状态
H_t = Z_t \bigodot H_{t-1} + (1-Z_t) \bigodot \tilde{H_t}
在这里插入图片描述
总结
\begin{array}{l} R_t = \sigma(X_tW_{xr} + H_{t-1}W_{hr} + b_r)\\ Z_t = \sigma(X_tW_{xz} + H_{t-1}W_{hz} + b_r)\\ \tilde{H_t} = tanh(X_tW_{xh} + (R_t \bigodot H_{t-1}) W_{hh} + b_h)\\ H_t = Z_t \bigodot H_{t-1} + (1-Z_t) \bigodot \tilde{H_t} \end{array}

五、长期记忆网络(LSTM)

长期记忆网络包含三部分关键的控制门

  • 遗忘门:将值朝0减少
  • 输入门:决定不是忽略掉输入数据
  • 输出门:决定是不是使用隐状态


I_t = \sigma(X_tW_{xi} + H_{t-1}W_{hi} + b_i)\\ F_t = \sigma(X_tW_{xf} + H_{t-1}W_{hf} + b_f)\\ O_t = \sigma(X_tW_{xo} + H_{t-1}W_{ho} + b_o)\\
在这里插入图片描述
候选记忆单元

\tilde{C_t} = tanh(X_tW_{xc} + H_{t-1}W_{hc} + b_c)
在这里插入图片描述
记忆单元
C_t = F_t \bigodot C_{t-1} + I_t \bigodot \tilde{C_t}
在这里插入图片描述
隐状态
H_t = O_t \bigodot tanh(C_t)
在这里插入图片描述
总结
\begin{array}{l} I_t = \sigma(X_tW_{xi} + H_{t-1}W_{hi} + b_i) \\ F_t = \sigma(X_tW_{xf} + H_{t-1}W_{hf} + b_f) \\ O_t = \sigma(X_tW_{xo} + H_{t-1}W_{ho} + b_o) \\ \tilde{C_t} = tanh(X_tW_{xc} + H_{t-1}W_{hc} + b_c) \\ C_t = F_t \bigodot C_{t-1} + I_t \bigodot \tilde{C_t} \\ H_t = O_t \bigodot tanh(C_t) \end{array}
LSTM与RNN的区别就是RNN试图记住前面所有的内容来预测后一个内容,而LSTM因为引入了一个记忆单元即C,通过记忆单元使得LSTM有能力自由选择每个时间步里面记忆的内容。
假设现在有一个场景:期末考试周,我们在每一个时间步要考一门课程。假设我们到了第t步,现在要靠线性代数,那么我们的输入就是X_t 就是我们复习的线性代数的内容,中间的过程就是我们复习过程中形成的记忆,考完线性代数后我们就会形成一个线代的记忆C_t和状态H_t , 假设C_{t-1}H_{t-1}是我们考完高等数学之后的记忆和状态。

首先是遗忘门 F_t:我们现在复习的是线性代数,那么我们就要尽可能的将C_{t-1}高数中和线性代数无关的那一部分的内容遗忘掉,好腾出空间来记忆线性代数的内容,那么如何做到这一点呢?我们得到的F_t是一个n维的向量(n的大小与你的记忆单元的维度相同),每一个维度的值都是0到1之间 F_t=[0,0.1,0.15,0.98,…,0.23] , 我们做运算 F_t\bigodot C_{t-1} ,即将两个向量对应元素相乘,达到的效果就是我们把高数中与现代无关的部分尽量忘掉,而与现代相关的部分保留下来。
然后是更新门 I_t\tilde{C_t}是通过输入学习到的新的记忆,但是这个记忆并不是都有用。例如我们在复习线性代数的时候,我们学习的知识可能包含与考点无关的内容,对我们的考试没有帮助。我们就通过I_t来过滤,通过做计算I_t\bigodot \tilde{C_t}。然后我们将高数中有用的知识和当下与考点相关的内容结合起来,就形成了新的记忆C_t=F_t\bigodot C_{t-1} + I_t\bigodot \tilde{C_t}
最后是输出门 O_t:当我们带着C_t去考试的时候,我们首先做一个tanh(C_t)的操作,即将我们记忆的内容转化为我们答题的能力,但是我们考试的内容并不是包含了我们记忆的所有知识,那么我们需要做计算O_t\bigodot tanh(C_t)用到部分的知识来答题。

全部评论 (0)

还没有任何评论哟~