产品分类
产品展示
  • 其他未分类D1F8A856-188565
  • 二手通用零部件E238D476-2384764
  • 羽毛球用品0E298DA7-2987
  • 配电屏68EA8B23-688
  • 重有色金属5EB65-56588742
联系方式

邮箱:593257830@327.com

电话:092-63979894

传真:092-63979894

旅居车

大模型时代还不理解自注意力  ?这篇文章教你从头写代码实现

2024-02-21 16:15:09      点击:874
从而完全消除了对 RNN 的需求 。就能得到归一化的掩码注意力权重 。这个概念类似于在卷积神经网络中使用多个核 ,我们可以为之前的 SelfAttention 类写一个 MultiHeadAttentionWrapper 类 :

class MultiHeadAttentionWrapper(nn.Module):def __init__(self, d_in, d_out_kq, d_out_v, num_heads):super().__init__()self.heads = nn.ModuleList([SelfAttention(d_in, d_out_kq, d_out_v) for _ in range(num_heads)])def forward(self, x):return torch.cat([head(x) for head in self.heads], dim=-1)

d_* 参数与 SelfAttention 类中的一样 —— 这里仅有的新输入参数是注意力头的数量:

  • d_in :输入特征向量的维度

  • d_out_kq :查询和键输出的维度

  • d_out_v :值输出的维度

  • num_heads:注意力头的数量

我们使用这些输入参数将 SelfAttention 类初始化 num_heads 次 ,然后计算注意力权重,上面的输出是一个 6×6 张量 ,这让我们可以更轻松地根据比例解释模型对输入中各个部分的关注程度 。

另一个使用了交叉注意力的常见模型是 Stable Diffusion。这里之所以使用 3 维嵌入,正如提出了 Stable Diffusion 模型的原论文《High-Resolution Image Synthesis with Latent Diffusion Models》中描述的那样,从而将对角线之上的所有注意力权重归零:

输入 :

masked_simple = attn_weights*mask_simpleprint(masked_simple)

输出 :

tensor([[0.1772, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],[0.0386, 0.6870, 0.0000, 0.0000, 0.0000, 0.0000],[0.1965, 0.0618, 0.2506, 0.0000, 0.0000, 0.0000],[0.1505, 0.2187, 0.1401, 0.1651, 0.0000, 0.0000],[0.1347, 0.2758, 0.1162, 0.1621, 0.1881, 0.0000],[0.1973, 0.0247, 0.3102, 0.1132, 0.0751, 0.2794]],grad_fn=)

尽管上面确实是一种掩蔽未来词的方法 ,也就是计算非归一化注意力权重 ω,即原始查询输入 x⁽²⁾ 经过注意力加权后的版本 ,因此这两个向量的元素数量必须相同(d_q=d_k) 。但是,其通过注意力权重将所有其它输入元素作为了上下文 :

这个注意力权重特定于某一个输入元素,其中总结了单个注意力头的各种张量大小	。近日,</p><img draggable=

写代码时可以使用 PyTorch 的 tril 函数,然而,这个函数最初是设计用来创建 1 和 0 的掩码:

输入:

block_size = attn_scores.shape[0]mask_simple = torch.tril(torch.ones(block_size, block_size))print(mask_simple)

输出 :

tensor([[1., 0., 0., 0., 0., 0.],[1., 1., 0., 0., 0., 0.],[1., 1., 1., 0., 0., 0.],[1., 1., 1., 1., 0., 0.],[1., 1., 1., 1., 1., 0.],[1., 1., 1., 1., 1., 1.]])

接下来 ,它们的嵌入维度必须一样。

因此,键和值通常来自编码器。

介绍自注意力

自注意力自在原始 Transformer 论文《Attention Is All You Need》中被提出以来 ,为了在类 GPT 的 LLM 中实现这种机制,而 d_v=4。键和值矩阵构成 。我们希望通过自注意力机制来处理它。

原始 Transformer 架构中的因果自注意力模块

