动手学深度学习(五)循环神经网络RNN
一、序列模型
1、统计工具
①联合概率分布
假设有一个序列 x=[x1,x2,…,xT],我们可以把序列的联合概率分解为多个条件概率的乘积。
②建模
f(x1,…,xt−1) 是一个函数,用于提取前 t−1个序列元素的信息。这意味着我们不需要存储每一个之前的序列元素,而是用函数 fff 来总结之前的状态。
2、自回归模型
自回归模型假设时间序列的当前值可以用其过去的一些值来表示。对于一个时间序列x_t,自回归模型的数学表达式如下:
在自回归模型中,当前时刻的值 xt是过去几个时刻值的加权和,加上一个随机误差项。这意味着模型假设当前的状态主要受历史状态的线性组合影响,而误差项捕捉了其他未解释的随机波动。
3、马尔可夫模型
序列中的当前状态只依赖于有限数量的过去状态,而不是依赖于所有的历史状态。这个依赖时刻的长度可以用来表示。
而一阶马尔科夫模型,是最常见的马尔科夫模型是假设当前状态只依赖于前一个时刻的状态。也就是当前时刻x_t只依赖于x_t-1,与更早的时刻无关。这个假设可以表示为:
4、潜变量模型
二、文本预处理
1、读取数据集
2、词元化
词元(token)是文本的基本单位,token可以是Word为单位,也可以是字符为单位。
3、词表
①词表
词元的类型是字符串,而模型需要的输入是数字,因此这种类型不方便模型使用。 [构建一个字典,通常也叫做词表(vocabulary), 用来将字符串类型的词元映射到从0开始的数字索引中]。
②语料
我们先将训练集中的所有文档合并在一起,对它们的唯一词元进行统计, 得到的统计结果称之为语料(corpus)。
③词表的构建
先统计整体的语料,再根据每个唯一词元的出现频率,为其分配一个数字索引。 很少出现的词元通常被移除,这可以降低复杂性。 另外,语料库中不存在或已删除的任何词元都将映射到一个特定的未知词元“<unk>”。 我们可以选择增加一个列表,用于保存那些被保留的词元, 例如:填充词元(“<pad>”); 序列开始词元(“<bos>”); 序列结束词元(“<eos>”)。
三、语言模型
齐普夫定律(Zipf's Law) 是一种描述自然语言中词频分布的经验法则。它表明,在一个足够大的语料库中,单词的出现频率与其频率排名呈反比关系。
1、语言模型的目的
x1,...,x_t是文本序列中的词元。
2、用计数来建模
①存在的问题
- 序列很长:给定的序列很长时,n(序列)可能会很小甚至于是0,表明这个序列并不存在于文本库中,此时会出现问题。
- 文本量不够大:文本量不够大,n都很小。
②问题解决思路
可以通过引入平滑方法(如拉普拉斯平滑)来避免词频为 0 的问题。即使某个词没有在训练集中出现,也为它赋予一个非零的最小概率。
③仍然存在的问题
首先,需要存储所有的计数。
其次,这完全忽略了单词的意思。例如,“猫”(cat)和“猫科动物”(feline)可能出现在相关的上下文中, 但是想根据上下文调整这类模型其实是相当困难的。
最后,长单词序列大部分是没出现过的。
3、N元语法
①一元语法
只考虑每个词的单独概率,完全忽略上下文。
例子:P(人工智能 是 未来 的 发展 方向)
≈ P(人工智能) * P(是) * P(未来) * P(的) * P(发展) * P(方向)
②二元语法
考虑当前词与前一个词之间的关系。
例子:P(人工智能 是 未来 的 发展 方向)
≈ P(人工智能) * P(是|人工智能) * P(未来|是) * P(的|未来) * P(发展|的) * P(方向|发展)
③三元语法
考虑当前词与前两个词的关系。
例子:P(人工智能 是 未来 的 发展 方向)
≈ P(人工智能) * P(是|人工智能) * P(未来|人工智能 是) * ...
4、 随机抽样小批量子序列
①解决的问题
- 计算资源问题:整个序列长度可能很长,直接处理会占用大量的内存和计算资源。
- 梯度消失或爆炸:对于 RNN 等模型,序列过长时,反向传播过程中的梯度可能会逐渐变小(梯度消失)或变大(梯度爆炸),导致训练过程中的数值不稳定。
- 训练效率低:使用完整的序列进行训练,可能导致每个批次的训练速度变慢。
②优点
- 提高训练效率:小批量采样子序列可以更快速地迭代模型训练,每个批次只处理一部分数据,减少了计算量。
- 防止模型过拟合:随机采样使模型每次看到不同的子序列,增强了训练数据的多样性,有助于防止过拟合。
- 避免序列过长导致的问题:通过对序列进行分段,降低了 RNN 等模型的梯度消失或梯度爆炸的可能性。
- 让模型看到更多的上下文组合:每次从不同的位置采样,可以让模型在训练过程中学习到更多不同的上下文关系。
③步骤
- 首先,设定一个超参数
num_steps
,表示每个子序列的长度。这个超参数决定了你想要采样的子序列的大小。 - 然后,通过随机选择序列中的起始位置来获取不同的子序列。每次的随机性体现在每个小批量子序列的第一个词元的起始位置不同。
- 最后,将采样到的子序列打乱顺序,划分成若干个批次,以供训练时使用。通过这样随机采样和打乱顺序的方式,确保模型不会只见到固定的序列,而是能看到更多的组合,提升模型的泛化能力。
5、顺序分区小批量子序列
不打乱子序列,而是保留数据的原始顺序,逐步从序列中按照顺序提取小批量的子序列。这样做的优点确实是可以让模型在训练时观察到更长的上下文序列,并保持数据的顺序性。
四、循环神经网络RNN
1、最简单的RNN的结构
①输入层
图中的 x
表示输入序列,像 "你好,世界!" 这样的文本序列,每个时间步输入一个单词的向量表示。
②隐藏层
h
表示隐藏状态,是 RNN 中的核心部分,用来存储历史信息。隐藏状态在每个时间步更新,表示当前时间步 t 时对之前所有输入的记忆。
③输出层
在每个时间步,RNN 都会基于隐藏状态h_t生成一个输出o_t,并通过某种激活函数来得到最终输出:
2、评估语言模型好坏
①使用交叉熵评估
②困惑度
3、梯度剪裁
①梯度剪裁针对的问题
迭代中计算这T个时间步上的梯度,在反向传播过程中产生长度为 O(T)的矩阵乘法链,导致数值不稳定。
- T是当前处理序列的长度,每个序列元素对应上一个时间步。
- 反向传播的过程中,由于每个时间步都依赖于它前面的一个时间步,因此从最后一个时间步传递到第一个时间步会产生O(T)大小的矩阵乘法链。
②梯度剪裁优点
有效防止梯度爆炸,但与与梯度消失问题无关。
长时间步或深度网络会使梯度在反向传播过程中逐渐累积。如果梯度过大,模型的权重更新会很剧烈,导致训练不稳定或失败。梯度剪裁可以有效限制梯度的增长,防止模型失控。
③梯度剪裁实现
梯度剪裁的基本思想是设置一个阈值,当梯度的范数(即梯度向量的长度)超过该阈值时,将梯度缩放到不超过这个阈值的范围。
4、更多RNN的应用
- One-to-One(单输入单输出):模型从单个输入(如句子或单词)生成单个输出。最常见的场景是文本生成,比如给定一句话生成相关的句子。
- One-to-Many(单输入多输出):从一个输入生成多个输出。用于生成类任务,例如给定一个主题生成多个相关句子。
- Many-to-One(多输入单输出):多个时间步的输入(如一句话中的多个单词)映射到一个输出。适用于文本分类任务。例如,给定一段文字,模型输出一个分类结果(如情感分类:积极、消极、中立)。
- Many-to-Many(多输入多输出):多个时间步的输入对应多个时间步的输出。非常适用于机器翻译、问答系统等任务。例如,给定一句话(源语言),生成相应的翻译(目标语言),或者在给定问题时生成答案。
- Many-to-Many(不同长度)(多输入多输出,输入输出长度不等):不同的是输入序列和输出序列的长度不一致。输入序列长度较短,但输出可能较长,或者相反。这个结构通常用于标签生成(如序列标注任务)。例如,给定一段文本,模型为每个词生成一个标签(如命名实体识别任务:标注人名、地名等)。