S'S ALGORITHM

Transformers 所有关键组件盘点,以及对编码解码的另一种思考


关于认知和模型的联系和思考

Transformers 是一种基于注意力机制的深度学习模型架构,它引入了自注意力机制(self-attention mechanism),通过直接学习输入序列中各个位置之间的依赖关系,实现了在序列数据上的并行化处理,从而在很大程度上提高了模型的效率和性能。

Transformers 模型由多层编码器和解码器组成,其中编码器用于将输入序列映射到隐藏表示,解码器用于根据编码器的输出生成目标序列。每个编码器和解码器层都由多个子层组成,包括自注意力层、前馈神经网络层和残差连接层,其中自注意力层是 Transformers 模型的核心组件之一。

学习到现在为止的很多构架都是编码器解码器的组合,比如自编码器,比如生成对抗模型中的潜在空间变量生成图片的过程可以被看作是一种解码器。再进一步思考,其实机器学习中的几乎所有模型,都是一种先编码(模拟出某个内在的模式),然后再解码预测的过程。这个概念很重要。甚至在人的认知方面,我们的学习对知识的吸收也必须是一种编码过程。编码就是一种对具体数据的抽象的过程。这种抽象过程包括了内容,性质,以及他们之间的联系。对我来说,可以总结为一种高维度的认识。

昨天我刚刚读了兰陵王的新文章,关于记忆的编码问题,短时记忆主要由听觉编码和视觉编码形成,长时记忆主要由语义编码所形成。在处理时间序列的模型中,虽然循环神经网络,LSTM等模型表现优秀,但是他们的限制就是这种短时记忆的原理,随着时间的进行,之前的记忆会被遗忘。最终起作用的信息,就只有最新的部分信息,一般来说LSTM对于20个步长以内的数据非常优秀,但是一旦拉长就不太行了,这也正是注意力机制和Transformer诞生的原因(虽然它的诞生其实是从图像处理来的,但是世界上很多东西都有很多共通的地方)。

注意力机制,Transformer是长时间记忆的先驱,虽然 Attention is all you need 这句话太酷了,但是它其实隐藏了真实的意图,通过注意力达到的效果,其实应该是知识关联。虽然似乎一开始,注意力机制的出现是从图像来的灵感,但是从人类智能角度来说,所有的都回到了第一性原理,都是如此的关联。

在自注意力层中,输入序列的每个位置都被视为一个查询(query)、一个键(key)和一个值(value),通过计算查询与键的相似度来获取注意力分数,然后将注意力分数与值相乘并加权求和,得到每个位置的加权表示。这种机制使得模型能够在不同位置之间进行有效的信息交互,并且能够灵活地捕捉输入序列中的长距离依赖关系。Transformers 更是加入了位置编码,将循环神经网络的特别之处通过不同的方式引入进来,这宛如一个知识宫殿的建设,让知识的提取更加准确高效。

再说的远一点,这让我非常看好强化学习和遗传算法,因为从认知的角度,要想达到记忆,学习,理解的目的,最好的方法永远是主动学习,兴趣驱动,功利驱动,而强化学习正是这方面的体现。AGI的未来如何我不知道能否看到,但是一定离不开人类对自身的探索和认知的深入。

还是非常感谢兰陵王的启迪。

回归正题,拆解一下 Transformers 的关键组件。

Tokenization

针对RNN 和 LSTM,Transformers 的进化来自于一个问题,为什么我们不把整个序列作为输入,从而摆脱隐藏层之间的依赖关系呢。这个想法很酷。因为这并不是一个现学现卖的世界,而是一个知识联系的世界。

如何存储知识?Tokenization。

