【阅读笔记】通俗易懂的 transformer 笔记

这里写目录标题

前言

这篇文章是关于《如何从浅入深理解transformer》 的一个阅读笔记。

因为是第一次接触 transformer,找了半天,感觉这篇文章作为入门来说还不错,可以将整个发展的历程串联起来讲。

但是这毕竟是一篇阅读笔记,我只会对个人不太清楚的概念详细了解。

第 1 节 · N 元文法语言模型

n-Gram 模型的主体思想就是:下一个词出现的概率只依赖于它前面 n-1 个词,也被称为 n 阶马尔科夫链。

文中出现的公式: P ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) = C ( w 1 , w 2 , . . . , w t ) C ( w 1 , w 2 , . . . , w t − 1 ) P(w_t|w_1, w_2,...,w_{t-1}) = \frac{C(w_1, w_2,...,w_{t})}{C(w_1, w_2,...,w_{t-1})} P(wtw1,w2,...,wt1)=C(w1,w2,...,wt1)C(w1,w2,...,wt) 其中的参数 C ( w 1 , w 2 , . . . , w t ) C(w_1, w_2,...,w_{t}) C(w1,w2,...,wt) 表示词序列 w 1 , w 2 , . . . , w t w_1, w_2,...,w_{t} w1,w2,...,wt 在训练语料中出现的次数(count)。 C ( w 1 , w 2 , . . . , w t − 1 ) C(w_1, w_2,...,w_{t-1}) C(w1,w2,...,wt1) 也是一样的含义。但是根据条件概率的定义的,本来的公式应该是: P ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) = P ( w 1 , w 2 , . . . , w t ) P ( w 1 , w 2 , . . . , w t − 1 ) P(w_t|w_1, w_2,...,w_{t-1}) = \frac{P(w_1, w_2,...,w_{t})}{P(w_1, w_2,...,w_{t-1})} P(wtw1,w2,...,wt1)=P(w1,w2,...,wt1)P(w1,w2,...,wt)也就是应该使用词序列在训练语料里出现的概率来进行计算的。

为什么第一个公式使用了次数(count)来代替概率(probability)来计算条件概率呢?

由于在实际应用中,直接计算词序列的联合概率 P ( w 1 , w 2 , . . . , w t ) P(w_1, w_2,...,w_{t}) P(w1,w2,...,wt) 通常不可行,因为它们涉及到整个词序列的概率计算,而这些概率往往非常小,容易导致数值下溢。因此,通常使用词频来近似这些概率。
根据大数定律,当语料库足够大时,词频可以近似为概率。因此,可以将上述公式中的概率用词频来替代:

接着,我们就可以对词序列 w 1 , w 2 , . . . , w t w_1, w_2,...,w_{t} w1,w2,...,wt 的概率 P ( w 1 , w 2 , . . . , w t ) P(w_1, w_2,...,w_{t}) P(w1,w2,...,wt) 进行计算了: P ( w 1 , w 2 , . . . , w t ) = P ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) ⋅ P ( w t − 1 ∣ w 1 , w 2 , . . . , w t − 2 ) … P ( w 1 ) = ∏ t − 1 i = 1 P ( w i ∣ w 1 : i − 1 ) \begin{align*} P(w_1, w_2,...,w_{t}) &= P(w_t|w_1, w_2,...,w_{t-1}) \cdot P(w_{t-1}|w_1, w_2,...,w_{t-2}) \dots P(w_1) \\ &=\prod_{t-1}^{i=1} P(w_i|w_{1:i-1}) \end{align*} P(w1,w2,...,wt)=P(wtw1,w2,...,wt1)P(wt1w1,w2,...,wt2)P(w1)=t1i=1P(wiw1:i1) 上面这个是 n-gram 模型计算词序列概率的式子,也就是第 n 个词只依赖于它前面的 n-1 个词。

如果是 n = 2 的情况,也就是二元文法 bi-gram 模型的情况呢,第 n 个词只依赖于它前面的第 n-1 个词,也就是当前词语出现的概率只由上一个词决定。我们可以稍微简化一下上面的公式,就可以得到: P ( w 1 , w 2 , . . . , w t ) = ∏ i = 1 t − 1 P ( w i ∣ w i − 1 ) = ∏ i = 1 t − 1 C ( w i − 1 , w i ) C ( w i − 1 ) P(w_1, w_2,...,w_{t}) = \prod_{i=1}^{t-1} P(w_i|w_{i-1}) = \prod_{i=1}^{t-1}\frac{C(w_{i-1}, w_i)}{C(w_{i-1})} P(w1,w2,...,wt)=i=1t1P(wiwi1)=i=1t1C(wi1)C(wi1,wi) 这就是二元文法 bi-gram 模型计算词序列出现概率的公式。

  • 如果有词出现次数为了 0,这一串乘出来就是 0 了,咋办?
  • 因为基于马尔科夫假设,所以 N 固定窗口取值,对长距离词依赖的情况会表现很差。
  • 如果把 N 值取很大来解决长距离词依赖,则会导致严重的数据稀疏(零频太多了),参数规模也会急速爆炸(高维张量计算)。

文中提出了上面 3 个问题,这个三个问题其实是有某种递进关系的,归纳起来就是

  • 零频
  • N 取值过小
  • N 取值过大

首先我们可以观察上面的 n-gram 公式,如果连乘的概率中出现一个词序列的概率为 0 的话,就会导致整个结果都变为 0。这个问题要怎么尽可能避免了呢,通过减少 N 的取值?尽可能杜绝零频率词序列的出现?但是这会导致长距离词依赖(Long-Range Dependencies)变现变差的问题,如果为了解决长距离词依赖的问题,将 N 值取得很大呢,这又会导致参数爆炸和数据稀疏的问题(也就是回到了问题一,出现词序列零频的问题)。所以这三个问题就像是一个恶性循环,如果尝试解决一个问题,就会引入另一个问题。

所以我们要另辟蹊径,通过外部的手段解决零频的问题,而不是简单的通过调整 N 值的大小。

1.2、平滑(Smoothing)

词序列零频的问题是普遍存在的:

  • 未登录词:N 值比较大的时候,可能导致的未登录词问题(Out Of Vocabulary,OOV),N 越大,代表着词序组合的长度越长,很多词序组合可能在训练数据中可能就出现了一两次,甚至没有出现过,并不具备统计学意义。
  • 有限的训练数据:训练数据很可能也不是 100% 完备覆盖实际中可能遇到的词的。