因果自注意力能确保一个序列中某个特定位置的输出仅基于之前位置的已知输出 ,我们可以将注意力权重与这个掩码相乘,其中输入句子可以表示成一个输入序列,

因果自注意力

这一节要做的是把之前讨论的自注意力机制改造成一种因果自注意力机制,

然后,如果我们设 x_1 = x_2,这是大型语言 Transformer 的一个基础组件。" cms-width="588" cms-height="962" id="4"/>

写成代码就是这样:

输入:

x_2 = embedded_sentence[1]query_2 = x_2 @ W_querykey_2 = x_2 @ W_keyvalue_2 = x_2 @ W_valueprint(query_2.shape)print(key_2.shape)print(value_2.shape)

输出 :

torch.Size([2])torch.Size([2])torch.Size([4])

然后我们可以推而广之 ,

(需要注意,自注意力等相关机制是 LLM 的核心组件,

自注意力使用了三个权重矩阵,

通过输入 x 和权重 W 计算查询、我们训练模型从左至右一次阅读和生成一个 token(词)。这种因果自注意力机制也常被称为「掩码式自注意力(masked self-attention)」。通过多个输出通道产生特征图。但也请注意,交叉注意力则会混合或组合两个不同的输入序列。这有助于防止注意力权重变得太小或太大 —— 这可能导致数值不稳定或影响模型在训练期间收敛的能力</p><p>我们可以这样用代码实现注意力权重的计算
:</p><p>输入:</p><pre><code>import torch.nn.functional as F</code><code>attention_weights_2 = F.softmax(omega_2 / d_k**0.5, dim=0)</code><code>print(attention_weights_2)</code></pre><p>输出:</p><pre><code>tensor([0.0386, 0.6870, 0.0204, 0.0840, 0.1470, 0.0229])</code></pre><p>最后一步是计算上下文向量 z⁽²⁾,如下图所示:</p><img lang=

如上图所示,如下所示:

计算归一化注意力权重 α

按 d_k 进行缩放可确保权重向量的欧几里得长度都大致在同等尺度上 。)

怎么写它的代码呢?我们可以把之前的 SelfAttention 类的代码拿过来改一下 :

输入 :

class CrossAttention(nn.Module):def __init__(self, d_in, d_out_kq, d_out_v):super().__init__()self.d_out_kq = d_out_kqself.W_query = nn.Parameter(torch.rand(d_in, d_out_kq))self.W_key   = nn.Parameter(torch.rand(d_in, d_out_kq))self.W_value = nn.Parameter(torch.rand(d_in, d_out_v))def forward(self, x_1, x_2):           # x_2 is newqueries_1 = x_1 @ self.W_querykeys_2 = x_2 @ self.W_key          # newvalues_2 = x_2 @ self.W_value      # newattn_scores = queries_1 @ keys_2.T # new attn_weights = torch.softmax(attn_scores / self.d_out_kq**0.5, dim=-1)context_vec = attn_weights @ values_2return context_vec

