MENU

Transformer 详解

July 12, 2020 • Read: 155931 • Deep Learning阅读设置

B 站视频讲解

Transformer 是谷歌大脑在 2017 年底发表的论文 attention is all you need 中所提出的 seq2seq 模型。现在已经取得了大范围的应用和扩展,而 BERT 就是从 Transformer 中衍生出来的预训练语言模型

这篇文章分为以下几个部分

  1. Transformer 直观认识
  2. Positional Encoding
  3. Self Attention Mechanism
  4. 残差连接和 Layer Normalization
  5. Transformer Encoder 整体结构
  6. Transformer Decoder 整体结构
  7. 总结
  8. 参考文章

0. Transformer 直观认识

Transformer 和 LSTM 的最大区别,就是 LSTM 的训练是迭代的、串行的,必须要等当前字处理完,才可以处理下一个字。而 Transformer 的训练时并行的,即所有是同时训练的,这样就大大增加了计算效率。Transformer 使用了位置嵌入 (Positional Encoding) 来理解语言的顺序,使用自注意力机制(Self Attention Mechanism)和全连接层进行计算,这些后面会讲到

Transformer 模型主要分为两大部分,分别是 EncoderDecoderEncoder 负责把输入(语言序列)隐射成隐藏层(下图中第 2 步用九宫格代表的部分),然后解码器再把隐藏层映射为自然语言序列。例如下图机器翻译的例子(Decoder 输出的时候,是通过 N 层 Decoder Layer 才输出一个 token,并不是通过一层 Decoder Layer 就输出一个 token)

本篇文章大部分内容在于解释 Encoder 部分,即把自然语言序列映射为隐藏层的数学表达的过程。理解了 Encoder 的结构,再理解 Decoder 就很简单了

上图为 Transformer Encoder Block 结构图,注意:下面的内容标题编号分别对应着图中 1,2,3,4 个方框的序号

1. Positional Encoding

由于 Transformer 模型没有循环神经网络的迭代操作,所以我们必须提供每个字的位置信息给 Transformer,这样它才能识别出语言中的顺序关系

现在定义一个位置嵌入的概念,也就是 Positional Encoding,位置嵌入的维度为 [max_sequence_length, embedding_dimension], 位置嵌入的维度与词向量的维度是相同的,都是 embedding_dimensionmax_sequence_length 属于超参数,指的是限定每个句子最长由多少个词构成

注意,我们一般以为单位训练 Transformer 模型。首先初始化字编码的大小为 [vocab_size, embedding_dimension]vocab_size 为字库中所有字的数量,embedding_dimension 为字向量的维度,对应到 PyTorch 中,其实就是 nn.Embedding(vocab_size, embedding_dimension)

论文中使用了 sin 和 cos 函数的线性变换来提供给模型位置信息:

$$ PE{(pos,2i)} = \sin(pos / 10000^{2i/d_{\text{model}}}) \\ PE{(pos,2i+1)} = \cos(pos / 10000^{2i/d_{\text{model}}}) $$

上式中 $pos$ 指的是一句话中某个字的位置,取值范围是 $[0, \text {max_sequence_length})$,$i$ 指的是字向量的维度序号,取值范围是 $[0, \text {embedding_dimension}/2)$,$d_{\text {model}}$ 指的是 embedding_dimension​的值

上面有 $\sin$ 和 $\cos$ 一组公式,也就是对应着 embedding_dimension 维度的一组奇数和偶数的序号的维度,例如 0,1 一组,2,3 一组,分别用上面的 $\sin$ 和 $\cos$ 函数做处理,从而产生不同的周期性变化,而位置嵌入在 embedding_dimension​维度上随着维度序号增大,周期变化会越来越慢,最终产生一种包含位置信息的纹理,就像论文原文中第六页讲的,位置嵌入函数的周期从 $2 \pi$ 到 $10000 * 2 \pi$ 变化,而每一个位置在 embedding_dimension​维度上都会得到不同周期的 $\sin$ 和 $\cos$ 函数的取值组合,从而产生独一的纹理位置信息,最终使得模型学到位置之间的依赖关系和自然语言的时序特性

如果不理解这里为何这么设计,可以看这篇文章 Transformer 中的 Positional Encoding