为了解决零频的问题,我们引入是平滑(smoothing)的概念。

简单来说就是

1.2.1、加 1 平滑 / 拉普拉斯平滑(Add-One Smoothing / Laplace Smoothing)

bi-gram 的加 1 平滑公式: P ( w i ∣ w i − 1 ) = C ( w i − 1 , w i ) + 1 ∑ j = 1 V ( C ( w i − 1 , w j ) + 1 ) = C ( w i − 1 , w i ) + 1 C ( w i − 1 ) + V P(w_i|w_{i-1}) = \frac{C(w_{i-1}, w_i) +1}{ {\textstyle \sum_{j=1}^{V}} (C(w_{i-1}, w_j) + 1)} = \frac{C(w_{i-1}, w_i)+1}{C(w_{i-1}) + V} P(wiwi1)=j=1V(C(wi1,wj)+1)C(wi1,wi)+1=C(wi1)+VC(wi1,wi)+1 其中:

  • C ( w i − 1 , w i ) C(w_{i-1}, w_i) C(wi1,wi): 表示词序列 w i − 1 , w i w_{i-1}, w_i wi1,wi 的词频
  • ∑ j = 1 V ( C ( w i − 1 , w j ) + 1 ) {\textstyle\sum_{j=1}^{V}} (C(w_{i-1}, w_j) + 1) j=1V(C(wi1,wj)+1):因为 j j j 是从 1 到 V,其中 V 表示的是词汇表的数量,也就是说 w j w_j wj 是词汇表中出现的每一个词。 w i − 1 , w j w_{i-1}, w_j wi1,wj 表示第一个词为 w i − 1 w_{i-1} wi1 的情况下,后面跟着不同词的总次数,所以化简之后可以得到最后部分的式子。

其中 N N N 表示所有词的词频之和, V V V 表示词汇表的大小。

原文中提到上面这句话,这个 N N N 可能是指原文公式的求和的上标 n n n,表示 token 的数量,但是我觉得公式里求和的上标应该是词汇表(vocabulary)的大小。

uni-gram 的加 1 平滑公式是: P ( w i ) = C ( w i ) + 1 N + V P(w_i) = \frac{C(w_i) + 1}{N+V} P(wi)=N+VC(wi)+1这里涉及的 N N N 就是训练的 token 总数,即便是有重复的词语,都会被计算在内, V V V 是词汇表的大小,表示在训练数据中唯一的存在,相当于 set 处理之后的 token。

1.2.2、 δ \delta δ 平滑(Add-K Smoothing / Delta Smoothing)

这是对加 1 平滑的优化,对于某些

bi-gram 的加 δ \delta δ 平滑公式: P ( w i ∣ w i − 1 ) = C ( w i − 1 , w i ) + δ ∑ j = 1 V ( C ( w i − 1 , w j ) + δ ) = C ( w i − 1 , w i ) + δ C ( w i − 1 ) + δ V P(w_i|w_{i-1}) = \frac{C(w_{i-1}, w_i) +\delta}{ {\textstyle \sum_{j=1}^{V}} (C(w_{i-1}, w_j) + \delta)} = \frac{C(w_{i-1}, w_i)+\delta}{C(w_{i-1}) + \delta V} P(wiwi1)=j=1V(C(wi1,wj)+δ)C(wi1,wi)+δ=C(wi1)+δVC(wi1,wi)+δ 和加 1 平滑基本一样,就是将 1 换成了 δ \delta δ

与加 1 平滑相比,加 δ \delta δ 平滑允许对于低频词的平滑程度进行更灵活的控制。当 δ \delta δ 较小时,对低频事件的平滑效果较小,而 δ \delta δ 较大时,平滑效果更加显著。这使得模型在平滑概率时能够更好地适应不同的数据特点。

δ \delta δ 是一个超参数,如果可以寻找到最优的值,就可以使得模型的效果更好,原文中说确定这个 δ \delta δ 值需要用到困惑度(Perplexity),但是其实个人觉得,只是一种比较间接的影响关系。

首先加 δ \delta δ 平滑操作的对象都是训练集,然后训练出一个语言模型,然后困惑度是用来评价语言模型在测试集上的性能的。通过调整 δ \delta δ 值,进行多次训练,使得语言模型在特定测试集的困惑度最小,这样便能得到最优的模型。

1.2.3、困惑度(Perplexity)

(在这里突然插入一个评价指标有点奇怪)

困惑度:评价一个概率模型预测测试集的能力,困惑度越低,表示模型越好。

下面是针对单一词序列 w 1 , w 2 , … , w N w_1, w_2, \dots ,w_N w1,w2,,wN 的困惑度计算公式,并不是整个测试集的: Perplexity ( W ) = P ( w 1 , w 2 , … , w N ) − 1 N = 1 P ( w 1 , w 2 , … , w N ) N \text{Perplexity}(W) =P(w_1, w_2, \dots ,w_N)^{-\frac{1}{N} }= \sqrt[N]{\frac{1}{P(w_1, w_2, \dots, w_N)}} Perplexity(W)=P(w1,w2,,wN)N1=NP(w1,w2,,wN)1 我们可以看到,公式里是将这段词序列的出现概率开了 N 次根,然后再求了倒数。

为什么要进行开 N 次方根的操作?
为了将不同长度的词序列归一化到一个统一的尺度来进行比较。我们将联合概率标准化为每个词的平均概率,即平均每个词的预测概率。这样,我们可以更好地比较模型在不同长度序列上的性能,而不受序列长度的影响。

我们将前面的联合概率的公式代入进去,就可以得到: Perplexity ( W ) = ∏ i = 1 N 1 P ( w i ∣ w 1 , w 2 , … , w i − 1 ) N \text{Perplexity}(W) = \sqrt[N]{\prod_{i=1}^{N} \frac{1}{P(w_i|w_1,w_2,\dots ,w_{i-1})} } Perplexity(W)=Ni=1NP(wiw1,w2,,wi1)1 也可以得到 bi-gram 的单一词序列困惑度计算公式: Perplexity ( W ) = ∏ i = 1 N 1 P ( w i ∣ w i − 1 ) N \text{Perplexity}(W) = \sqrt[N]{\prod_{i=1}^{N} \frac{1}{P(w_i| w_{i-1})} } Perplexity(W)=Ni=1NP(wiwi1)1