这是自然语言处理中重要的部分,非常重要,token 是文本的最小单位。它们可以是单词,词组,标点符号,一个单词并不一定对应一个token,有时候是好几个,取决于处理规则和文本处理需要。我对它的处理方式很感兴趣,所以查找了一下tokenization的方法:

  1. 分词: 在某些语言中,单词可能由多个连续字符组成,如英语中的 “New York”、中文中的 “人工智能”。在分词的过程中,这些连续的字符可能会被视为一个 token,而不是分成单独的字符。

  2. 标点符号处理: 标点符号通常与单词一起出现,但在某些情况下,它们可能被视为独立的 token。例如,在一些 NLP 任务中,可以选择保留标点符号作为单独的 token,也可以选择将它们与相邻的单词合并成一个 token。

  3. 词形变化和词根: 单词的不同形式(如时态、语态、复数等)可能会被视为相同的 token,这样可以减少词汇表的大小并简化模型的训练。例如,”running”、”runs”、”ran” 等形式可能被归为同一个 token “run”。

  4. 停用词和特殊符号: 停用词(如 “the”、”and”)和一些特殊符号(如空格、制表符、换行符)可能会被忽略或视为特殊的 token,并在 tokenization 过程中进行过滤。

  5. 领域特定处理: 在特定的 NLP 任务中,可能会根据领域的特点和需求进行定制化的 tokenization 规则。例如,在医学领域的文本处理中,可能需要对医学术语进行特殊处理,将其视为单独的 token。

因此,一个单词不一定对应一个 token 是因为在实际的文本处理中,需要根据不同的情况和任务来设计合适的 tokenization 规则,以满足特定的需求和处理要求。

Word Embeddings

一般来说,嵌入(embedding)是指将一个符号(单词、字符、句子)表示为连续值向量的分布式低维空间中的一种方法。

这是一个单词“king”的词嵌入(在维基百科上训练的GloVe向量):

[ 0.50451 , 0.68607 , -0.59517 , -0.022801, 0.60046 , -0.13498 , -0.08813 , 0.47377 , -0.61798 , -0.31012 , -0.076666, 1.493 , -0.034189, -0.98173 , 0.68229 , 0.81722 , -0.51874 , -0.31503 , -0.55809 , 0.66421 , 0.1961 , -0.13495 , -0.11476 , -0.30344 , 0.41177 , -2.223 , -1.0756 , -1.0783 , -0.34354 , 0.33505 , 1.9927 , -0.04234 , -0.64319 , 0.71125 , 0.49159 , 0.16754 , 0.34344 , -0.25663 , -0.8523 , 0.1661 , 0.40102 , 1.1685 , -1.0137 , -0.21585 , -0.15155 , 0.78321 , -0.91241 , -1.6106 , -0.64426 , -0.51042 ]

这可能是这个主题最好的参考网页:

https://jalammar.github.io/illustrated-word2vec/

第三次读这篇神说明文,第三次总结其中的经典思想。

读完了,还是觉得好棒。

另外,我自己之前的一些关于嵌入空间的误解:我看到那个向量很长误解为是一个高维度的向量,其实当我们谈论嵌入空间时,我们指的是将高维的离散型数据(如单词、字符、句子等)映射到一个连续值的低维空间中的过程。这个连续值的低维空间就是嵌入空间。嵌入空间中的每个维度代表了某种特定的语义信息,这样一来,通过计算嵌入空间中的向量之间的距离或相似度,我们就可以衡量它们在语义上的关联性。它只是一个很长的很多数字的向量。

举个例子,假设我们有一个词汇表,包含了所有可能的单词。在传统的表示方法中,我们可能会用一个非常稀疏的独热编码(One-Hot Encoding)来表示每个单词,其中每个单词被表示为一个非常长的向量,其中只有一个元素是1,其他元素都是0。

但是,这样的表示方法存在一些问题,比如维度过高、稀疏性等。嵌入空间的概念就是为了解决这些问题。在嵌入空间中,每个单词被表示为一个密集的低维向量,其中每个维度都代表了一些语义信息。这样一来,相似的单词在嵌入空间中会被映射到彼此附近的位置,而不相似的单词则会被映射到远离的位置。

嵌入空间通常是通过机器学习模型在大量数据上学习得到的,比如词向量模型(Word2Vec)、词嵌入模型(Word Embedding)、BERT(Bidirectional Encoder Representations from Transformers)等。这些模型会根据上下文信息来学习单词之间的关系,并将它们映射到一个低维的连续值空间中,从而得到单词的嵌入向量。