下面画一下位置嵌入,纵向观察,可见随着 embedding_dimension​序号增大,位置嵌入函数的周期变化越来越平缓

  • import numpy as np
  • import matplotlib.pyplot as plt
  • import seaborn as sns
  • import math
  • def get_positional_encoding(max_seq_len, embed_dim):
  • # 初始化一个positional encoding
  • # embed_dim: 字嵌入的维度
  • # max_seq_len: 最大的序列长度
  • positional_encoding = np.array([
  • [pos / np.power(10000, 2 * i / embed_dim) for i in range(embed_dim)]
  • if pos != 0 else np.zeros(embed_dim) for pos in range(max_seq_len)])
  • positional_encoding[1:, 0::2] = np.sin(positional_encoding[1:, 0::2]) # dim 2i 偶数
  • positional_encoding[1:, 1::2] = np.cos(positional_encoding[1:, 1::2]) # dim 2i+1 奇数
  • return positional_encoding
  • positional_encoding = get_positional_encoding(max_seq_len=100, embed_dim=16)
  • plt.figure(figsize=(10,10))
  • sns.heatmap(positional_encoding)
  • plt.title("Sinusoidal Function")
  • plt.xlabel("hidden dimension")
  • plt.ylabel("sequence length")

  • plt.figure(figsize=(8, 5))
  • plt.plot(positional_encoding[1:, 1], label="dimension 1")
  • plt.plot(positional_encoding[1:, 2], label="dimension 2")
  • plt.plot(positional_encoding[1:, 3], label="dimension 3")
  • plt.legend()
  • plt.xlabel("Sequence length")
  • plt.ylabel("Period of Positional Encoding")

2. Self Attention Mechanism

对于输入的句子 $X$,通过 WordEmbedding 得到该句子中每个字的字向量,同时通过 Positional Encoding 得到所有字的位置向量,将其相加(维度相同,可以直接相加),得到该字真正的向量表示。第 $t$ 个字的向量记作 $x_t$

接着我们定义三个矩阵 $W_Q,W_K.W_V$,使用这三个矩阵分别对所有的字向量进行三次线性变换,于是所有的字向量又衍生出三个新的向量 $q_t,k_t,v_t$。我们将所有的 $q_t$ 向量拼成一个大矩阵,记作查询矩阵 $Q$,将所有的 $k_t$ 向量拼成一个大矩阵,记作键矩阵 $K$,将所有的 $v_t$ 向量拼成一个大矩阵,记作值矩阵 $V$(见下图)

为了获得第一个字的注意力权重,我们需要用第一个字的查询向量 $q_1$ 乘以键矩阵 K(见下图)

  • [0, 4, 2]
  • [1, 0, 2] x [1, 4, 3] = [2, 4, 4]
  • [1, 0, 1]

之后还需要将得到的值经过 softmax,使得它们的和为 1(见下图)

  • softmax([2, 4, 4]) = [0.0, 0.5, 0.5]

有了权重之后,将权重其分别乘以对应字的值向量 $v_t$(见下图)

  • 0.0 * [1, 2, 3] = [0.0, 0.0, 0.0]
  • 0.5 * [2, 8, 0] = [1.0, 4.0, 0.0]
  • 0.5 * [2, 6, 3] = [1.0, 3.0, 1.5]

最后将这些权重化后的值向量求和,得到第一个字的输出(见下图)

  • [0.0, 0.0, 0.0]
  • + [1.0, 4.0, 0.0]
  • + [1.0, 3.0, 1.5]
  • -----------------
  • = [2.0, 7.0, 1.5]

对其它的输入向量也执行相同的操作,即可得到通过 self-attention 后的所有输出

矩阵计算

上面介绍的方法需要一个循环遍历所有的字 $x_t$,我们可以把上面的向量计算变成矩阵的形式,从而一次计算出所有时刻的输出

第一步就不是计算某个时刻的 $q_t,k_t,v_t$ 了,而是一次计算所有时刻的 $Q,K$ 和 $V$。计算过程如下图所示,这里的输入是一个矩阵 $X$,矩阵第 $t$ 行为第 $t$ 个词的向量表示 $x_t$

接下来将 $Q$ 和 $K^T$ 相乘,然后除以 $\sqrt {d_k}$(这是论文中提到的一个 trick),经过 softmax 以后再乘以 $V$ 得到输出

Multi-Head Attention

这篇论文还提出了 Multi-Head Attention 的概念。其实很简单,前面定义的一组 $Q,K,V$ 可以让一个词 attend to 相关的词,我们可以定义多组 $Q,K,V$,让它们分别关注不同的上下文。计算 $Q,K,V$ 的过程还是一样,只不过线性变换的矩阵从一组 $(W^Q,W^K,W^V)$ 变成了多组 $(W^Q_0,W^K_0,W^V_0)$ ,$(W^Q_1,W^K_1,W^V_1)$,… 如下图所示

对于输入矩阵 $X$,每一组 $Q$、$K$ 和 $V$ 都可以得到一个输出矩阵 $Z$。如下图所示

Padding Mask

上面 Self Attention 的计算过程中,我们通常使用 mini-batch 来计算,也就是一次计算多句话,即 $X$ 的维度是 [batch_size, sequence_length],sequence_length​是句长,而一个 mini-batch 是由多个不等长的句子组成的,我们需要按照这个 mini-batch 中最大的句长对剩余的句子进行补齐,一般用 0 进行填充,这个过程叫做 padding