1.3、回退(Back-off)

在回退模型中,计算一个3-gram的概率时,如果这个3-gram在训练数据中存在,则直接使用其概率;如果不存在,则回退到2-gram,然后再回退到1-gram,依此类推。这样,即使某个组合在3-gram中没有观察到,仍然可以通过回退到2-gram等较低阶的模型来估计其概率。

这个感觉还是挺好理解的,一句话来说就是:退而求其次

公式也挺好理解的: P ( w i ∣ w i − 2 , w i − 1 ) = { P ( w i ∣ w i − 2 , w i − 1 ) C ( w i − 2 , w i − 1 , w i ) > 0 P ( w i ∣ w i − 1 ) C ( w i − 2 , w i − 1 , w i ) = 0     and     C ( w i − 1 , w i ) > 0 P(w_i|w_{i-2},w_{i-1}) = \begin{cases} P(w_i|w_{i-2},w_{i-1}) & C(w_{i-2},w_{i-1},w_i)>0 \\ P(w_i|w_{i-1}) & C(w_{i-2},w_{i-1},w_i)=0 \space\space\space\space \text{and} \space\space\space\space C(w_{i-1},w_i)>0 \end{cases} P(wiwi2,wi1)={P(wiwi2,wi1)P(wiwi1)C(wi2,wi1,wi)>0C(wi2,wi1,wi)=0    and    C(wi1,wi)>0

1.4、插值(Interpolation)

在计算 n-gram 的概率的时候,也考虑 n-1-gram,甚至是 n-2-gram 的概率:
P ( w i ∣ w i − 2 , w i − 1 ) = λ 1 P ( w i ∣ w i − 2 , w i − 1 ) + λ 2 P ( w i ∣ w i − 1 ) + λ 3 P ( w i ) P(w_i|w_{i-2},w_{i-1}) = \lambda_1P(w_i|w_{i-2},w_{i-1}) + \lambda_2P(w_i|w_{i-1}) + \lambda_3P(w_i) P(wiwi2,wi1)=λ1P(wiwi2,wi1)+λ2P(wiwi1)+λ3P(wi)

第 2 节 · 感知器(Perceptron)

感知器一般用于处理二分类问题: y = { 1 ,  if  w x + b > 0 0 , othrewise y = \begin{cases} 1, & \text{ if } wx + b > 0 \\ 0, & \text{othrewise} \end{cases} y={1,0, if wx+b>0othrewise其中 w w w 是权重, b b b 是偏置项。

2.1、线性回归(Linear Regression)

上面的公式中 w x + b wx+b wx+b 的计算结果是需要映射到 0,1 两个结果中,是离散值,解决的是分类问题。但是如果我们直接将 w x + b wx+b wx+b 的计算结果拿过来使用,而不进行映射的话,结果将会是一个连续值,问题也会变成线性回归问题。

原本中有一个使用 pytorch 库中 nn.Linear 进行代码实操的代码片段,这里就不多说了。

2.2、逻辑回归(Logistic Regression)

如果说线性回归的结果是没有限制范围的,那么逻辑回归就会将计算结果限制在一个特定范围之内。

在线性回归的公式为: y = w x + b y = wx + b y=wx+b

那么在逻辑回归中: y = σ ( w x + b ) y = \sigma (wx + b) y=σ(wx+b)

激活函数会将线性回归 w x + b wx+b wx+b 的计算结果,再限制在一个确定的范围了。

在这里插入图片描述

上面这张图展示了逻辑回归的流程,已经有点神经网络的神经元的雏形了。

2.3、Sigmoid 回归

sigmoid 函数是我们在神经网络中常见到的激活函数,简单来说,当逻辑回归中的 σ ( z ) \sigma(z) σ(z) 是 sigmoid 函数的时候,这个逻辑回归也叫做 sigmoid 回归。
y = Sigmoid ( w x + b ) = 1 1 + e − ( w x + b ) y = \text{Sigmoid} (wx + b) = \frac{1}{1+e^{-(wx+b)}} y=Sigmoid(wx+b)=1+e(wx+b)1 其中 Sigmoid ( z ) = 1 1 + e − z \text{Sigmoid}(z) = \frac{1}{1+e^{-z}} Sigmoid(z)=1+ez1,这个 sigmoid 函数会将结果值限制在 [ 0 , 1 ] [0, 1] [0,1] 之间。

一般是用在二分类问题中预测概率。

2.4、Softmax 回归

在这里插入图片描述
上面说到 sigmoid 回归主要是针对二分类的问题,现在要介绍的 softmax 回归主要就是针对多分类的问题

如上图所示,一个输入 x x x,如果要进行 softmax 回归,进行类别为 K K K 的 多元分类。首先和 sigmoid 一样,需要经过各自类别的权重 w K w_K wK 和偏置 b K b_K bK 处理之后,得到每个类别的原始得分 z K z_K zK。然后一起送入到 softmax 函数内计算对应类别的概率。

这个公式就是计算输入在类别 i i i 的概率:
y i = σ ( z ) i = e z i ∑ j = 1 K e z j = e z i e z 1 + e z 2 + ⋯ + e z K y_i = \sigma (\bold{z})_i = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}} = \frac{e^{z_i}}{ e^{z_1} + e^{z_2} + \dots + e^{z_K} } yi=σ(z)i=j=1Kezjezi=ez1+ez2++ezKezi其中有 z K = w K x + b K z_K = w_K x + b_K zK=wKx+bK

一般来说,输入 x x x 其实也是一个维度为 K K K 的向量,如果不足的可以进行补零,超出的话就进行保留最主要的 K K K 个特征。这里我假设这个输入向量 x \bold{x} x 的所有维度的值都是 x x x

2.6、多层感知器(Multi-Layer Perceptron)

MLP 是一种全连接的前馈神经网络,每一层的每个神经元都与上一层的所有神经元相连,有点全连接层的意思。

而且,MLP 中的激活函数必须得是非线性激活函数,如果是线性,不管是多少层,其实都只是一个简单的线性模型,只能解决简单的线性问题,没有非线性的学习能力。

因为全连接的关系,所以一个比较大的缺点就是:计算量非常大。

MLP 主要有输入层,隐藏层和输出层三个部分组成。

其中最重要的是隐藏层,加权求和,还有激活函数的操作都是在隐藏层进行的。

在这里插入图片描述
为了加深对 MLP 的理解,我自己画了一下 MLP 的一个简单的网络结构图。输入层和输出层没有像大多数图那样画成节点的形式,也将权重偏置显式地标注了出来。

图中的 w 0 , 00 w_{0, 00} w0,00 的下标逗号之前的 0 0 0 表示这个参数属于第 0 0 0 层隐藏层的。下标逗号后面的 00 00 00 表示这是输入参数 x 0 x_0 x0 与隐藏单元 h 0 , 0 h_{0, 0} h0,0 之间的参数。

如果将这个网络结构用向量公式表现出来就是下面的式子: A 0 = f ( W 0 ⋅ X + b 0 ) Y = f ( W 1 ⋅ A 0 + b 1 ) \begin{matrix} \mathbf{A_0} = f(\mathbf{ W_0 \cdot X + b_0})\\ \mathbf{Y} = f(\mathbf{W_1 \cdot A_0 + b_1} ) \end{matrix} A0=f(W0X+b0)Y=f(W1A0+b1)其中 W 0 = [ w 0 , 00 w 0 , 10 w 0 , 01 w 0 , 11 w 0 , 02 w 0 , 12 ] ,       X = [ x 0 x 1 ] ,     b 0 = [ b 0 , 00 + b 0 , 10 b 0 , 01 + b 0 , 11 b 0 , 02 + b 0 , 12 ] \mathbf{W_0} = \begin{bmatrix} w_{0,00} & w_{0, 10} \\ w_{0,01} & w_{0, 11}\\ w_{0,02} & w_{0, 12} \end{bmatrix}, \space \space \space \space \space \mathbf{ X} = \begin{bmatrix} x_0\\ x_1 \end{bmatrix}, \space \space \space \mathbf{b_0} =\begin{bmatrix} b_{0,00} + b_{0, 10}\\ b_{0,01} + b_{0, 11} \\ b_{0,02} + b_{0,12} \end{bmatrix} W0= w0,00w0,01w0,02w0,10w0,11w0,12 ,     X=[x0x1],   b0= b0,00+b0,10b0,01+b0,11b0,02+b0,12 可以得到: W 0 ⋅ X + b 0 = [ w 0 , 00 w 0 , 10 w 0 , 01 w 0 , 11 w 0 , 02 w 0 , 12 ] ⋅ [ x 0 x 1 ] + [ b 0 , 0 b 0 , 1 b 0 , 2 ] = ( w 0 , 00 ⋅ x 0 + w 0 , 10 ⋅ x 1 + b 0 , 0 )      + ( w 0 , 01 ⋅ x 0 + w 0 , 11 ⋅ x 1 + b 0 , 1 )      + ( w 0 , 02 ⋅ x 0 + w 0 , 12 ⋅ x 1 + b 0 , 2 ) \begin{align*} \mathbf{ W_0 \cdot X + b_0} &= \begin{bmatrix} w_{0,00} & w_{0, 10} \\ w_{0,01} & w_{0, 11}\\ w_{0,02} & w_{0, 12} \end{bmatrix} \cdot \begin{bmatrix} x_0\\x_1\end{bmatrix} + \begin{bmatrix} b_{0,0} \\ b_{0,1} \\ b_{0,2} \end{bmatrix}\\ &= (w_{0,00}\cdot x_0 + w_{0,10}\cdot x_1 + b_{0,0}) \\ & \space \space \space \space + (w_{0,01}\cdot x_0 + w_{0,11}\cdot x_1 + b_{0,1}) \\ & \space \space \space \space + (w_{0,02}\cdot x_0 + w_{0,12}\cdot x_1 + b_{0,2}) \end{align*} W0X+b0= w0,00w0,01w0,02w0,10w0,11w0,12 [x0x1]+ b0,0b0,1b0,2 =(w0,00x0+w0,10x1+b0,0)    +(w0,01x0+w0,11x1+b0,1)    +(w0,02x0+w0,12x1+b0,2) 因为偏置项 b b b 是常数,所以这里我将两个常数直接合并了。

最后得出来的结果也是我们非常熟悉的形式。然后和之前的逻辑回归类似,将这个加权求和得到的结果,送到激活函数中,进行非线性的变换,得到我们所需的下一个隐藏层的输入项 A 0 \mathbf{A_0} A0

在这里插入图片描述
合并偏置常数项之后,我们的 MLP 结构图也可以画成上图的形式。

第二隐藏层的具体式子在这里就不列出来了。

2.7、简述如何训练一个模型:正向传播与反向传播

这个原文中也没做过多的展开,我这里也留着

第 3 节 · 卷积神经网络(CNN)

MLP 里每一层的每个元素,都要乘以一个独立参数的权重 W,再加上一个偏置 b,这样的神经网络层常被我们叫做「全连接层(Fully Connected Layer)或稠密层(Dence Layer)。但是这样有个显著问题:如果输入内容的局部重要信息只是发生轻微移动并没有丢失,在全连接层处理后,整个输出结果都会发生很大变化 —— 这不合理

即全连接层可能在处理输入时过于强调整体特征,而不足够关注输入中的局部结构信息。

所以为了让全连接层也能关注局部信息,是否可以设置一个滑动窗口来对局部输入进行处理。这就会 n-gram 设置 N 值的大小思路一样。这就引出了我们熟悉的 CNN 卷积神经网络,使用卷积核在原图上的滑动来提取图像的局部特征。

  1. CNN 与 MLP 有什么区别?
  2. CNN 的共享权重是什么意思?
  3. CNN 的局部连接是什么意思?

上面我画的示意图中,MLP 的输入数据是一维的,所以所有输入 MLP 进行全连接处理的数据都得先展平成一维的数据,而 CNN 的输入数据则是二维的,也就是可以。

其实某种意义上,可以将 MLP 理解成一个 1 × 1 1\times1 1×1 的卷积,但是和真正的 1 × 1 1\times1 1×1 卷积又有一些微秒的区别,在 CNN 中我们也常使用 1 × 1 1\times1 1×1 卷积来实现全连接结构,但是 1 × 1 1\times1 1×1 卷积的输入可以是二维的数据,而 MLP 还是只能输入一维的数据。

让我们修改一下上面的图,将输入数据变成我们熟悉的二维图像数据。

第 4 节 · 循环神经网络(RNN)

4.1、经典结构的 RNN(N vs N RNN)

也就是输入和输出的维度是一样的。
在这里插入图片描述

N vs.1 的 RNN

在这里插入图片描述

1 vs. N 的 RNN

在这里插入图片描述

4.4、LSTM(Long Short-Term Memory)长短时记忆网络

LSTM 因为在图像处理中基本用不上,所以我比较陌生。是一种特殊的 RNN 网络。

我们通过 RNN 的结构就可以看出来,后向传播的时候,连续的乘法结构很容易造成梯度消失或者梯度爆炸的问题。在 CNN 的时候,我们会通过 dropout 对某些神经元进行舍弃,来进行正则化,缓解梯度消失和爆炸的问题。而 LSTM 中呢,我们也会设计一个模块对过往的信息进行舍弃,来缓解 RNN 中出现的梯度消失或者爆炸问题。

LSTM将长短期记忆文本管理问题分为两个子问题:

  1. 从上下文中删除不再需要的信息(舍弃部分过往信息)
  2. 添加可能在后续决策中需要的信息(添加当前输入信息)

LSTM 引入了 “门(gate)” 的概念来控制信息的流向,可以分成 3 个门:

  • 遗忘门(forget gate):用于去除上文中所不需要的信息。
  • 输入门(input gate):将当前的信息输入到上下文中。
  • 输出门(output gate):输出当前的上下文信息。

其实 LSTM 的结构和公式都不难理解,但是我希望在这里一步步从 RNN 推导到 LSTM,就有很多细节不清楚了,这里暂时也不过多的纠结了。

4.5、双向 RNN 和双向 LSTM

在经典的循环神经网络中,状态的传输是从前往后单向的。然而,在有些问题中,当前时刻的输出不仅和之前的状态有关系,也和之后的状态相关。这时就需要双向RNN(BiRNN)来解决这类问题。例如预测一个语句中缺失的单词不仅需要根据前文来判断,也需要根据后面的内容,这时双向RNN就可以发挥它的作用。
在这里插入图片描述
原文中给了一张简单的 bi-RNN 示意图,但是我感觉有点容易误解的地方。它只是对前向和反向的隐藏层权重 W W W W ′ W' W 进行了区分,但是没有区分 V V V V ′ V' V U U U U ′ U' U 的权重。

一般计算的步骤是从前向后算一次,然后再从后向前计算一次。

原文虽然小标题中提到了双向 LSTM,但是实际没有具体的提到双向 LSTM 的,所以我这里也暂时略过。

4.6、堆叠循环神经网络(Stacked RNN)、堆叠长短时记忆网络(Stacked LSTM)

就是一层的输出作为下一层的输入,多层堆叠

4.7、N vs. M 的 RNN (Encoder-Decoder)

在这里插入图片描述
上图是一个抽象化的 Encoder-Decoder 模型示意图,相当于一个 N vs 1 和一个 1 vs M 的 RNN 网络的串联。一般我们也称之为 Seq2Seq 模型。

Encoder-Decoder 的结构有多种,但是大体的原理都是一样的。

仅管 Encoder-Decoder 模型可以实现序列到序列的任务,对于语音识别,翻译任务,问答任务的效果很不错,但是也存在一些问题:

  • 长序列信息丢失问题:Encoder 将输入编码为固定大小的向量的过程实际上是一个“信息有损的压缩过程”,如果信息量越大,那么这个转化向量的过程对信息的损失就越大。
  • 梯度消失或者爆炸:因为传统 Encoder-Decoder 是采用 RNN 作为编码器和解码器的,所以自然,RNN 存在的梯度消失和梯度爆炸问题也是不可避免的。
  • 并行效果差:因为每一时刻都依赖于前一时刻的输出。

第 5 节 · 为什么说 RNN 模型没有体现「注意力」?

其实从 4.7 就可以看出来,传统的 Encoder-Decoder 对于长序列的处理能力是很差的,因为不管序列多长,在经过编码器之后,都会压缩成固定尺寸的上下文向量(context vector),在这个压缩编码的过程中,很多长序列的信息就会丢失,导致后续解码部分也没办法获得全部的长序列信息。

举个简单的例子,如果长序列信息类似于山洞里的宝库,这时候一个盗贼进入了山洞,他只有一个箱子可用于搬运且只能搬运一次的话,可能是没办法将山洞内的财宝全部取走。想要实现利益最大化的话,自然是聚焦于高价值的宝物,也就是尽可能的用高价值的宝物填满箱子。

这就引出我们久有耳闻的 attention 机制。将注意力放到有价值的信息上面。

在传统的 Encoder-Decoder 模型里引入 attention 模块。

第 6 节 · 基于 Attention 机制的 Encoder-Decoder 模型

从Encoder-Decoder(Seq2Seq)理解Attention的本质 :这篇文章挺不错的。

在这里插入图片描述
从上面的 Encoder-Decoder 示意图,我们可以知道上下文向量(context vector)可以通过下式: C = f ( x 1 , x 2 , … , x n ) C = f(x_1, x_2, \dots ,x_n) C=f(x1,x2,,xn) 计算得到,而想要对于最后的输出结果 y i y_i yi,我们可以通过: y k = g ( C , y 1 , y 2 , … , y k − 1 ) y_k = g(C, y_1, y_2, \dots, y_{k-1}) yk=g(C,y1,y2,,yk1)但是我们也可以看出来,不管是计算 y 1 y_1 y1 y 2 y_2 y2 还是 y 3 y_3 y3,我们都是通过 C C C 来计算的,也就是说 Encoder 中任意单词对于 Decoder 的输出的影响都是一样的,不存在注意力机制的。

在这里插入图片描述
如果加入了 attention 模块,我们就可以看出来,对于不同的输出 y i y_i yi,我们有着不同的 C i C_i Ci,则对应的输出: y k = g ( C k , y 1 , y 2 , … , y k − 1 ) y_k = g(C_k, y_1, y_2, \dots, y_{k-1}) yk=g(Ck,y1,y2,,yk1)

但是问题来了,这个 C k C_k Ck 是怎么计算出来的?
在这里插入图片描述

也就是说,对于不同的 C k C_k Ck,attention 模块会给出不同的注意力向量 a k \mathbf{a^k} ak 来进行计算,上面的式子中, n n n 为输入序列的长度,而 k k k 为输出序列的长度。

a i k a_{ik} aik 就是 attention 注意力权重,表示第 i i i 个输入在第 k k k 个输出上分配的注意力。数值越高表示第 j j j 个输出受到第 i i i 个输入的影响越大。

如果将他们看成一个矩阵运算的话,则可以写成下面的形式: [ h 1 h 2 ⋯ h i h 1 h 2 ⋯ h i ⋮ ⋮ ⋱ ⋮ h 1 h 2 ⋯ h i ] ⋅ [ a 1 , 1 a 1 , 2 ⋯ a 1 , k a 2 , 1 a 1 , 2 ⋯ a 2 , k ⋮ ⋮ ⋱ ⋮ a i , 1 a i , 2 ⋯ a i , k ] = [ C 1 , C 2 , ⋯   , C k ] \begin{bmatrix} h_1 & h_2 & \cdots & h_i \\ h_1 & h_2 & \cdots & h_i \\ \vdots & \vdots & \ddots & \vdots \\ h_1 & h_2 & \cdots & h_i \end{bmatrix} \cdot \begin{bmatrix} a_{1,1} & a_{1,2} & \cdots & a_{1,k}\\ a_{2,1} & a_{1,2} & \cdots & a_{2,k}\\ \vdots & \vdots & \ddots & \vdots\\ a_{i,1} & a_{i,2} & \cdots & a_{i,k} \end{bmatrix} = [C_1, C_2, \cdots , C_k] h1h1h1h2h2h2hihihi a1,1a2,1ai,1a1,2a1,2ai,2a1,ka2,kai,k =[C1,C2,,Ck]其中等式左边第一个向量的维度为 k × i k \times i k×i,第二个向量 attention 权重矩阵的维度为 i × k i \times k i×k,最后获得的上下文权重向量的维度为 1 × k 1 \times k 1×k

但是问题又来了, a i k a_{ik} aik 是怎么计算获得的?
在 attention 模块中,注意力评价值 e i k e_{ik} eik 是由两部分组成的:

  • i i i 个输入(当前时刻输入)的隐藏层输出 h i h_i hi
  • k − 1 k-1 k1 个输出(前一时刻输出)的隐藏层输出 s k − 1 s_{k-1} sk1

也就是说这个注意力的值其实既和当前输入有关,也和前一时刻的输出相关。

公式可以写成: e i k = α ( h i , s k − 1 ) e_{ik} = \alpha(h_i, s_{k-1}) eik=α(hi,sk1)我们这里的 attention 机制是 soft attention,所以和 softmax 类似,我们要对上面这个注意力评价值进行加权平均: a i k = exp ⁡ ( e i k ) ∑ i = 1 n exp ⁡ ( e i k ) a_{ik} = \frac{\exp(e_{ik})}{\sum_{i=1}^n \exp(e_{ik})} aik=i=1nexp(eik)exp(eik)其中:

  • ∑ i = 1 n exp ⁡ ( e i k ) \sum_{i=1}^n \exp(e_{ik}) i=1nexp(eik):计算了每个时刻输入和前一时刻的输出的评价值之和
  • exp ⁡ ( e i k ) \exp(e_{ik}) exp(eik):表示当前时刻输入和前一时刻输出的评价值

感觉有种当前时刻输入,在总输入中的贡献占比的感觉。

为什么要使用 exp 来计算 attention 权重呢?

最后我们列出计算输出 y i y_i yi 的完整流程:

  1. 已知所有的输入 h i h_i hi s k − 1 s_{k-1} sk1,计算 e i k = α ( h i , s k − 1 ) e_{ik} = \alpha(h_i, s_{k-1}) eik=α(hi,sk1)
  2. 已知 e i k e_{ik} eik,计算 a i k = exp ⁡ ( e i k ) / ∑ i = 1 n exp ⁡ ( e i k ) a_{ik} = \exp(e_{ik}) / \sum_{i=1}^n \exp(e_{ik}) aik=exp(eik)/i=1nexp(eik)
  3. 已知所有的输入 h i h_i hi a i k a_{ik} aik, 计算 C k = ∑ i = 1 n a i k h i C_k = \sum^n_{i=1} a_{ik}h_i Ck=i=1naikhi
  4. 已知 s k − 1 s_{k-1} sk1 y k − 1 y_{k-1} yk1 C k C_{k} Ck,计算当前时刻输出的隐藏状态 s k = f ( s k − 1 , y k − 1 , C k ) s_{k} = f(s_{k-1}, y_{k-1}, C_{k}) sk=f(sk1,yk1,Ck)
  5. 已知 s k s_k sk y k − 1 y_{k-1} yk1 C k C_{k} Ck,计算当前时刻输出 y k = g ( y k − 1 , s k , C k ) y_k = g(y_{k-1}, s_k, C_k) yk=g(yk1,sk,Ck)
  6. 有了 s k s_k sk y k y_k yk,又可以继续下一时刻的循环了

第 7 节 · 自注意力机制(Self-Attention)

下面的章节就正式的进入到 transformer 的邻域了。

参考文章:The Illustrated Transformer
在这里插入图片描述这是我参考文章内的图片自己做的一个示意图。通过这个示意图,我来大概地讲解一下 self attention 的计算流程:

  1. 首先一句话中的单词经过 embedding 之后,每个单词都会生成对应的输入向量 x i \mathbf{x_i} xi
  2. 然后 X \mathbf{X} X 通过和对应权重 W Q \mathbf{W^Q} WQ W K \mathbf{W^K} WK W V \mathbf{W^V} WV 进行矩阵乘法,获得 Q \mathbf{Q} Q K \mathbf{K} K V \mathbf{V} V
  3. 通过计算 A = Softmax ( Q ⋅ K T / d k ) \mathbf{A} = \text{Softmax}(\mathbf{Q \cdot K^T /\sqrt{d_k}}) A=Softmax(QKT/dk ) 来获得自注意力权重矩阵。
  4. 最后将自注意力权重矩阵 A \mathbf{A} A 与 value 矩阵 V \mathbf{V} V 进行矩阵乘法,就可以获得 Z \mathbf{Z} Z

为什么要除以 d k \sqrt{d_k} dk
d k d_k dk 表示的是 key 矩阵的中 k i k_i ki 的维度,除以 d k \sqrt{d_k} dk 是为了:

  1. 防止 Q ⋅ K T \mathbf{Q \cdot K^T} QKT 的值过大,导致 softmax 计算的时候出现上溢出(overflow)。
  2. 使得 Q ⋅ K T \mathbf{Q \cdot K^T} QKT 的结果满足期望为 0,方差为 1 的分布。

为什么要计算 Q ⋅ K T \mathbf{Q \cdot K^T} QKT
计算 Q ⋅ K T \mathbf{Q \cdot K^T} QKT 时,从几何的角度来说,相当于对内部的向量 q \mathbf{q} q k \mathbf{k} k 进行点积操作,来计算两个向量的相似度。
q ⋅ k = ∥ q ∥ ∥ k ∥ ⋅ cos ⁡ θ \mathbf{q \cdot k} =\left \| \mathbf{q} \right \| \left \| \mathbf{k} \right \| \cdot \cos \theta qk=qkcosθ

  • 当两个向量垂直的时候, θ = 9 0 ∘ \theta = 90^{\circ} θ=90,就是点积为 0,表示这两个向量没有相似的地方。
  • 当两个向量平行的时候,两个向量的点积即为 ∥ q ∥ ∥ k ∥ \left \| \mathbf{q} \right \| \left \| \mathbf{k} \right \| qk
  • 当两个向量存在夹角 θ \theta θ,两个向量的点积就如上面公式一样。点积就是 q \mathbf{q} q k \mathbf{k} k 上投影的模。

这里 self attention 的 Z \mathbf{Z} Z 和 soft attention 中的 C \mathbf{C} C 是等效的。

第 8 节 · 多头注意力(Multi-Head attention)

多头注意力,其实相当于多次求 self attention,上面的 self attention 的产物是一个 Z \mathbf{Z} Z 矩阵,在多头注意力里,我们通过多组的 W Q \mathbf{W^Q} WQ W K \mathbf{W^K} WK W V \mathbf{W^V} WV,来获得多组的 Z \mathbf{Z} Z

在这里插入图片描述我们将上面 self attention 中计算 Z \mathbf{Z} Z 的过程简化一下,直接得到对应的 Z 0 , Z 1 , ⋯   , Z n \mathbf{Z_0, Z_1, \cdots, Z_n} Z0,Z1,,Zn。然后将其组合起来,与一个新设置的权重矩阵 W o \mathbf{W^o} Wo 相乘,获得最终的 Z \mathbf{Z} Z

多头注意力中的每个头都能关注不同的信息。

第 9 节 · 退化现象、残差网络与 Short-Cut

ResNet 残差网络在 CV 中也经常用到,所及就不详细解说了。

第 10 节 · Transformer 的位置编码(Positional Embedding)

在计算 attention 的时候,我们只是关注词与词之间的相似关系,但是却忽略了位置关系

在 Transformer 中,为了解决这个问题,新增了位置编码(Positional Encoding)。

在这里插入图片描述
大致的流程如上图所示,就是在 embedding 和计算 Q \mathbf{Q} Q K \mathbf{K} K V \mathbf{V} V 之间加入一个位置编码的流程,来将每个词或者说 token 的位置信息加入到输入向量 x \mathbf{x} x 中。

矩阵 P E \mathbf{PE} PE 中的向量 p e \mathbf{pe} pe 内的元素具体是怎么计算的呢? p e t , i = { sin ⁡ ( 1 1000 0 2 i d x ⋅ t )  if  i = 2 k cos ⁡ ( 1 1000 0 2 i d x ⋅ t )  if  i = 2 k + 1         for   k = 0 , 1 , 2 , ⋯   , d x 2 − 1 pe_{t,i} = \begin{cases} \sin(\frac{1}{10000^{\frac{2i}{d_x} }} \cdot t) & \text{ if } i = 2k \\ \cos(\frac{1}{10000^{\frac{2i}{d_x} }} \cdot t) & \text{ if } i= 2k + 1 \end{cases} \space \space \space \space \space \space \space \space \text{for} \space \space k = 0, 1, 2, \cdots, \frac{d_x}{2}-1 pet,i= sin(10000dx2i1t)cos(10000dx2i1t) if i=2k if i=2k+1        for  k=0,1,2,,2dx1其中:

  • d x d_x dx 是向量 x \mathbf{x} x 的维度。
  • k k k 0 0 0 d x / 2 − 1 d_x/2-1 dx/21 的整数

如果 i i i 是偶数的话,就是计算 sin ⁡ \sin sin,如果 i i i 是奇数的话就是计算 cos ⁡ \cos cos。一个完整的 p e \mathbf{pe} pe 向量可以写成下面形式: p e t = [ p e t , 0 , p e t , 1 , ⋯   , p e t , d x ] \mathbf{pe}_t = [pe_{t,0}, pe_{t,1}, \cdots, pe_{t,d_x}] pet=[pet,0,pet,1,,pet,dx]而一个完整的位置编码矩阵可以写成下面形式: P E = [ p e 0 p e 1 ⋮ p e t ] = [ p e 0 , 0 p e 0 , 1 ⋯ p e 0 , d x p e 1 , 0 p e 1 , 1 ⋯ p e 1 , d x ⋮ ⋮ ⋱ ⋮ p e t , 0 p e t , 1 ⋯ p e t , d x ] \mathbf{PE}=\begin{bmatrix} \mathbf{pe}_0 \\ \mathbf{pe}_1 \\ \vdots \\ \mathbf{pe}_t \end{bmatrix}=\begin{bmatrix} pe_{0,0} & pe_{0,1} & \cdots & pe_{0,d_x}\\ pe_{1,0} & pe_{1,1} & \cdots & pe_{1,d_x}\\ \vdots & \vdots & \ddots & \vdots\\ pe_{t,0} & pe_{t,1} & \cdots & pe_{t,d_x} \end{bmatrix} PE= pe0pe1pet = pe0,0pe1,0pet,0pe0,1pe1,1pet,1pe0,dxpe1,dxpet,dx

上面介绍的这种,是绝对编码中的三角式位置编码(Sinusoidal Positional Encoding),除此之外还有相对位置编码。

10.2、绝对位置编码

除了上面提到的三角式位置编码,还有下面几种绝对位置编码:

  • 习得式位置编码:将位置编码矩阵作为可学习的超参数来进行训练,缺点是外推性比较差,推理时输入文本超出训练的最大长度,则无法处理。
  • 循环式位置编码:将向量 x \mathbf{x} x 一个一个送入 RNN 中,RNN 中的模块是 Transformer,因为 RNN 暗含位置信息,所以就不需要进行额外的编码了,但是缺点就是 RNN 的并行性比较差。
  • 相乘式位置编码:使用哈达玛德积(Hadamard product),也就是逐元素相乘来替代相加,也就是 x ⊙ p e x_ \odot pe xpe 代替 x + p e x + pe x+pe

10.3、相对位置编码与其他位置编码

感觉暂时也不需要详细展开了,后续有时间的话再写吧。

第 11 节 · Transformer 的编码器 Encoder 和解码器 Decoder

11.1、Encoder 和 Decoder 的图示结构

在这里插入图片描述
沿用之前的参数符号,上图是 Transformer 的 Encoder 示意图,可以看出来:

  • 输入是经过位置编码的 X p e \mathbf{X}_{pe} Xpe
  • 经过第一个多头注意力模块(其中 MHA \text{MHA} MHA 是 Multi-Head Attention 的缩写)之后,输出的 Z \mathbf{Z} Z
  • 然后经过 Add & Norm 层,将 X p e \mathbf{X}_{pe} Xpe Z \mathbf{Z} Z 相加之后进行层归一化(LayerNorm),输出 X out 1 \mathbf{X}_{\text{out}1} Xout1
  • 然后输入 Feed Forward 层,这是由两个全连接层组成的,第一个全连接层的激活函数是 ReLU,第二个全连接层没有激活函数。得到我们的 F \mathbf{F} F
  • 最后再经过一次 Add & Norm 层,进行层归一化操作,得到 Encoder 的最终输出 X out 2 \mathbf{X}_{\text{out}2} Xout2

其中可以看到 Encoder 中存在两条 short-cut。

在这里插入图片描述

第一个 masked multi-head attention

接下来看看 Decoder 的结构,除了一个新的 masked multi-head attention 模块,其他模块都和 Encoder 的类似,所以具体的计算公式就不标注出来了。

我们说一下 masked multi-head attention 模块,这个 masked 的对象是什么,具体有什么用?

我们这里以单个注意力模块,也就是 self attention 来举例子,其实 masked 的操作主要是加在注意力权值矩阵进行 softmax 操作之前。
在这里插入图片描述
这是我从 self attention 示意图中抽取的注意力权值矩阵计算部分,左边是普通的计算注意力权值矩阵,在求解 A ′ \mathbf{A'} A 之后,直接对其进行 softmax 操作就行了。

而右边则是包含 mask 操作的,在进行 softmax 操作之前,先进行一个 mask 操作,防止后续标签进行对前面的预测进行影响,因为我们知道 Transformer 中 Decoder 也会输入一次输入序列,但是和 Encoder 部分不同的是,会对输入数列进行一个右移(shifted right)的操作。类似 Encoder 如果输入 [i am a student],那么 Decoder 部分,则会输入 [ I am a student],多了一个起始符,这个起始符就是用于预测 “I” 的。

第二个 multi-head attention

这个注意力模块也可以叫做 cross attention 模块,或者是 encoder-decoder attention 模块

第二个多头注意力模块,其实我们注意到,有两条线是来自于 Encoder,我们知道在计算 self attention 的时候,首先我们要计算 Q \mathbf{Q} Q K \mathbf{K} K V \mathbf{V} V 三个矩阵,一般来说,这三个矩阵都是一个输入和对应的权重矩阵计算获得的。

但是在 Decoder 的第二个多头注意力模块中,其中 K \mathbf{K} K V \mathbf{V} V 是由 Encoder 的输出 X out 2 \mathbf{X}_{\text{out}2} Xout2 计算获得的,而 Q \mathbf{Q} Q 是由 Decoder 的第一个 masked multi-head attention 模块的输出计算获得的。

11.2、Decoder 的第一个输出结果

我理解为 Transformer 是由很多个 Decoder block 组成的,但是第一个 Decoder block 和后续的 Decoder block 有一点细微的差别

在这里插入图片描述
假设 Transformer 一共有 N N N 个 Decoder block,第一个 Decoder block 是不含 masked multi-head attention 模块的。后续的 N − 1 N-1 N1 个 Decoder block 才是我们上面分析的正常的 Decoder block,这是为什么?

为什么 transformer的第一个 decoder blcok 不需要经过 masked multi-head attention 模块?

在 Transformer 模型中,第一个 Decoder block 的出结果是基于 Encoder 的输出和 Decoder 的初始输入(通常是起始符号,如)生成的。这个过程不需要经过 Masked Multi-Head Attention Layer,因为即便是经过了多头注意力模块,

11.3、Decoder 后续的所有输出

没看懂

11.4、Decoder 之后的 Linear 和 Softmax

这个部分其实和 cv 中的分类最后是一样的,只是 transformer 中的类别是词汇表。

第 12 节 · Transformer 模型整体

在这里插入图片描述

  • 首先输入数据生成词的嵌入式向量表示(Embedding),生成位置编码(Positional Encoding,简称 PE)。进入
  • Encoders 部分。先进入多头注意力层(Multi-Head Attention),是自注意力处理,然后进入全连接层(又叫前馈神经网络层),每层都有 ResNet、Add & Norm。
  • 每一个Encoder 的输入,都来自前一个 Encoder 的输出,但是第一个 Encoder 的输入就是 Embedding + PE。
  • 进入 Decoders 部分。先进入第一个多头注意力层(是 Masked 自注意力层),再进入第二个多头注意力层(是 Encoder-Decoder 注意力层),每层都有 ResNet、Add & Norm。
  • 每一个 Decoder 都有两部分输入。
  • Decoder 的第一层(Maksed 多头自注意力层)的输入,都来自前一个 Decoder 的输出,但是第一个 Decoder 是不经过第一层的(因为经过算出来也是 0)
  • Decoder 的第二层(Encoder-Decoder 注意力层)的输入,Q 都来自该 Decoder 的第一层,且每个 Decoder 的这一层的 K、V 都是一样的,均来自最后一个 Encoder。* 最后经过 Linear、Softmax 归一化。

后面的部分基本都是实际训练部分了。

参考文章:举个例子讲下transformer的输入输出细节及其他

但是感觉 transformer decoder 的输入输出维度还是不是很清晰

  • 输入输出的维度是多少?
  • 上一个 decoder 的输出是怎么整合到下一个 decoder 的输入的
  • 训练的时候输入的是 ground truth,但是推理的时候 decoder 的输入维度是多少?
  • transformer 的并行训练体现在哪里?
  • 25
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值