Positional encodings

位置编码是为了处理序列数据(如自然语言文本)中单词或标记的位置信息而引入的一种技术。在循环神经网络(RNN)、Transformer等模型中,位置编码用于将单词或标记的位置信息嵌入到模型中,以便模型能够理解和利用单词的顺序信息。

常用的位置编码方法包括绝对位置编码和相对位置编码。绝对位置编码直接将位置信息编码成一个固定的向量,而相对位置编码则根据单词之间的相对位置关系来编码位置信息。

在Transformer模型中,常用的位置编码方法是通过加入正弦曲线和余弦曲线的组合来实现。这是由于正弦曲线和余弦曲线具有周期性,能够很好地表示序列的位置信息。

为什么使用正弦曲线呢?这是因为正弦曲线在周期性方面具有较好的性质,它的周期性可以帮助模型理解序列中单词之间的位置关系。正弦曲线的周期性特征使得在不同位置上的位置编码能够被有效地区分开来,从而帮助模型更好地理解序列的顺序。

但是一开始我也有自己的疑问,因为正弦曲线是周期性的,这意味着位置会重复,意味着在序列中的某个位置出现的编码会在另一个位置上再次出现,尤其是对于较长的序列而言。

尽管如此,这并不一定会导致问题,因为在Transformer模型中,位置编码主要用于为模型提供关于词序的信息,而不是唯一确定词的位置。Transformer模型通过结合自注意力机制来处理序列中的单词,因此,即使位置编码具有周期性,模型仍然能够根据上下文动态地学习每个单词的语义和位置。

此外,Transformer模型通常会在输入序列中添加一个特殊的开始标记和结束标记,这样可以帮助模型更好地理解序列的边界,并进一步减轻周期性重复位置编码的影响。

虽然正弦曲线作为位置编码可能会导致周期性重复的问题,但在实践中,Transformer模型已经证明了其在自然语言处理等序列任务中的有效性。通过合理的模型设计和训练,Transformer模型能够有效地利用位置编码,并取得良好的性能。

Feature-based attention: key, value, query

起源:

一开始我一直不懂这个概念,经过调查,了解到QKV的起源,是信息检索系统。

在信息检索系统中,键-值-查询概念被广泛应用。在这种上下文中,键(Key)代表了用于索引和标识文档或数据的关键信息。这些关键信息通常是文档的关键词、标签、或者其他能够唯一标识文档的特征。值(Value)则代表了与键相关联的实际文档内容或数据。查询(Query)则是用户提出的信息需求,通常是一个或多个关键词或短语。

信息检索系统的目标是根据用户的查询来检索出与之相关的文档或数据。这个过程通常涉及到在文档集合中使用关键信息(键)来匹配用户查询,并返回与查询相关的文档内容(值)。查询与关键信息的匹配程度通常通过计算它们之间的相似度来衡量。

基于信息检索系统的这种概念,键-值-查询概念被引入到了神经网络中的注意力机制中。在神经网络中,键、值、和查询分别用来衡量、存储和查询信息。通过计算查询与键的相似度,可以根据键的重要性来对值进行加权,从而实现对输入数据的关注和选择。这种基于信息检索系统概念的键-值-查询机制在神经网络中的应用,能够帮助模型更好地处理输入数据的复杂关系,并实现更有效的信息检索和处理。

内涵:

Feature-based attention 是注意力机制的一种形式,它用于在神经网络模型中实现对输入的加权关注,以便更好地捕捉输入之间的关系和重要性。在这中间,通常会如此定义键(key)、值(value)、和查询(query)。他们的计算都是线性变换,内部都是权重矩阵。

  1. Key(键): 键是用来衡量输入的重要性的向量。通常来说,键是通过将输入向量乘以一个权重矩阵(通常称为键权重矩阵)得到的,从而得到与输入向量等长的键向量。键向量的每个元素表示了对应输入向量在不同方面的重要性。在注意力机制中,键向量用于衡量输入中的不同部分对于查询的相关性

  2. Value(值): 值是与每个键对应的向量,用于存储输入的信息。值是通过将输入向量乘以另一个权重矩阵(通常称为值权重矩阵)得到的,从而得到与输入向量等长的值向量。在注意力机制中,值向量表示了与对应键向量相关的信息

  3. Query(查询): 查询是一个向量,用于指定模型要关注的特定方面或信息。查询通常是通过与输入进行点乘操作得到的,以此来计算查询与输入的相似度。查询向量通常是模型的一部分,可以随着模型的训练过程学习而得到。

在注意力机制中,通过计算查询与键的相似度,可以得到一个权重向量,用于指示模型对输入的不同部分的关注程度。然后,这个权重向量会与值向量相乘,以此来计算输出。这种基于特征的注意力机制允许模型根据输入的不同部分动态地调整其关注的重点,从而更好地处理输入数据的复杂关系。

输入:

在这个模型中的输入,我一开始以为是要预测的对象,其实,输入通常指的是待处理的数据,这些数据可以是任何形式的,如自然语言文本、图像、音频等。

输入可能是一个句子或一段文本。在图像处理领域中,输入可能是一张图像。在语音处理领域中,输入可能是一段音频。总的来说,输入可以是任何形式的数据,取决于具体的应用场景。

在注意力机制中,输入数据通常会被表示为一组向量或张量。这些向量或张量可以包含各种信息,如词向量、图像特征向量、音频特征向量等,用来表示输入数据的不同方面和特征。

在键-值-查询模型中,每个输入数据都会被分别映射为键、值和查询。键表示输入数据的重要性或关键信息,值表示输入数据的实际内容,而查询则表示模型关注的特定方面或信息。通过计算查询与键的相似度,模型可以根据输入数据的不同部分动态地调整其关注的重点,并输出相应的结果。因此,输入在键-值-查询模型中扮演着重要的角色,它是模型进行信息处理和决策的基础。

similarity(Q x K) --> attention weighted mean -->map weight to info(values) --> relevant info

Short residual skip connections

短残差跳跃连接,是残差网络的一部分,用于解决梯度消失和梯度爆炸问题。快捷连接,允许输入直接传递给输出。也就是:Final(x) = Hidden(x) + x,这样一种关系。

在 Transformers 的机制中,它又有另一种深意。我总结出来就是:跳跃思维,联系思维,直接用第一性原理指导实践的思维。

人类有一种对世界的广泛理解以及我们可以将不同观念结合起来的能力。人类的语言包括了我们对世界中的各种概念自身,以及对他们的关系的理解,甚至结合和创新创造。同时还体现在我们能够将不同观念和想法结合起来,以创造新的表达方式和意义。在残差网络中,信息可以直接从输入连接到输出层,作为一种想法结合的形式。

我们的语言理解和使用受到自上而下的影响,即我们的期望和先前的知识会影响我们对语言的理解和表达。这种影响使我们能够根据上下文和语境来理解和解释语言。在残差网络中,快捷连接可以允许信息的自上而下的传递,从而帮助网络在训练过程中更好地调整权重,提高网络的性能。

和认知科学相似,在自然语言处理中,自上而下的期望通常指的是根据上下文、语境和先验知识对语言进行理解和解释的能力。通过利用语言的语境和文化背景等因素,我们可以根据自身的期望来理解语言中的含义,从而更准确地进行语言交流和理解。

Layer normalization

Layer normalization 是一种用于深度神经网络中的归一化技术,旨在解决训练过程中梯度消失和梯度爆炸的问题,同时有助于提高网络的泛化能力。

Layer normalization 的核心思想是对每个特征维度进行归一化,而不是对整个样本进行归一化。具体来说,首先对每个样本计算其每个特征维度上的均值和标准差,使用均值和标准差对每个特征维度进行归一化,最后对每个特征维度进行缩放和平移,以便学习适当的特征表达。缩放和平移中的参数是可以学习的。