但这时在进行 softmax 就会产生问题。回顾 softmax 函数 $\sigma (z_i)=\frac {e^{z_i}}{\sum_{j=1}^K e^{z_j}}$,$e^0$ 是 1,是有值的,这样的话 softmax 中被 padding 的部分就参与了运算,相当于让无效的部分参与了运算,这可能会产生很大的隐患。因此需要做一个 mask 操作,让这些无效的区域不参与运算,一般是给无效区域加一个很大的负数偏置,即

$$ \begin{align*} &Z_{illegal}=Z_{illegal}+bias_{illegal}\\ &bias_{illegal}→-∞ \end{align*} $$

3. 残差连接和 Layer Normalization

残差连接

我们在上一步得到了经过 self-attention 加权之后输出,也就是 $\text {Self-Attention}(Q, \ K, \ V)$,然后把他们加起来做残差连接

$$ X_{embedding} + \text{Self-Attention}(Q, \ K, \ V) $$

Layer Normalization

Layer Normalization 的作用是把神经网络中隐藏层归一为标准正态分布,也就是 $i.i.d$ 独立同分布,以起到加快训练速度,加速收敛的作用

$$ \mu_{j}=\frac{1}{m} \sum^{m}_{i=1}x_{ij} $$

上式以矩阵的列(column)为单位求均值;

$$ \sigma^{2}_{j}=\frac{1}{m} \sum^{m}_{i=1}(x_{ij}-\mu_{j})^{2} $$

上式以矩阵的列(column)为单位求方差

$$ LayerNorm(x)=\frac{x_{ij}-\mu_{j}}{\sqrt{\sigma^{2}_{j}+\epsilon}} $$

然后用每一列每一个元素减去这列的均值,再除以这列的标准差,从而得到归一化后的数值,加 $\epsilon$ 是为了防止分母为 0

下图展示了更多细节:输入 $x_1,x_2$ 经 self-attention 层之后变成 $z_1,z_2$,然后和输入 $x_1,x_2$ 进行残差连接,经过 LayerNorm 后输出给全连接层。全连接层也有一个残差连接和一个 LayerNorm,最后再输出给下一个 Encoder(每个 Encoder Block 中的 FeedForward 层权重都是共享的)

4. Transformer Encoder 整体结构

经过上面 3 个步骤,我们已经基本了解了 Encoder 的主要构成部分,下面我们用公式把一个 Encoder block 的计算过程整理一下:

1). 字向量与位置编码

$$ X = \text{Embedding-Lookup}(X) + \text{Positional-Encoding} $$

2). 自注意力机制

$$ Q = \text{Linear}_q(X) = XW_{Q}\\ K = \text{Linear}_k(X) = XW_{K}\\ V = \text{Linear}_v(X) = XW_{V}\\ X_{attention} = \text{Self-Attention}(Q,K,V) $$

3). self-attention 残差连接与 Layer Normalization

$$ X_{attention} = X + X_{attention}\\ X_{attention} = \text{LayerNorm}(X_{attention}) $$

4). 下面进行 Encoder block 结构图中的第 4 部分,也就是 FeedForward,其实就是两层线性映射并用激活函数激活,比如说 $ReLU$

$$ X_{hidden} = \text{Linear}(\text{ReLU}(\text{Linear}(X_{attention}))) $$

5). FeedForward 残差连接与 Layer Normalization

$$ X_{hidden} = X_{attention} + X_{hidden}\\ X_{hidden} = \text{LayerNorm}(X_{hidden}) $$

其中

$$ X_{hidden} \in \mathbb{R}^{batch\_size \ * \ seq\_len. \ * \ embed\_dim} $$

5. Transformer Decoder 整体结构

我们先从 HighLevel 的角度观察一下 Decoder 结构,从下到上依次是:

  • Masked Multi-Head Self-Attention
  • Multi-Head Encoder-Decoder Attention
  • FeedForward Network

和 Encoder 一样,上面三个部分的每一个部分,都有一个残差连接,后接一个 Layer Normalization。Decoder 的中间部件并不复杂,大部分在前面 Encoder 里我们已经介绍过了,但是 Decoder 由于其特殊的功能,因此在训练时会涉及到一些细节

Masked Self-Attention

具体来说,传统 Seq2Seq 中 Decoder 使用的是 RNN 模型,因此在训练过程中输入 $t$ 时刻的词,模型无论如何也看不到未来时刻的词,因为循环神经网络是时间驱动的,只有当 $t$ 时刻运算结束了,才能看到 $t+1$ 时刻的词。而 Transformer Decoder 抛弃了 RNN,改为 Self-Attention,由此就产生了一个问题,在训练过程中,整个 ground truth 都暴露在 Decoder 中,这显然是不对的,我们需要对 Decoder 的输入进行一些处理,该处理被称为 Mask

