Ai目录:sheng的学习笔记-AI目录-CSDN博客
基础知识
定义:
序列模型是输入输出均为序列数据的模型,它能够将输入序列数据转换为目标序列数据。常见的序列模型类型包括一对一、一对多、多对一、部分多对多和完全多对多。
重要的是需要有顺序,普通的模型对于采样数据不需要有顺序,可以打乱,但序列模型的输入必须是有序的
应用场景
- 在进行语音识别时,给定了一个输入音频片段X ,并要求输出对应的文字记录Y 。这个例子里输入和输出数据都是序列模型,因为X是一个按时播放的音频片段,输出Y是一系列单词
- 音乐生成问题是使用序列数据的另一个例子,在这个例子中,只有输出数据是序列,而输入数据可以是空集,也可以是个单一的整数,这个数可能指代你想要生成的音乐风格,也可能是你想要生成的那首曲子的头几个音符。输入的可以是空的,或者就是个数字,然后输出序列 。
- 在处理情感分类时,输入数据 是序列,你会得到类似这样的输入:“There is nothing to like in this movie.”,你认为这句评论对应几星?
- 在机器翻译过程中,你会得到这样的输入句:“Voulez-vou chante avecmoi?”(法语:要和我一起唱么?),然后要求你输出另一种语言的翻译结果。
- 在进行视频行为识别时,你可能会得到一系列视频帧,然后要求你识别其中的行为。
- 在进行命名实体识别时,可能会给定一个句子要你识别出句中的人名。
循环神经网络模型(Recurrent Neural Network Model)
为啥不用标准的神经网络
如何建立一个神经网络模型,学习x到y的映射
可以尝试的方法之一是使用标准神经网络,在我们之前的例子中,我们有9个输入单词。想象一下,把这9个输入单词,可能是9个one-hot向量(文字转为字典向量,详细见nlp文章),然后将它们输入到一个标准神经网络中,经过一些隐藏层,最终会输出9个值为0或1的项,它表明每个输入单词是否是人名的一部分。
主要有2个问题:
啥是循环神经网络(Recurrent Neural Network)
RNN流程图
RNN公式
RNN简化公式
RNN前向传播示意图
RNN反向传播示意图
在这个反向传播的过程中,最重要的信息传递或者说最重要的递归运算就是这个从右到左的运算,这也就是为什么这个算法有一个很别致的名字,叫做“通过(穿越)时间反向传播(backpropagation through time)”。取这个名字的原因是对于前向传播,你需要从左到右进行计算,在这个过程中,时刻不断增加。而对于反向传播,你需要从右到左进行计算,就像时间倒流。“通过时间反向传播”,就像穿越时光,这种说法听起来就像是你需要一台时光机来实现这个算法一样
RNN前向和反向传播组合
前向传播:上图蓝色箭头所指方向,在神经网络中从左到右地计算这些激活项,直到输出所有地预测结果。
反向传播:反向传播地计算方向(上图红色箭头所指方向)与前向传播基本上是相反的
循环神经网络的梯度消失
基本的RNN算法还有一个很大的问题,就是梯度消失的问题。
看到这个句子,“The cat, which already ate ……, was full.”,前后应该保持一致,因为cat是单数,所以应该用was。“The cats, which ate ……, were full.”,cats是复数,所以用were。这个例子中的句子有长期的依赖,最前面的单词对句子后面的单词有影响。但是我们目前见到的基本的RNN模型,不擅长捕获这种长期依赖效应
在一个很深的神经网络中,会出现梯度爆炸和梯度消失。在反向传播的时候,随着层数的增多,梯度不仅可能指数型的下降,也可能指数型的上升
梯度爆炸
会展示的很明显,指数级大的梯度会让你的参数变得极其大,以至于你的网络参数崩溃。所以梯度爆炸很容易发现,因为参数会大到崩溃,你会看到很多NaN,或者不是数字的情况,这意味着你的网络计算出现了数值溢出。
如果你发现了梯度爆炸的问题,一个解决方法就是用梯度修剪。梯度修剪是观察你的梯度向量,如果它大于某个阈值,缩放梯度向量,保证它不会太大,这就是通过一些最大值来修剪的方法。所以如果你遇到了梯度爆炸,如果导数值很大,或者出现了NaN,就用梯度修剪,这是相对比较鲁棒的,这是梯度爆炸的解决方法
梯度消失
前面计算的结果,随着层数的增多,很难影响后续层的计算,通常用门控循环单元(Gated Recurrent Unit)或者长短期记忆(LSTM(long short term memory)unit)解决
不同类型的循环神经网络
上述的架构,它的输入量Tx等于输出数量Ty。事实上,对于其他一些应用,Tx和Ty并不一定相等。
比如音乐生成这个例子,Tx可以是长度为1甚至为空集。再比如电影情感分类,输出可以是1到5的整数,而输入是一个序列
还有一些情况,输入长度和输出长度不同,他们都是序列但长度不同,比如机器翻译,一个法语句子和一个英语句子不同数量的单词却能表达同一个意思
“多对一”(many-to-one)
你想处理情感分类问题,这里可能是一段文本,比如一个电影的评论,“These is nothing to like in this movie.”(“这部电影没什么还看的。”),所以就是一个序列,而可能是从1到5的一个数字,或者是0或1,这代表正面评价和负面评价,而数字1到5代表电影是1星,2星,3星,4星还是5星
“一对一”(one-to-one)的结构
这就是一个小型的标准的神经网络,输入x然后得到输出y
“一对多”(one-to-many)
音乐生成,你的目标是使用一个神经网络输出一些音符。对应于一段音乐,输入可以是一个整数,表示你想要的音乐类型或者是你想要的音乐的第一个音符,得到RNN的输出,第一个值,然后就没有输入了,再得到第二个输出,接着输出第三个值等等,一直到合成这个音乐作品的最后一个音符
“多对多”
输入和输出长度不同的情况,像机器翻译这样的应用,一个法语的句子,和输出句子的单词数量,翻译成英语,这两个句子的长度可能不同
先读入这个句子,读入这个输入,比如你要将法语翻译成英语,读完之后,这个网络就会输出翻译结果。有了这种结构Tx和Ty就可以是不同的长度了。
情况1:有可能输入所有的数据,然后返回所有的输出
情况2:输入一个x1,就得到一个输出y1,在输入第二个x2,得到输出y1和y2
门控循环单元(GRU Gated Recurrent Unit)
这是普通的流程图,这一段贼复杂,多看几遍
原理
对于 “The cat, which already ate……, was full.” 这句话:
完整公式
注意,上面最后h的公式是错的,应该是 (1-门u)+c. 改成(1-门u)*c,+改成*
辅助理解
理解一下,这里的 c<t>代表一系列相关参数,比如cat,was是相关的,如果是相关的,就更新(值为1),如果是不相关的,就不更新(值为0)。在不更新的时候,会记录相关参数,避免在深层次网络中遗忘。这是从别的地方搞来的图,感受一下,里面的h就是上文提到的c
长短期记忆(LSTM(long short term memory)unit)
原理
“窥视孔连接”(peephole connection)
最常用的版本可能是门值不仅取决于和,有时候也可以偷窥一下的值(上图编号13所示),这叫做“窥视孔连接”(peephole connection)。“偷窥孔连接”其实意思就是门值不仅取决于和,也取决于上一个记忆细胞的值(),然后“偷窥孔连接”就可以结合这三个门来计算了。
如你所见LSTM主要的区别在于一个技术上的细节,比如这(上图编号13所示)有一个100维的向量,你有一个100维的隐藏的记忆细胞单元,然后比如第50个的元素只会影响第50个元素对应的那个门,所以关系是一对一的,于是并不是任意这100维的可以影响所有的门元素。相反的,第一个的元素只能影响门的第一个元素,第二个元素影响对应的第二个元素,如此类推。但如果你读过论文,见人讨论“偷窥孔连接”,那就是在说也能影响门值。
LSTM前向传播图:
LSTM反向传播计算:
门求偏导:
参数求偏导 :
别的地方弄得图,感受一下
GRU和LSTM对比
GRU的优点是这是个更加简单的模型,所以更容易创建一个更大的网络,而且它只有两个门,在计算性上也运行得更快,然后它可以扩大模型的规模。
但是LSTM更加强大和灵活,因为它有三个门而不是两个。如果你想选一个使用,我认为LSTM在历史进程上是个更优先的选择,所以如果你必须选一个,我感觉今天大部分的人还是会把LSTM作为默认的选择来尝试。虽然我认为最近几年GRU获得了很多支持,而且我感觉越来越多的团队也正在使用GRU,因为它更加简单,而且还效果还不错,它更容易适应规模更加大的问题。
双向循环神经网络(Bidirectional RNN)
当需要预测y的时候,不能只看前面的数据,还需要看后面的数据,就需要用BRNN
这就是双向循环神经网络,并且这些基本单元不仅仅是标准RNN单元,也可以是GRU单元或者LSTM单元。事实上,很多的NLP问题,对于大量有自然语言处理问题的文本,有LSTM单元的双向RNN模型是用的最多的。所以如果有NLP问题,并且文本句子都是完整的,首先需要标定这些句子,一个有LSTM单元的双向RNN模型,有前向和反向过程是一个不错的首选。
以上就是双向RNN的内容,这个改进的方法不仅能用于基本的RNN结构,也能用于GRU和LSTM。通过这些改变,你就可以用一个用RNN或GRU或LSTM构建的模型,并且能够预测任意位置,即使在句子的中间,因为模型能够考虑整个句子的信息。这个双向RNN网络模型的缺点就是你需要完整的数据的序列,你才能预测任意位置。比如说你要构建一个语音识别系统,那么双向RNN模型需要你考虑整个语音表达,但是如果直接用这个去实现的话,你需要等待这个人说完,然后获取整个语音表达才能处理这段语音,并进一步做语音识别。对于实际的语音识别的应用通常会有更加复杂的模块,而不是仅仅用我们见过的标准的双向RNN模型。但是对于很多自然语言处理的应用,如果你总是可以获取整个句子,这个标准的双向RNN算法实际上很高效。
深层循环神经网络(Deep RNNs)
预测未来几个时间步长
通常,时间序列预测描述了预测下一个时间步长的观测值。这被称为“一步预测”,因为仅要预测一个时间步。在一些时间序列问题中,必须预测多个时间步长。与单步预测相比,这些称为多步时间序列预测问题
方案一
选择是使用已经训练好的模型,使其预测下一个值,然后将该值加到输入中(就像这个预测值实际上已经有了),然后再次使用该模型预测后面的值,以此类推
series = generate_time_series(1, n_steps + 10)
X_new, Y_new = series[:, :n_steps], series[:, n_steps:]
X = X_new
for step_ahead in range(10):y_pred_one = model.predict(X[:, step_ahead:])[:, np.newaxis, :]X = np.concatenate([X, y_pred_one], axis=1)Y_pred = X[:, n_steps:]
由于误差可能会累积,因此对下一步长的预测通常会比对未来几个时间步长的预测更为准确
方案二
训练RNN一次预测所有10个值。我们仍然可以使用一个序列到向量的模型,但是它输出10个值而不是1个值
series = generate_time_series(10000, n_steps + 10)
X_train, Y_train = series[:7000, :n_steps], series[:7000, -10:, 0]
X_valid, Y_valid = series[7000:9000, :n_steps], series[7000:9000, -10:, 0]
X_test, Y_test = series[9000:, :n_steps], series[9000:, -10:, 0]model = keras.models.Sequential([keras.layers.SimpleRNN(20, return_sequences=True, input_shape=[None, 1]),keras.layers.SimpleRNN(20),keras.layers.Dense(10)
])Y_pred = model.predict(X_new)
得到的y中有10个变量(代表10个时间步骤的值,比如明天一个值,后天一个值,大后天一个值)
方案三
训练模型在每个时间步长来预测下一个10个值。换句话说,我们可以将这个序列到向量的RNN转换为序列到序列的RNN。这种技术的优势在于,损失将包含每个时间步长的RNN输出项,而不仅仅是最后一个时间步长的输出项。这意味着将有更多的误差梯度流过模型,它们不需要随时间而“流淌”。它们从每个时间步长的输出中流出。这会稳定和加速训练
模型在时间步长0时会输出一个向量,其中包含时间步长1到10的预测,然后在时间步长1时模型会预测时间步长2到11,以此类推。因此,每个目标必须是与输入序列长度相同的序列,在每个步长都必须包含10维向量
要将模型转换为序列到序列的模型,我们必须在所有循环层(甚至最后一层)中设置return_sequen ces=True,必须在每个时间步长都应用输出Dense层。Keras为此提供了一个TimeDistributed层:它包装了任何层(例如Dense层),在输入序列的每个时间步长应用这个层。它通过重构输入的形状来有效地做到这一点,以便每个时间步长都被视为一个单独的实例(将输入的形状从[批量大小,时间步长,输入维度]调整为[批量大小×时间步长,输入维度]。在此示例中,输入维数为20,因为前一个SimpleRNN层有20个单元),然后运行Dense层,最后将输出重构为序列(将输出从[批量大小×时间步长,输出维度]重构为[批量大小,时间步长,输出维度]。在此示例中,由于Dense层有10个单元,因此输出维度的数量为10)[1]。这是更新的模型
Y = np.empty((10000, n_steps, 10)) # each target is a sequence of 10D vectors
for step_ahead in range(1, 10 + 1):Y[:, :, step_ahead - 1] = series[:, step_ahead:step_ahead + n_steps, 0]
Y_train = Y[:7000]
Y_valid = Y[7000:9000]
Y_test = Y[9000:]
model = keras.models.Sequential([keras.layers.SimpleRNN(20, return_sequences=True, input_shape=[None, 1]),keras.layers.SimpleRNN(20, return_sequences=True),keras.layers.TimeDistributed(keras.layers.Dense(10))
])
Dense层实际上支持序列作为输入(甚至是更高维的输入):它像TimeDistributed(Dense(…))一样处理它们,这意味着它仅仅应用于最后一个输入维度(独立于所有的时间步长)。因此,我们可以用Dense(10)代替最后这一层。但是为了清楚起见,我们将继续使用TimeDistributed(Dense(10)),因为它清楚地表明在每个时间步长都独立应用了Dense层,并且模型将输出一个序列,而不仅仅是一个向量
参考文章:
RNN及其变体(LSTM、GRU)的介绍_gru是rnn-CSDN博客
吴恩达深度学习,序列模型的视频