Layer normalization 的优点包括:

因此,Layer normalization 是一种用于深度神经网络的归一化技术,通过对每个特征维度进行归一化,有助于提高网络的稳定性和泛化能力。

Add linear layers

关键组件的学习中,大部分都是一些 Transformers 构架特有的组件,这里开始进行一点编码,要加入一些深度学习中的一般来说常有的组件。第一个这里就是线形层,其中包括 dropout 等技术和激活函数。

在多头自注意力(multi-head self-attention)之后添加线性层的想法是将表示投影到一个更高的空间,然后再投影回原始空间。这有助于解决一些稳定性问题并抵消糟糕的初始化

具体来说,在多头自注意力机制之后,得到的表示通常是低维的,可能包含了来自不同头的信息。通过添加线性层,可以将这些表示投影到一个更高维度的空间中,从而增加了表示的丰富度和表达能力。这可以帮助网络更好地捕获输入序列的复杂特征和语义。然后线性层通常会再通过另一个线性变换将表示投影回原始的低维空间。这个步骤的目的是为了将表示重新调整到原始空间的范围内,使得网络能够更好地处理这些表示,并将它们用于后续的任务。

清点一下整个构架: 在位置编码后面的层有很多层。

inputs --> tokenization --> Embeddings 
--> Positional Encoding 
N x (
--> Multi-head Attention --> Add x & layer Norm 
--> Linear --> Add x & layer Norm >>
)

线性层构架如下:可以看到对维度进行了一次放大(upprojection),然后又缩回(downprojection)到了原来的维度。最后经过一个残差跳跃,加上了输入的 x。

import torch
import torch.nn as nn

dim = 512
dim_linear_block = 1024 ## usually a multiple of dim
dropout = 0.1

norm = nn.LayerNorm(dim)
linear = nn.Sequential(
            nn.Linear(dim, dim_linear_block),
            nn.ReLU(),
            nn.Dropout(dropout),
            nn.Linear(dim_linear_block, dim),
            nn.Dropout(dropout)
        )

out = norm(linear(x) + x)

Masked multi-head self-attention

Masked multi-head self-attention 在自注意力机制的基础上增加了掩码(masking)操作。它通常用于序列到序列(sequence-to-sequence)模型中,特别是在处理自然语言处理任务时,如机器翻译、语言建模等。比如 GPT 的文本生成任务。

在传统的自注意力机制中,每个输入序列中的每个位置都可以与其他位置进行关联,从而获得全局的上下文信息。但在某些任务中,例如语言建模或者文本生成任务,我们不希望模型在生成当前位置的输出时使用未来位置的信息,因为这样会导致信息泄漏和模型性能下降。

因此,在 Masked multi-head self-attention 中,会引入一个掩码(masking)机制,用于在计算注意力权重时限制模型只能关注当前位置之前的信息,而屏蔽当前位置之后的信息。

具体的计算,就是在多头注意力机制的上面,加上 masking 操作。在计算注意力分数也就是QK点积运算时,需要应用掩码来限制模型只能关注当前位置之前的信息。具体来说,可以使用前瞻掩码(Look-ahead Mask)或填充掩码(Padding Mask),将不允许关注的位置对应的注意力分数设为一个较大的负数,以便在计算 softmax 时将其约束为 0。

在 Masked multi-head self-attention 中,不允许关注的位置通常是当前位置之后的位置。这是因为在许多序列到序列的任务中,特别是在语言建模和文本生成任务中,模型通常需要按顺序逐步生成输出序列。因此,在生成当前位置的输出时,不应该使用当前位置之后的信息,以避免信息泄漏和模型性能下降。

在其他任务中,例如处理变长序列的情况下,可能会使用填充掩码(Padding Mask)来屏蔽填充位置的信息。填充掩码可以确保模型在计算注意力权重时不会将注意力集中在填充位置上,而是关注实际的输入序列部分。

recap

到这里就是Transformers的所有的重要部分了。 一个篇章进行编码的解构和练习。image