举个例子,Decoder 的 ground truth 为 "<start> I am fine",我们将这个句子输入到 Decoder 中,经过 WordEmbedding 和 Positional Encoding 之后,将得到的矩阵做三次线性变换($W_Q,W_K,W_V$)。然后进行 self-attention 操作,首先通过 $\frac {Q\times K^T}{\sqrt {d_k}}$ 得到 Scaled Scores,接下来非常关键,我们要对 Scaled Scores 进行 Mask,举个例子,当我们输入 "I" 时,模型目前仅知道包括 "I" 在内之前所有字的信息,即 "<start>" 和 "I" 的信息,不应该让其知道 "I" 之后词的信息。道理很简单,我们做预测的时候是按照顺序一个字一个字的预测,怎么能这个字都没预测完,就已经知道后面字的信息了呢?Mask 非常简单,首先生成一个下三角全 0,上三角全为负无穷的矩阵,然后将其与 Scaled Scores 相加即可

之后再做 softmax,就能将 - inf 变为 0,得到的这个矩阵即为每个字之间的权重

Multi-Head Self-Attention 无非就是并行的对上述步骤多做几次,前面 Encoder 也介绍了,这里就不多赘述了

Masked Encoder-Decoder Attention

其实这一部分的计算流程和前面 Masked Self-Attention 很相似,结构也一摸一样,唯一不同的是这里的 $K,V$ 为 Encoder 的输出,$Q$ 为 Decoder 中 Masked Self-Attention 的输出

6. 总结

到此为止,Transformer 中 95% 的内容已经介绍完了,我们用一张图展示其完整结构。不得不说,Transformer 设计的十分巧夺天工

下面有几个问题,是我从网上找的,感觉看完之后能对 Transformer 有一个更深的理解

Transformer 为什么需要进行 Multi-head Attention?

原论文中说到进行 Multi-head Attention 的原因是将模型分为多个头,形成多个子空间,可以让模型去关注不同方面的信息,最后再将各个方面的信息综合起来。其实直观上也可以想到,如果自己设计这样的一个模型,必然也不会只做一次 attention,多次 attention 综合的结果至少能够起到增强模型的作用,也可以类比 CNN 中同时使用多个卷积核的作用,直观上讲,多头的注意力有助于网络捕捉到更丰富的特征 / 信息

Transformer 相比于 RNN/LSTM,有什么优势?为什么?

  1. RNN 系列的模型,无法并行计算,因为 T 时刻的计算依赖 T-1 时刻的隐层计算结果,而 T-1 时刻的计算依赖 T-2 时刻的隐层计算结果
  2. Transformer 的特征抽取能力比 RNN 系列的模型要好

为什么说 Transformer 可以代替 seq2seq?

这里用代替这个词略显不妥当,seq2seq 虽已老,但始终还是有其用武之地,seq2seq 最大的问题在于将 Encoder 端的所有信息压缩到一个固定长度的向量中,并将其作为 Decoder 端首个隐藏状态的输入,来预测 Decoder 端第一个单词 (token) 的隐藏状态。在输入序列比较长的时候,这样做显然会损失 Encoder 端的很多信息,而且这样一股脑的把该固定向量送入 Decoder 端,Decoder 端不能够关注到其想要关注的信息。Transformer 不但对 seq2seq 模型这两点缺点有了实质性的改进 (多头交互式 attention 模块),而且还引入了 self-attention 模块,让源序列和目标序列首先 “自关联” 起来,这样的话,源序列和目标序列自身的 embedding 表示所蕴含的信息更加丰富,而且后续的 FFN 层也增强了模型的表达能力,并且 Transformer 并行计算的能力远远超过了 seq2seq 系列模型

7. 参考文章

Last Modified: June 6, 2021
Archives Tip
QR Code for this page
Tipping QR Code
Leave a Comment