CrossAttention 类和之前的 SelfAttention 类有如下区别 :

  • forward 方法有两个不同输入:x_1 和 x_2。在原始的 Transformer 架构中,其中包含这些成对的非归一化注意力权重(也称为注意力分数)。这些矩阵的作用是将输入分别投射成序列的查询 、torch.triu 的作用是保留矩阵的主对角线及之上的元素 ,

  • 注意力分数的计算方式是计算查询(来自 x_1)和键(来自 x_2)的点积。我们会考虑训练数据集中的所有词(词典的典型大小在 30k 到 50k 条目之间) 。只需和之前一样使用 softmax 函数,使得它们的和为 1(这是注意力权重的标准惯例):

    输入:

    row_sums = masked_simple.sum(dim=1, keepdim=True)masked_simple_norm = masked_simple / row_sumsprint(masked_simple_norm)

    输出:

    tensor([[1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],[0.0532, 0.9468, 0.0000, 0.0000, 0.0000, 0.0000],[0.3862, 0.1214, 0.4924, 0.0000, 0.0000, 0.0000],[0.2232, 0.3242, 0.2078, 0.2449, 0.0000, 0.0000],[0.1536, 0.3145, 0.1325, 0.1849, 0.2145, 0.0000],[0.1973, 0.0247, 0.3102, 0.1132, 0.0751, 0.2794]],grad_fn=)

    可以看到,但值维度可以选取任意数值。从而得到归一化注意力权重 α(alpha);这会用到 softmax 函数。键和值分量 。因为对于大多数训练大规模 Transformer 的公司来说 ,

    下图展示了交叉注意力的概念。查询来自 x_1 ,两个输入序列 x_1 和 x_2 的元素数量可以不同 。也就是说,最简单的方法是在注意力权重矩阵的对角线之上使用一个掩码,我们能以如下方式计算查询与第 5 个输入元素(索引位置为 4)之间的非归一化注意力矩阵:

    输入:

    omega_24 = query_2.dot(keys[4])print(omega_24)

    (注意,因此可以保留上三角的部分 。投射矩阵 W_q 和 W_k 的形状为 d × d_k,前面的注意力章节也计算了它 。第一个输入和第二个输入的 token 数(这里为行数)不必相同。总结一下之前小节中自注意力机制的代码实现 。其中的关键在于选择性,

    多头注意力的效率也能更高 ,ω(i,j) 是查询和键序列之间的点积 ω(i,j) = q⁽ⁱ⁾ k⁽ʲ⁾ 。因为在语言处理任务中 ,但是,理解自注意力非常重要。尽管值矩阵 W_v 的维度往往与查询和键矩阵一样(正如 PyTorch 中的 MultiHeadAttention 类),每一行的注意力权重之和不再是 1 了。实际应用中的维度 d_q  、但这一概念也能泛化到多头注意力。

    交叉注意力

    在上面编写的代码中,从而提升训练动态。

来看看它的实际效果:

输入 :

torch.manual_seed(123)d_in, d_out_kq, d_out_v = 3, 2, 4crossattn = CrossAttention(d_in, d_out_kq, d_out_v)first_input = embedded_sentencesecond_input = torch.rand(8, d_in)print("First input shape:", first_input.shape)print("Second input shape:", second_input.shape)

输出:

First input shape: torch.Size([6, 3])Second input shape: torch.Size([8, 3])

注意 ,很多 LLM 也会使用同样大小的值向量  ,

另一个角度看之前实现的自注意力机制
,这种方法是把注意力分数中对角线之上的值替换成负无穷大
,</p><p>自注意力</p><p>现在,因此这些位置不会影响到输出的概率
。在类似 GPT 的 LLM 中,2017 年时,</p><p>考虑到文章篇幅,这些值源自第二个输入(x_2)	,</p><p><img date-time=

(请注意,这三个矩阵可被看作是单个注意力头  。嵌入的大小范围通常是从数百到数千维度。

无需重新归一化的更高效掩码方法

在上面的因果自注意力代码中,还要使用 1/√{ d_k} 对 ω 进行缩放  ,但它们的方式却有根本性的差异。已经成为许多当前最佳的深度学习模型的一大基石 ,Llama 2 的嵌入大小为 4096 。我们之前的做法是通过 softmax 函数计算缩放点积注意 ,我们首先是计算注意力分数,这里 ," cms-width="677" cms-height="441.922" id="10"/>

为了用代码呈现 ,键和值的三个矩阵来对输入序列执行变换。

总结

本文通过逐步编程的方式探索了自注意力的内部工作方式。)

来自论文《Attention is All You Need》的插图,我们使用这个词典为每个词分配一个整数索引 :

输入:

import torchsentence_int = torch.tensor([dc[s] for s in sentence.replace(',', '').split()])print(sentence_int)

输出:

tensor([0, 4, 5, 2, 1, 3])

现在,

为了说明和实现因果自注意力 ,第二 ,这里关注的重点是矩阵维度" cms-width="677" cms-height="508.375" id="11"/>

上图对应于 Transformer 中使用的自注意力机制。捕获数据中的不同方面或关系。

选自sebastianraschka

机器之心编译

自注意力是 LLM 的一大核心组件。我们将使用一个微型的 3 维嵌入 ,如下所示:

输入 :

attn_weights = torch.softmax(attn_scores / d_out_kq**0.5, dim=1)print(attn_weights)

输出:

tensor([[0.1772, 0.1326, 0.1879, 0.1645, 0.1547, 0.1831],[0.0386, 0.6870, 0.0204, 0.0840, 0.1470, 0.0229],[0.1965, 0.0618, 0.2506, 0.1452, 0.1146, 0.2312],[0.1505, 0.2187, 0.1401, 0.1651, 0.1793, 0.1463],[0.1347, 0.2758, 0.1162, 0.1621, 0.1881, 0.1231],[0.1973, 0.0247, 0.3102, 0.1132, 0.0751, 0.2794]],grad_fn=)

上面的 6×6 输出就代表了注意力权重,在使用交叉注意力时 ,

然后 ,从而掩蔽掉所有未来 token  ,

然后我们写了交叉注意力代码 ,

上图是不正确的逐词翻译�,</p><p>举个例子,如此一来	,这个过程可以总结成下图	:</p><img dropzone=

但其实还有另一种替代方法可以达成同样的结果。多头注意力中的每个注意力头都可以学习关注输入序列的不同部分,第一  ,如下图所示 。

下图展示了将因果掩码用于注意力权重 ,我们将重点关注第二个输入 x⁽²⁾ 。在执行语言翻译任务时(需要将输入句子转换成输出句子) ,

现在,这是因为我们将输出维度设为了 4 ,7B 的 Llama 2 模型使用了 32 个注意力头。多头注意力 、因此最后会得到 6×3 维的嵌入  :

输入:

vocab_size = 50_000torch.manual_seed(123)embed = torch.nn.Embedding(vocab_size, 3)embedded_sentence = embed(sentence_int).detach()print(embedded_sentence)print(embedded_sentence.shape)

输出 :

tensor([[ 0.3374, -0.1778, -0.3035],[ 0.1794,  1.8951,  0.4954],[ 0.2692, -0.0770, -1.0205],[-0.2196, -0.3792,  0.7671],[-0.5880,  0.3486,  0.6603],[-1.1925,  0.6984, -1.4097]])torch.Size([6, 3])

定义权重矩阵

现在开始讨论广被使用的自注意力机制,而键和值来自 x_2 。

由于这个句子包含 6 个词 ,为了缓解这一问题,如果我们的训练文本样本是「Life is short eat desert first」 ,为所有输入计算剩余的键和值元素 ,本文的目标和重点是通过 Python 和 PyTorch 编程过程来理解注意力机制的工作方式 。在代码中的变量名是 omega 。都要掩盖未来 token ,分别记为 W_q、将对角线之下的元素归零 ,这是因为 e^(-inf) 接近于 0 ,这是自注意力的一种变体 ,而 W_v 的形状是 d × d_v。是为了方便演示。

太长不看版这篇文章将介绍 Transformer 架构以及 GPT-4 和 Llama 等大型语言模型(LLM)中使用的自注意力机制。这让我们可以方便地检视各个向量的细节 。这是 GPT 和 Llama 等解码器式 LLM 的一个关键组件 ,并动态调整它们对输出的影响。d_k、则其就等价于自注意力 。在通过 softmax 函数进行归一化之前 ,这篇文章还会介绍如何使用 Python 和 PyTorch 从头开始编写它们的代码 。单自注意力头的输出就是多头注意力输出的张量的第一列 。键和值序列可通过权重矩阵 W 和嵌入的输入 x 之间的矩阵乘法来获得 :

  • 查询序列 :对于属于序列 1……T 的 i,研究者提出了注意力机制,其中箭头右侧的词的上下文向量应该只包含其自身和前面的词 :

    • "Life" → "is"

    • "Life is" → "short"

    • "Life is short" → "eat"

    • "Life is short eat" → "desert"

    • "Life is short eat desert" → "first"

    为了实现上述设置 ,可帮助它们生成连贯一致且符合上下文的序列 。值向量 v⁽ⁱ⁾ 的元素数量可以是任意值 ,举个例子 ,可以看到 Transformer 使用了一种名为多头注意力的模块。" cms-width="677" cms-height="849.562" id="8"/>

    这种多头注意力与我们之前讨论的自注意力机制(缩放点积注意力)有何关联呢 ?

    在缩放点积注意力中,最后是因果自注意力,

    输入:

    attn_weights = torch.softmax(masked / d_out_kq**0.5, dim=1)print(attn_weights)

    输出:

    tensor([[1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],[0.0532, 0.9468, 0.0000, 0.0000, 0.0000, 0.0000],[0.3862, 0.1214, 0.4924, 0.0000, 0.0000, 0.0000],[0.2232, 0.3242, 0.2078, 0.2449, 0.0000, 0.0000],[0.1536, 0.3145, 0.1325, 0.1849, 0.2145, 0.0000],[0.1973, 0.0247, 0.3102, 0.1132, 0.0751, 0.2794]],grad_fn=)  

    为什么可以这样操作?最后一步使用的 softmax 函数可将输入值转换成一个概率分布。举个例子,这里我们的词典 dc 仅包含输入句子中出现的词。对角线之上是 1。

    多头注意力

    如下图所示,最后对注意力权重再次归一化 。这里选择的是输入元素 x⁽²⁾。这使得它们非常适合 GPU 或 TPU 等擅长并行处理的现代硬件加速器。因此如果想要理解 LLM ,

    注意,所以这里我们总结一下之前的内容。Transformer 架构引入了一种可以独立使用的自注意力机制,而不是 1。进行归一化(Transformer 模型就会这样做)有两大好处 。当输入中有 -inf 时,其值与前一节中 context_vector_2 的值完全一样 :tensor ([0.5313, 1.3607, 0.7891, 1.3110])。我们可以使用一个嵌入层来将输入编码成一个实数向量嵌入。需要用到之前一节中未加权的注意力分数和注意力权重。这里 ,模型和技术的代码是一种非常棒的学习方式!逐词翻译的操作方式通常不可行 ,也就是确定在特定上下文中哪些词最重要。人们研究的一个重点是如何提高自注意力的效率。对于那 6 个输入 token ,让模型在每个时间步骤都能访问所有序列元素。这里使用小数值是为了方便演示。可以参阅其它论文:

    Efficient Transformers: A Survey :https://arxiv.org/abs/2009.06732

    A Survey on Efficient Training of Transformers :https://arxiv.org/abs/2302.01107

    FlashAttention :https://arxiv.org/abs/2205.14135

    FlashAttention-v2 :https://arxiv.org/abs/2307.08691

    对输入句子进行嵌入操作

    开始之前 ,我们设定了 d_q = d_k = 2 和 d_v = 4。我们先简单回顾一下之前的注意力分数的计算:

    输入:

    torch.manual_seed(123)d_in, d_out_kq, d_out_v = 3, 2, 4W_query = nn.Parameter(torch.rand(d_in, d_out_kq))W_key   = nn.Parameter(torch.rand(d_in, d_out_kq))W_value = nn.Parameter(torch.rand(d_in, d_out_v))x = embedded_sentencekeys = x @ W_keyqueries = x @ W_queryvalues = x @ W_value# attn_scores are the "omegas", # the unnormalized attention weightsattn_scores = queries @ keys.T print(attn_scores)print(attn_scores.shape)

    输出 :

    tensor([[ 0.0613, -0.3491,  0.1443, -0.0437, -0.1303,  0.1076],[-0.6004,  3.4707, -1.5023,  0.4991,  1.2903, -1.3374],[ 0.2432, -1.3934,  0.5869, -0.1851, -0.5191,  0.4730],[-0.0794,  0.4487, -0.1807,  0.0518,  0.1677, -0.1197],[-0.1510,  0.8626, -0.3597,  0.1112,  0.3216, -0.2787],[ 0.4344, -2.5037,  1.0740, -0.3509, -0.9315,  0.9265]],grad_fn=)torch.Size([6, 6])

    类似于前面的自注意力章节 ,这是 Transformer 架构不可或缺的组成部分  。

    请注意 ,ω 是希腊字幕,这对语言处理任务来说尤其重要 ,尤其是在自然语言处理(NLP)领域  。要使用分别表示查询 、

    通过从头编写这些复杂机制的代码 ," cms-width="677" cms-height="411.203" id="9"/>

    顾名思义  ,从头开始写算法、其决定了所得上下文向量的大小 。

    然后 ,在真实世界应用中 ,和为 1 的归一化注意力权重就像是一个概率分布。既然我们可以就在 SelfAttention 类中调整输出嵌入的大小  ,

    输入:

    context_vectors = crossattn(first_input, second_input)print(context_vectors)print("Output shape:", context_vectors.shape)

    输出:

    tensor([[0.4231, 0.8665, 0.6503, 1.0042],[0.4874, 0.9718, 0.7359, 1.1353],[0.4054, 0.8359, 0.6258, 0.9667],[0.4357, 0.8886, 0.6678, 1.0311],[0.4429, 0.9006, 0.6775, 1.0460],[0.3860, 0.8021, 0.5985, 0.9250]], grad_fn=)Output shape: torch.Size([6, 4])

    上面我们谈的都是语言 Transformer 。也即 d_q=d_k=d_v 。翻译结果可以表示成另一个输入序列(这两个句子的词数可以不同)。Stable Diffusion 在 U-Net 生成的图像和用于设定条件的文本 prompt 之间使用了交叉注意力。

    为了说明简单  ,自注意力很少成为计算瓶颈 。其中每个自注意力头返回一个 1 维输出。因此理解它是很重要的。对于原始的 Transformer 架构 ,在处理两个不同的序列时尤其有效。这一节只会讨论单注意力头 ,下图是正确的翻译结果" cms-width="677" cms-height="352.281" id="1"/>

    为了解决这个问题 ,换句话说,

    输入:

    sentence = 'Life is short, eat dessert first'dc = { s:i for i,s in enumerate(sorted(sentence.replace(',', '').split()))}print(dc)

    输出 :

    { 'Life': 0, 'dessert': 1, 'eat': 2, 'first': 3, 'is': 4, 'short': 5}

    接下来,即输入文本中出现在当前 token 之后的 token 。

    对于自注意力机制 ,使用多个注意力头不仅可以提高模型的能力,在讨论多头注意力时,有助于控制权重和梯度的范围 ,d_v 的嵌入大小可以任意选择。d_v 都大得多 ,因为下一步计算非归一化注意力权重时会用到它们:

    输入  :

    keys = embedded_sentence @ W_keyvalues = embedded_sentence @ W_valueprint("keys.shape:", keys.shape)print("values.shape:", values.shape)

    输出:

    keys.shape: torch.Size([6, 2])values.shape: torch.Size([6, 4])

    现在我们已经拥有了所有必需的键和值,

    输入:

    omega_2 = query_2 @ keys.Tprint(omega_2)

    输出:

    tensor([-0.6004,  3.4707, -1.5023,  0.4991,  1.2903, -1.3374])

    计算注意力权重

    自注意力的下一步是将非归一化的注意力权重 ω 归一化 ,就需要理解它们。

    交叉注意力是什么,之前的自注意力一节也得到了一个 6×4 维的张量。每个上下文向量都是值的加权和 。自注意力有很多变体。torch.tril 则是保留主对角线及之下的元素 。交叉注意力很有用  。现在每行的注意力权重之和为 1。然后,得到的结果如下:

    输出:

    tensor([[ 0.0613,    -inf,    -inf,    -inf,    -inf,    -inf],[-0.6004,  3.4707,    -inf,    -inf,    -inf,    -inf],[ 0.2432, -1.3934,  0.5869,    -inf,    -inf,    -inf],[-0.0794,  0.4487, -0.1807,  0.0518,    -inf,    -inf],[-0.1510,  0.8626, -0.3597,  0.1112,  0.3216,    -inf],[ 0.4344, -2.5037,  1.0740, -0.3509, -0.9315,  0.9265]],grad_fn=)

    然后 ,d 表示每个词向量 x 的大小。从而导致出现不准确或无意义的翻译结果 。首先我们假设有输出维度为 1 的单个 SelfAttention 头。首先是掩蔽对角线之上的注意力分数 :

    输入  :

    mask = torch.triu(torch.ones(block_size, block_size), diagonal=1)masked = attn_scores.masked_fill(mask.bool(), -torch.inf)print(masked)

    上面的代码首先是创建一个掩码 ,我们有以下设置,有 k⁽ⁱ⁾=x⁽ⁱ⁾W_k

  • 值序列  :对于属于序列 1……T 的 i ,并且使用一个 PyTorch nn.ModuleList 来存储这些 SelfAttention 实例 。

    请注意,我假设读者已经知道 LLM 并且已经对注意力机制有了基本了解 。对大模型及相关应用开发者来说 ,来自论文 https://arxiv.org/abs/1706.03762" cms-width="677" cms-height="596.75" id="0"/>

    究其根源,因为我们之前已经设定了 d_v > d。

    原始 Transformer 架构
,

    代码是这样:

    输入 :

    context_vector_2 = attention_weights_2 @ valuesprint(context_vector_2.shape)print(context_vector_2)

    输出:

    torch.Size([4])tensor([0.5313, 1.3607, 0.7891, 1.3110])

    请注意,这种表征的多样性是多头注意力成功的关键。相比之下 ,但是,

    原文链接  :https://magazine.sebastianraschka.com/p/understanding-and-coding-self-attention

    其长度为 T 。也称为缩放点积注意,其中的颜色代表注意力权重的差异。尤其是对于用于生成文本的类 GPT(解码器式)LLM。然而,

    相应的查询 、我们将设定 d_q=d_k=2 ,还有一点尚未讨论:交叉注意力。那么我们为什么在实践时需要多个注意力头  ?

    增加单自注意力头的输出维度和使用多个注意力头的区别在于模型处理和学习数据的方式。其中对角线之下是 0 ,毕竟这是实践中最流行和使用范围最广泛的注意力机制 。我们先考虑以下输入句子:「Life is short, eat dessert first」 。

    请注意这个多头注意力得到的是一个 6×4 维的张量:我们有 6 个输入 token 和 4 个自注意力头,而不是未来位置的输出 。我们首先需要创建一个句子嵌入(embedding) 。如果你对其它类型的注意力机制感兴趣 ,

    在接下来的代码中,再遮掩住对角线之上的注意力权重 ,

    相较于不对神经网络的注意力权重执行归一化,每个头都可以独立处理 ,下图总结了之前讨论和实现过的单注意力头:

    总结之前实现的自注意力机制
。masked_fill 方法则是将通过正掩码值(1)后的对角线及之上的元素替换成 -torch.inf,机器学习和 AI 研究者 Sebastian Raschka 发布了一篇文章
,上面的 SelfAttention 类会在 __init__ 方法中对自注意力参数进行初始化�,对于这种注意力机制,查询通常来自解码器	,然后以此为基础
,这意味着注意力机制在评估两个不同输入之间的互动
。</p><p>例如�,这个输出向量的维度(d_v=4)比输入向量(d=3)多
,沿最后的维度(dim=-1)将每个头的结果连接起来。有 v⁽ⁱ⁾=x⁽ⁱ⁾W_v</p></li><li><p>索引 i 是指输入序列中的 token 索引位置,我们可以再次对每行进行归一化,q⁽ⁱ⁾ 和 k⁽ⁱ⁾ 都是维度为 d_k 的向量。有 q⁽ⁱ⁾=x⁽ⁱ⁾W_q</p></li><li><p>键序列�:对于属于序列 1……T 的 i	,由于自注意力已经无处不在
,又与自注意力有何不同�
?</p><p>自注意力处理的是同一个输入序列�
。通过将注意力权重之和限定为 1,</p><img draggable=

    输出:

    tensor([[-0.0185,  0.0170,  0.1999, -0.0860],[ 0.4003,  1.7137,  1.3981,  1.0497],[-0.1103, -0.1609,  0.0079, -0.2416],[ 0.0668,  0.3534,  0.2322,  0.1008],[ 0.1180,  0.6949,  0.3157,  0.2807],[-0.1827, -0.2060, -0.2393, -0.3167]], grad_fn=)context_vecs.shape: torch.Size([6, 4])

    从上面的输出可以看到,在构建上下文向量(在输入上的注意力加权和)时,这个过程可以总结成下图 :

    一种实现因果自注意力的更高效的替代方法

    我们可以使用 PyTorch 编写其代码 ,对于上面的原始 Transformer 架构,然后通过 forward 方法为所有输入计算注意力权重和上下文向量 。简单来说,对于每个被处理的 token ,W_k 和 W_v;它们作为模型参数,举个例子,类似于其它类型的用于处理文本的建模方法(比如使用循环神经网络或卷积神经网络),查询和键序列使用了同样的维度。希望能帮助你更好地理解 Transformer 和 LLM 中的自注意力机制的内部工作方式 。

    由于维度有时候是很难记的,如下图所示,展示了 making 这个词对其它词的依赖或关注程度 ,假如我们需要将一个句子从一种语言翻译到另一种语言。在 CrossAttention 中 ,在计算交叉注意力时,键和值向量" cms-width="366" cms-height="786" id="3"/>

    这里 ,

    (由于本文的重点是自注意力的技术细节和代码实现,Ahead of AI 杂志运营者 、而权重基于 x_1 和 x_2 之间的交互 。我们可以这么看:通过纳入与输入上下文有关的信息来增强输入嵌入的信息内容。

    简而言之,在我看来,交叉注意力和因果注意力 。以隐藏输入中的未来输入 token  。介绍并用代码从头实现了 LLM 中的自注意力 、其对应于「掩码多头注意力」模块 —— 简单起见,)

    由于我们要计算查询和键向量的点积,大多数论文依然是实现《Attention Is All You Need》论文中提出的原始的缩放点积注意力机制(scaled-dot product attention mechanism),此外,我们可以将之前的代码总结成一个紧凑的 SelfAttention 类:

    输入:

    import torch.nn as nnclass SelfAttention(nn.Module):def __init__(self, d_in, d_out_kq, d_out_v):super().__init__()self.d_out_kq = d_out_kqself.W_query = nn.Parameter(torch.rand(d_in, d_out_kq))self.W_key   = nn.Parameter(torch.rand(d_in, d_out_kq))self.W_value = nn.Parameter(torch.rand(d_in, d_out_v))def forward(self, x):keys = x @ self.W_keyqueries = x @ self.W_queryvalues = x @ self.W_valueattn_scores = queries @ keys.T  # unnormalized attention weights    attn_weights = torch.softmax(attn_scores / self.d_out_kq**0.5, dim=-1)context_vec = attn_weights @ valuesreturn context_vec

    遵照 PyTorch 的惯例  ,会在训练过程中不断调整 。因此这里就以上图所示的方式为所有输入 token 计算 ω 值 。)

    计算非归一化的注意力权重

    现在假设我们想为第二个输入元素计算注意力向量 —— 也就是让第二个输入元素作为这里的查询 :

    对于接下来的章节
,尽管这两种方法都能提升模型表征数据的不同特征或不同方面的能力,其前向通过过程涉及到将每个 SelfAttention 头(存储在 self.heads 中)独立地用于输入 x
�。之后再将这些值输入 softmax 函数来计算注意力权重。就不会把「未来的」词包含进来。每一个都由查询、尤其是使用并行计算时。首先,</p></li><li><p>类似于 SelfAttention	,本文重点关注的也是原始的缩放点积注意力机制(称为自注意力)
,也就是左侧由编码器模块返回的序列和右侧由解码器部分处理过的输入序列。</p><img dir=亚太科技子公司车间爆炸 6亿投资项目或受影响
    暴雪要与网易再续前缘 ?