Seq2Seq模型概述
全称Sequence to Sequence模型,字面意思,输入一个序列,输出一个序列。输入和输出的序列长度是可变的。有的模型需要填充(padding)到特定长度
e.g. 输入长度为5,输出长度为4;输入了5个汉字,输出了4个英文单词
图片来源:https://github.com/Spr1ng7/fun-transformer/raw/main/docs/chapter1/images/image%EF%BC%881%EF%BC%89.PNG
而Seq2Seq模型就不需要padding,用BOS标识序列开始,EOS标识序列结束
在处理可变长度的序列时,使用 BOS 和 EOS 可以减少对 填充(padding)的依赖,因为模型可以根据这些标记来识别序列的边界。
:Begin Of Sequence(BOS)
:End Of Sequence(EOS)
“BOS I have a pen EOS”,在这里,BOS 和 EOS 分别标记句子的开始和结束。
关于固定长度(摘自讲义):
在 Seq2Seq 框架提出之前,深度神经网络在图像分类等问题上取得了非常好的效果。在其擅长解决的问题中,输入和输出通常都可以表示为固定长度的向量,如果长度稍有变化,会使用填充(Padding)等操作,其目的是将序列数据转换成固定长度的格式,以便于输入到需要固定长度输入的神经网络中。
Padding可能引入的问题
例如在计算损失时可能需要特殊的处理来忽略这些补零的元素,以免它们影响模型的训练效果。这通常通过使用掩码(Mask)来实现,掩码可以指示哪些位置是补零的,以便在计算过程中忽略这些位置。
然而许多重要的问题,例如机器翻译、语音识别、对话系统等,表示成序列后,其长度事先并不知道。因此如何突破先前深度神经网络的局限,使其可以适应这些场景,成为了2013年以来的研究热点,Seq2Seq框架应运而生。
Seq2Seq模型的优缺点
优点:端到端学习,处理可变长度序列,信息压缩与表示,可扩展性
缺点:上下文信息量压缩,短期记忆限制,暴露者偏差
-
优点
- 端到端学习
Seq2Seq模型实现了从原始输入数据(如文本、语音等)到期望输出序列的直接映射,无需进行显式的特征提取或工程化步骤
能直接从原始的文本或语音等数据中学习,然后直接输出我们想要的结果,整个过程不需要人工去提取特征或进行复杂的预处理- 处理可变长度序列
模型具备处理输入和输出序列的能力,能够适应不同长度的序列数据。
- 信息压缩与表示
编码器通过编码过程将输入序列压缩为一个固定维度的上下文向量,该向量作为输入序列的抽象表示,解码器基于此上下文向量进行解码操作以生成目标输出序列。
编码器把一大段文字浓缩成一个简短的摘要,它把整个输入序列压缩成一个精华的上下文向量。然后,解码器根据这个摘要重新创作出一篇完整的文章。- 可扩展性
Seq2Seq模型具有良好的模块化特性,能够与卷积神经网络(CNNs)、循环神经网络(RNNs)等神经网络架构无缝集成,以应对更加复杂的数据处理任务和场景。
Seq2Seq模型就像一个模块化的工具箱,可以轻松地与其他神经网络技术(比如卷积神经网络或循环神经网络)结合,这样一来,它就能处理更加复杂和多样化的任务,能力更加强大。 -
缺点
- 上下文信息量压缩
输入序列的全部信息需要被编码成一个固定维度的上下文向量,这导致了信息压缩和信息损失的问题,尤其是细粒度细节的丢失
(信息打包问题)想象一下,你试图把一本厚厚的百科全书的内容全部塞进一个小小的记忆卡片里。Seq2Seq模型在做类似的事情,它需要把整个输入序列的信息压缩成一个固定大小的上下文向量。这就好比你只能记住百科全书的概要,而丢失了很多细致入微的细节。- 短期记忆限制
由于循环神经网络(RNN)的固有特性,Seq2Seq模型在处理长序列时存在短期记忆限制,难以有效捕获和传递长期依赖性。这限制了模型在处理具有长距离时间步依赖的序列时的性能。
Seq2Seq模型有时候就像一个有短期记忆障碍的人,它很难回忆起很久以前发生的事情。这意味着,当处理很长的序列时,模型往往难以捕捉到序列开始和结束之间的长期依赖关系,就像试图回忆一个长故事的每一个细节一样困难。- 暴露偏差Exposure Bias
在Seq2Seq模型的训练过程中,经常采用“teacher forcing”策略,即在每个时间步提供真实的输出作为解码器的输入(相当于训练的时候就告诉模型正确答案,但是真正使用模型的时候没有正确答案可以输入,只有模型自身上一步的输出,这时模型的运行结果就会出和预期现偏差)。然而,这种训练模式与模型在推理时的自回归生成模式存在不一致性,导致模型在测试时可能无法很好地适应其自身的错误输出,这种现象被称为暴露偏差。
下文摘自http://www.sniper97.cn/index.php/note/deep-learning/note-deep-learning/4265/
出现exposure bias的主要原因是由于训练阶段采用的是Teacher Forcing,而才测评或者生成时,我们基本都采用贪心或者beam search的方法,这就造成了训练和测评上的一些gap。在训练阶段,模型总是能获取上一步的正确答案。
这就像我们在上学时,老师带你做题很容易就做出来,因为老师每一步在引导你时,上一步都是正确答案,而到了自己做题时,由于没有办法确保上一步的正确性,因此就可能会出现一些错误。
Encoder-Decoder模型
Seq2Seq模型和Encoder-Decoder模型的关系
Seq2Seq 模型(强调目的):不特指具体方法,而是以满足“输入序列、输出序列”的目的。而 Seq2Seq 使用的具体方法基本都属于Encoder-Decoder 模型(强调方法)的范畴。
- Seq2Seq模型是Encoder-Decoder架构的一种具体应用
- Seq2Seq 更强调目的,Encoder-Decoder 更强调方法
Encoder-Decoder将现实问题转化为数学问题,通过求解数学问题,从而解决现实问题。 Encoder 又称作编码器。它的作用就是“将现实问题转化为数学问题”。
Decoder 又称作解码器,他的作用是“求解数学问题,并转化为现实世界的解决方案”。
关于 Encoder-Decoder的2点说明(摘自讲义):
不论输入和输出的长度是什么,中间的“上下文向量 ” 长度都是固定的(这也是它的缺陷,下文会详细说明)
根据不同的任务可以选择不同的编码器和解码器(可以是一个 RNN ,但通常是其变种 LSTM 或者 GRU ) 只要是符合上面的框架,都可以统称为 Encoder-Decoder 模型
Encoder-Decoder的工作流程
Encoder(编码器)
编码器是一个循环神经网络(RNN),由多个循环单元(如 LSTM 或 GRU)堆叠而成。每个单元通过依次处理输入序列中的一个元素,生成一个上下文向量。这个上下文向量汇总了输入序列的全部信息,它通过在单元间传递当前元素的信息,逐步构建对整个序列的理解。
kimi解释:
这段描述的是Seq2Seq模型中的编码器部分,它主要负责处理输入序列并生成一个能够代表整个输入序列信息的上下文向量。下面是对这段描述的详细解释:
- 循环神经网络(RNN):编码器是基于循环神经网络构建的,RNN是一种适合于处理序列数据的神经网络,因为它能够在处理序列中的每个元素时,将之前元素的信息保留下来。
- 循环单元:RNN可以由多种类型的循环单元构成,最常见的两种是长短期记忆单元(LSTM)和门控循环单元(GRU)。这些循环单元设计用来解决传统RNN在处理长序列时的梯度消失或梯度爆炸问题,能够更好地捕捉序列中长期依赖的信息。
- 处理输入序列:编码器通过依次处理输入序列中的每个元素(例如,一句话中的每个单词)来逐步构建对整个序列的理解。在处理每个元素时,循环单元会更新自己的状态,这个状态包含了之前所有元素的信息。
- 上下文向量:经过整个输入序列的处理后,编码器生成一个上下文向量(也称为隐状态向量)。这个向量是对输入序列的压缩表示,它包含了构建对整个输入序列理解所需的所有信息。
- 信息传递:在循环单元间传递信息的过程,实际上是通过单元的状态实现的。每个单元的状态都是基于当前元素和前一个单元状态计算得出的,这样就能够逐步构建起对整个序列的理解。
总结来说,Seq2Seq模型中的编码器是一个由多个循环单元组成的RNN,它负责读取输入序列,并生成一个包含整个序列信息的上下文向量,为后续的解码过程提供必要的信息。
关于梯度消失和梯度爆炸
(由kimi生成)
好的,让我们用一些通俗的语言来解释梯度消失和梯度爆炸这两个概念。梯度消失
想象一下,你在玩一个游戏,游戏里有一个小球在一个山谷里滚动。小球的目标是滚到山谷的最底部,也就是最低点。在这个游戏里,小球每滚一小步,都会根据斜坡的陡峭程度(也就是梯度)来决定下一步的方向。斜坡的陡峭程度就是告诉我们小球应该怎样调整方向,以便更快地滚到谷底。
现在,如果山谷非常平缓,斜坡的陡峭程度(梯度)就会非常小。这意味着每次小球滚动时,它几乎感觉不到斜坡的存在,因此它几乎不会改变方向。这就是“梯度消失”的情况。在神经网络中,如果梯度(也就是告诉我们如何调整权重以改善模型的信号)太小,那么在训练过程中,权重的更新就会非常微小,导致学习过程变得非常缓慢,就像小球几乎滚不动一样。梯度爆炸
还是用刚才的游戏来比喻。现在,想象山谷的某些地方非常陡峭,小球一旦滚到那里,就会以极快的速度滚下去,几乎无法控制。如果斜坡太陡,小球可能会一下子滚得太远,甚至可能飞出山谷,完全失去控制。
在神经网络中,“梯度爆炸”就是指梯度(斜坡的陡峭程度)变得非常大,导致权重更新得太大,从而使模型的参数一下子变化很多,这可能会让模型的输出变得极其不稳定,甚至导致模型完全无法学习。就像小球在陡峭的斜坡上滚得太快,最后可能飞出山谷一样。
简而言之,梯度消失就像是小球在平缓的斜坡上滚得太慢,而梯度爆炸则是小球在陡峭的斜坡上滚得太快,两者都不利于小球(或者说神经网络)达到目标(谷底)。
编码器的工作包括 1.词序列转换 2.序列处理 3.生成上下文向量
- 词序列转换
在Seq2Seq模型的初始阶段,输入文本(例如一个句子)通过一个嵌入层(embedding layer)进行转换。这一层将每个词汇映射到一个高维空间中的稠密向量,这些向量携带了词汇的语义信息。这个过程称为词嵌入(word embedding),它使得模型能够捕捉到词汇之间的内在联系和相似性。
假设我们有一个简单的词汇表,包含以下四个词:{“猫”,“狗”,“爱”,“跑”}。在词嵌入之前,这些词可能只是用数字索引来表示,例如:
“猫” = 0 “狗” = 1
“爱” = 2
“跑” = 3
这种表示方式没有包含词汇的语义信息。
现在,我们使用词嵌入将这些词映射到一个三维空间中(实际上,词嵌入通常是更高维度的,这里为了简化说明,我们使用三维)。每个词汇将会有一个对应的稠密向量,例如:
“猫” = [0.3, 0.4, 0.25]
“狗” = [0.35, 0.45, 0.3]
“爱” = [0.8, 0.8, 0.8]
“跑” = [0.1, 0.2, 0.6]
在这个例子中,每个词汇被表示为一个三维向量。
这些向量在多维空间中的位置是根据词汇的语义相似性来确定的。例如,“猫”和“狗”在语义上比较接近,所以它们在三维空间中的向量可能也比较接近。而“爱”和“跑”可能在语义上与“猫”和“狗”有较大的差异,因此它们在空间中的位置也会相对较远。
这种稠密向量的表示方式使得词汇之间的关系可以通过向量之间的距离或角度来度量。例如,如果我们计算“猫”和“狗”的向量之间的距离,我们会发现它们比“猫”和“爱”的向量之间的距离要近,这反映了“猫”和“狗”在语义上的相似性。
在实际应用中,词嵌入通常是在一个更高的维度空间中进行的,比如50维、100维、甚至更高,这样可以更准确地捕捉词汇的复杂语义关系。
讲义代码实现解释
下图为讲义提供的代码实现解释效果:
词汇序列转换代码示例(讲义提供):
# 定义词汇及其对应的三维向量
words = ["猫", "狗", "爱", "跑"]
vectors = [[0.3, 0.4, 0.25], [0.35, 0.45, 0.3], [0.8, 0.8, 0.8], [0.1, 0.2, 0.6]]
# 将嵌套列表转换为numpy数组
vectors = np.array(vectors)
# 创建3D图形
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')
# 为每个词汇及其向量绘图
scatter = ax.scatter(vectors[:, 0], vectors[:, 1], vectors[:, 2],
c=['blue', 'green', 'red', 'purple'], s=100)
# 为每个点添加标签
for i, word in enumerate(words):
ax.text(vectors[i, 0], vectors[i, 1], vectors[i, 2], word, size=15, zorder=1)
#略去了绘制坐标图的代码
- 序列处理
随后,这些经过嵌入的向量序列被送入一个基于循环神经网络(RNN)的结构中,该结构可能由普通的RNN单元、长短期记忆网络(LSTM)单元或门控循环单元(GRU)组成。RNN的递归性质允许它处理时序数据,从而捕捉输入序列中的顺序信息和上下文关系。在每个时间步,RNN单元不仅处理当前词汇的向量,还结合了来自前一个时间步的隐藏状态信息 - 生成上下文向量
经过一系列时间步的计算,RNN的最后一个隐藏层输出被用作整个输入序列的表示,这个输出被称为“上下文向量(context vector)”。上下文向量是一个固定长度的向量,它通过汇总和压缩整个序列的信息,有效地编码了输入文本的整体语义内容。这个向量随后将作为Decoder端的重要输入,用于生成目标序列。
Decoder(解码器)
解码器同样采用递归神经网络(RNN)架构,它接收编码器输出的上下文向量作为其初始输入,并依次合成目标序列的各个元素。