124 Comments
  1. lawson lawson

    softmax ([2,4,4,]) 的结果是 ([0.0634, 0.4683, 0.4683])

  2. lawson lawson

    tql, 真的 tql

  3. vip vip

    我也觉得,tql,感觉清北大佬的水平

  4. yu yu

    position_encoding 应该是 2*(i//2) 吧

    1. mathor mathor

      @yu 不是的,就是我上面的公式

    2. mor mor

      @mathor 您好,关于 position_encoding 代码里面 i (10000 的指数 2i/dmodel) 的取值,我有一些看法。
      为了方便描述,我们假设 encoding_dim=6,如果我们按照 [2 * i for i in range (6)] 来生成,得到的会是 [0, 2, 4, 6, 8, 10],而从论文的公式和您的笔记来看,我们想要的 i 应是 [0, 0, 2, 2, 4, 4]。
      也许可以这样:[2 * i for i in range (3)] 得到 [0, 2, 4],再进一步得到 [0, 0, 2, 2, 4, 4]。
      当然,我省略了 np.power ()。

    3. mathor mathor

      @mor 这个不太容易跟您解释,就连我自己都想了一晚上我是否错了,不过我在上面代码第 13 行加了一个 print 语句,打印 positional_encoding,并且对照公式算了一下,发现我没有错,要不您也试一下 @(太开心)

    4. how哇you how 哇 you

      @mor 同意 @mor。希望博主再仔细考证一下。

    5. Dionysus Dionysus

      @mor 可以(i//2)for i in range (embed_dim)

    6. Yao Yao

      @Dionysus 我最后试出来是 2*np.floor (I/2) for I in range (embed_dim)

    7. xhhdsh xhhdsh

      @mathor 公式没问题,但是代码确实写错了

    8. wang wang

      @yu 博主既然是先计算的 temp=pos/10000^(2i/d),那就应该保证 2i 位置和 2i+1 位置的 temp 值是相等的,但是代码中很明显是不同的呢 @(委屈)

    9. 剪烛西窗 剪烛西窗

      @mor 我理解了博主没有错。论文和这篇文章的描述是正确的,因为他是针对 PE (pos,2i) 和 PE (pos,2i+1) 分别描述的,所以范围是(2,encoding_dim/2)。而这篇文章中的代码是先求出来所有的,然后再分别求奇数位置和偶数位置。

    10. 4daJKong 4daJKong

      @mor(2 * i / embed_dim) for i in range(embed_dim)
      同意,这个嵌入生成确实有点问题,希望楼主能看下

    11. 4daJKong 4daJKong

      @4daJKong [(2 * (hid_j // 2)) for hid_j in range(hidden_dim)]
      这样生成的才对

  5. NLPer NLPer

    您好,我想转载该文,希望能都得到授权

    1. mathor mathor

      @NLPer 请问转载到什么地方?

    2. NLPer NLPer

      @mathor 您好,微信公众号:python 遇见 NLP

    3. mathor mathor

      @NLPer 可以,请在开头注明出处

    4. NLPer NLPer

      @mathor 好的,感谢

    5. cc cc

      @mathor 博主 你上面的图显示不出来

    6. 喵子 喵子

      @cc 我这边开了梯子就可以显示出来了

  6. 孙暖暖 孙暖暖

    给 up 主打 call 太厉害了!

    1. mathor mathor

      @孙暖暖谢谢

  7. zfx zfx

    受益匪浅,谢谢博主!!

  8. Liam Liam

    醍醐灌顶,谢谢博主,tql@(大拇指)

  9. CCZ CCZ

    博主,我是在你写给贺老师的邮件中读到你目前的状态,来到了这里,曾经我也是贺老师的学生;博主你的能力应该是挺强的,贺老师给你的回复也很贴合;我目前也是一名工科 211 在读研究生

    1. mathor mathor

      @CCZ 这么有缘吗,您居然是贺老师的学生!

  10. Vermouth Vermouth

    LayerNorm,是沿 Embedding 的方向进行标准化的吧?

    1. mathor mathor

      @Vermouth 是的

    2. gq gq

      @mathor 为什么使用 Layer Normalization,而不使用 Batch Normalization ?

    3. mathor mathor

      @gq 效果不好呗,还能是什么原因

    4. 肖恩懒洋杨 肖恩懒洋杨

      @gqbatchNorm 要指定 L (句子长度), 但是每个 batch 的最大句子长短是不固定的。

    5. 66 66

      @gqbatch 的只能是相当于同一维度的 但是因为长短不一 所以 layer 相当于同一个东西 来进行 nor 所以会更好

  11. Hang Zhou Hang Zhou

    请问训练的时候 decoder 是如何处理 dec_input, 因为我看你在 greedy search 中用了了 for-loop, 一步步更新 dec_input. 但是这个操作,在训练模型的时候不需要吗? 我指训练的时候如何一步步利用 dec_input 里面的真实值,好像在代码中没找到相关内容

    1. Hang Zhou Hang Zhou

      @Hang Zhou 好像找到相关解释了,,是并行处理了。避开了 loop

    2. idealcp idealcp

      @Hang Zhou 你好 请问在哪里看到相关解释了 这个问题我也没搞明白 非常感谢!

  12. obsidian obsidian

    加油,讲的很棒!

  13. phac123 phac123

    大佬你好,我想问一个问题,下面这个地方 i 的范围是不是应该除以二,
    [pos / np.power(10000, 2 * i / embed_dim) for i in range(embed_dim)]

    即为:[pos /np.power (10000, 2 * i /embed_dim) for i in range (embed_dim // 2)]

    1. fantasy fantasy

      @phac123 那一步只是算了 sin 或者 cos 里面的值,真正取值是按照步长为 2 来取得

    2. 阿木 阿木

      @phac123 我也觉得这步好像有些问题

  14. solthx solthx

    总结的太好了,赞!

  15. wisley wisley

    非常棒! 就是 LN 的地方图表示的不是很清楚感觉,对应的应该是 batch_size,max_leng,hidden 的张量来画图表示可能会更好!

    1. 喵子 喵子

      @wisley 说的太对了,我一直卡在这了

  16. hellopbc hellopbc

    可见随着 序号增大,位置嵌入函数的周期变化越来越平缓;
    请问这个是怎么看的?

  17. hellopbc hellopbc

    第 t 个字、t 时刻有什么说法嘛?没看太明白

  18. hellopbc hellopbc

    这个 mask 讲的绝,一下就清楚了,要是每个论文都和作者一样写,我就笑死了

  19. hellopbc hellopbc

    博主你好,我想请问一下,有的 bert 预训练模型似乎有一个输入序列长度的限制,请问这是出于什么目的才这样做呢,不限制序列长度不是更加灵活吗

    1. mathor mathor

      @hellopbc 显存是有限的

    2. 肖恩懒洋杨 肖恩懒洋杨

      @hellopbc 不限制序列长度的话, 每次都要重新计算位置嵌入

  20. bin bin

    请问 decoder 哪个 outputs (shifted right) 是 groundtruth 吗,那在 inference 的时候这部分是怎么处理的呀

  21. Jarvis Jarvis

    请问这个输入到 Decoder 的 X_hidder 是怎么变换成 K 和 V 矩阵的?

  22. XD XD

    博主讲的好清晰,打 call 一下

  23. LL人 LL 人

    老哥我想转载你的文章在 CSDN

  24. T T

    同样是研一,差距为啥这么大

  25. king king

    博主,你好。因为 decoder 部分每个层都有两个输入,请问 Transformer 反向传播的路径是怎样的

  26. yuka yuka

    tql,真的 tql@(太开心)

  27. salute salute

    tql 原来是太强了的意思

  28. Olyvar Olyvar

    谢谢,谢谢,受益匪浅

  29. 杰

    楼主写的很好,认真学习后,有个问题问题:1. 在训练的时候目标函数定义的是什么呢?我在原论文中好像没看到。2. 对于 QK 来自 Encoder V 来自 Decode 这句表述,是每一层 Encoder Bolck 和 Decode Block 对应的吗,即第一层的 Encoder Bolck QK 对应 第一层的 Decode Bolck 中的 V,还是只用最外一层的 Encoder Bolck 的 QK 对应于每一层的 Decode Block 种的 V 呢 @(呵呵)

    1. mathor mathor

      @杰 1. 目标函数(损失函数)为 CrossEntropyLoss

      只用 Encoder 最后一层对应每一层 Decoder
  30. 六月 六月

    讲的太清楚了,赞

  31. 烂人一个 烂人一个

    我很好奇,MASK 三角矩阵那里到底应该是按位相乘,还是相加,还是两者都可以,好像别的地方都说的按位相乘,但我觉得你讲解的更正确,按位相乘会存在一些问题,元素符号不确定,相加似乎更稳妥,总是可以得到负无穷,望解答!

    1. mathor mathor

      @烂人一个感觉都可以,但我觉得相加还是要简单一点

  32. 烂人一个 烂人一个

    我还想请教大佬一个问题,为什么字编码和位置编码是直接相加,不是拼接。直觉上直接相加两者不就混在一起了吗?看到一种说法是两者拼接在一起再乘以一个转换矩阵得到的结果就是两者相加,不知道对不对?望解答!

    1. mathor mathor

      @烂人一个拼接肯定不好,假设一个 batch 就一句话,这一句话就两个词,那么向量的维度就是 [1,2,emb_dim],前面的 1 我们省略掉,那么就是 [2, emb_dim]。现在相当于有两个 [emb_dim] 向量,拼接成了 [2,emb_dim] 这样一个矩阵,但是这两个 [emb_dim] 向量是有顺序的,不能随便乱拼,而这个顺序的体现,其实就是通过在 [emb_dim] 向量中融入位置编码

      至于你说的另一种说法,我没看到过,好像也不是目前业界主流

    2. zc zc

      @mathor 大佬,我也有和层主相似的疑问,即是否可以用拼接。按照您的例子,用相加是 [2,emb_dim],用拼接就是变成了 [2, 2*emb_dim](word_emb + position_emb),维度扩展了一倍。
      谢谢大佬,看您的博客收获很多!

  33. Pluto Pluto

    请问训练过程 decoder 的输入是不是就是 groundtruth?还有我记得测试阶段 decoder 的输入好像和训练阶段不一样,博主能解释一下为什么嘛?

  34. 银

    请问大佬几个关于 decoder 测试阶段加速的问题 :
    1、在不损失太多精度的情况下是不是减少 decoder ff_units 或者 decoder layer 数量 或者 seq lenth 来提速?
    2、有没有一种方法可以不让 decoder 的预测串行 比如之前是预测完第一个字再预测第二个 能不能直接把全部的 encoder 的信息拿来并行预测?

  35. 亭亭玉立 亭亭玉立

    Layer Normalization 部分应该是矩阵的行吧,应该不是列

  36. Yang Yang

    博主您好,在多头注意力部分,attentiona is all your need 中是将已经得到的一组 Q,K,V 通过不同的线性变换得到不同的 Q,K,V。您在博客中写的,是直接使用不同的 Wq, Wk, Wv,去处理输入,得到不同的 Q,K,V。
    我个人觉得这两者似乎没有任何差别,不知道想的对不对? 在相关的论文里,大家是习惯用第一种方式来描述,还是第二种哎?
    感谢!

  37. 阿木 阿木

    博主您好,我认为 [pos /np.power (10000, 2 * (i//2) /embed_dim) for i in range (embed_dim)]

    1. 河东第一萌新 河东第一萌新

      @阿木确实,这个才符合公式

  38. NLP工程师 NLP 工程师

    您好,可否转载您的系列文章?公众号:算法工程师,会声明出处并加上原文链接!

    1. mathor mathor

      @NLP 工程师 no

  39. JuniorSummer JuniorSummer

    博主你好有些数学公式失效变成 [Math Processing Error],直接就看不懂了,能麻烦博主重新更新下吗 @(乖)

    1. JuniorSummer JuniorSummer

      @JuniorSummer 回复完发现突然能看了,那没事了,不麻烦博主啦~

    2. mathor mathor

      @JuniorSummer 强制刷新

  40. gasol gasol

    博主好厉害!新 nlper 看了醍醐灌顶!

  41. su人 su 人

    这动图是怎么做的呀

    1. mathor mathor

      @su 人这不是我做的,是我从某个外文博客上 download 下来的

  42. hhhttt hhhttt

    浩哥?

  43. Feirou Feirou

    太强了 @(大拇指)@(大拇指)@(大拇指)

  44. yiyan yiyan

    只有我想问 W_Q,W_K,W_V 是怎么来的吗?看了好多资料还是不清楚是怎么这三个系数矩阵是怎么设计的,哪位好心人帮一下啊?

    1. mathor mathor

      @yiyan 学过 pytorch 吗,学过的话就是 nn.Linear () 直接产生的

      没学过的话,你可以理解为随机产生的三个矩阵

  45. 问刘十九 问刘十九

    博主,有一点不明白的位置,前面几层的 decoder 的 input,你视频你说的是不经过 linear,直接送到下一个 decoder 的。那图中的 output embedding,在训练过程中是只在第一层 decoder 上对 ground truth 进行编码,其他层的 decoder 就没有用到 output embedding 了。那在 inference 的时候,是不是第一次是对 <SOS> 进行 embedding,送到模型,然后预测出下一个单词了,再进行一次 embedding,是这样理解吗?

    1. mathor mathor

      @问刘十九我画了一张图,不知道能否解答你的疑问

      https://z3.ax1x.com/2021/11/24/oCH0Qf.jpg

    2. 问刘十九 问刘十九

      @mathor 懂了,懂了,谢谢博主

  46. 阿QQ 阿 QQ

    博主讲的太好了,疑惑顿消。请问可以转载么?转到 CSDN 博客,只用于自己学习

    1. mathor mathor

      @阿 QQsure

  47. fightingX fightingX

    通俗易懂

  48. 123456 123456

    请问经过自注意力机制后的矩阵 是一个对称矩阵吗?

  49. Mr.Anonymous Mr.Anonymous

    博主的文章写的好棒!希望加个 markdown 下载的功能,方便将您的博文收藏和添加注释。谢谢分享~@(笑眼)

    1. mathor mathor

      @Mr.Anonymous 以国内的环境来看,这样只会更方便大家互相抄

  50. ML ML

    博主这里可以添加一个细节,Q,K,V 的维度,很多博客都未涉及这点,新手看 transformer,认为三个矩阵维度都是一样的

  51. jshn jshn

    大佬好厉害!!!!!!

  52. zhy zhy

    大佬您好,一直有个疑惑。请问 encoder 输出给 decoder 的 k 和 v 就是 Xhidden 吗?谢谢

  53. Foreverythin Foreverythin

    太强了

  54. nlp-learning nlp-learning

    博主你好,自注意机制那里好像有点问题,单个词向量得到的权重分别和各个值向量相乘再求和,但是在矩阵计算中是将经过 softmax 的权重矩阵直接和值矩阵相乘,这两种计算方法应该是不一样的吧?

  55. aaxym aaxym

    太厉害了,大佬 @(真棒)

  56. Liisu Liisu

    图片是不是都挂了

  57. 怂怂 怂怂

    写得是真的好啊 @(花心)

  58. try try

    在用矩阵方法计算注意力的时候提到了论文中除以根号下 d_k 的 trick,博客里好像没有对这个 d_k 进行说明,我查了一下原论文,发现是 key 矩阵的 dimension,希望作者有时间可以补充说明一下

  59. Cqiang Cqiang

    我想问一个题外话,我是 CSDN 过来的,请问一下这个博客,是什么网站或者说是什么名字,这排版看着好舒服啊!!!

    1. mathor mathor

      @Cqiang 这个博客用的是 typecho 框架搭建的,你所看到的排版或者界面是用的一个叫 Mirages 主题,就在博客最底下有个超链接,点进去就可以了解了

  60. 小鱼 小鱼

    “为了获得第一个字的注意力权重,我们需要用第一个字的查询向量 乘以键矩阵 K(见下图)”,这里 K 得写成 K^T,可能作者一下写错了吧。总体来说,作者这篇文章写得太棒了,学到许多,也让我弄懂了这个 transformer 中的运算以及原理,感谢。

  61. 小鱼 小鱼

    Wq,Wk,Wv 三个矩阵大小都是字向量的维数 m * 字向量的维数 m 形式的。

    1. 小鱼 小鱼

      @小鱼加上这个让内容更加充分。

    2. 小白 小白

      @小鱼多头注意力机制输入是一个 batch,请问是否是也计算句子与句子之间的权重呢

    3. 小鱼 小鱼

      @小白只计算句子内部。

  62. yyer yyer

    tql 太强了

  63. ebgert ebgert

    您好,想请教一下文章中的动图是用什么做的

  64. AI_magician AI_magician

    这里的 layer normalization 应该也可以是和 batch norm 一样的,可以学习偏移和平移变化的参数吧,而不是单纯用 z-score 吧?@(哈哈)

  65. nunu nunu

    大佬,请收下我的膝盖。

  66. heron heron

    大佬

  67. Neon Neon

    两年前看的时候研 0,为了接触实验室的科研任务看的第一个大型深度学习模型,跟着复现了一遍代码,看得懵懵懂懂;转眼间研二,做科研一年多,用了无数次 Transformer,今天为了背八股又翻回来看数学家写的 Transformer 博客,感觉有了更透彻的理解,真是厉害啊。

    1. mathor mathor

      @Neon 你有了更深的理解,是你厉害

  68. 北风 北风

    你的文章写的很棒很棒

  69. zql zql

    这篇文章在 Positional Encoding 部分就征服我了

    1. 胖子 胖子

      @zql 实际上 position encoding 的作用在自然语言处理领域有用,如果把 transformer 用到 graph 或者 CV 中就不应该用 position encoding(注意,是不该用,而不是说用了之后没用,实际上用了之后是给了一个自己提前定的权重模式)。
      自然语言处理由于存在主谓宾定装补这种词性,所以这些词性的词语在句子中 position 一般都比较固定,所以用 position encoding。
      不过这儿也有几个问题,第一是这些词语实际位置不是严格固定的,第二是并没有一定特别好的 position encoding 方法,只需要是一个随着位置变化而变化的数值就行。

      一家之言,我看过全网都没人分析为什么要 position encoding。我不是做 NLP 的,我是做基础算法,当年分析过 Graph 和 NLP 的异同,实际上这个 position encoding 从某种意义上弥补了以前 word2vec 的不足。

  70. kjz kjz

    24 年研 0,cv 方向,老板让搞 vit。天杀的,本科四年就没搞过 nlp,查阅了很多资料,论文也读了好几遍,对于流程一知半解,最开始的 decoder 中输入名称是 outputs 就一直不明白,看来博主的帖子,豁然开朗,万分感谢。(对于自己的方向有一种 94 年入....... 的感觉,不知道大佬们有什么看法)。

  71. 步行者 步行者

    厉害

  72. 白桦林 白桦林

    真是一个牛逼了得啊,写的太好了,无敌。

  73. zeng zeng

    Positional Encoding 似乎有问题。代码里 for i in range (embed_dim),先然 i 的取值范围为 [0, embeeding_dimension), 而不是论文中的 [0, embeeding_dimension/2)