第三章 深度学习基础
神经网络没有训练好甚至根本没训练起来,也即loss没达到预期,也即收敛在了局部极限值或鞍点(非全局最优但是梯度接近零,训练无法继续)。
各种方式能帮助优化。
注:很多优化方法都是从凸优化里得到的启发
局部极小值与鞍点
临界点及其种类
梯度为零的点统称临界点(critical point),局部极小值(local minimum)与鞍点(saddle point)都是临界点。
判断临界值种类的方法
网络损失函数很复杂,可用泰勒级数近似看损失函数的形状来判断是局部极小值还是鞍点。
L
(
θ
)
≈
L
(
θ
′
)
+
(
θ
−
θ
′
)
T
g
+
1
2
(
θ
−
θ
′
)
T
H
(
θ
−
θ
′
)
L(\theta) \approx L(\theta') + (\theta - \theta')^{T} g + \frac{1}{2}(\theta - \theta')^{T} H(\theta - \theta')
L(θ)≈L(θ′)+(θ−θ′)Tg+21(θ−θ′)TH(θ−θ′)
在临界点展开时梯度为零(级数展开第二项的
g
g
g 就为零),所以函数形状就看第三项 —— 也即误差表面
1
2
(
θ
−
θ
′
)
T
H
(
θ
−
θ
′
)
\frac{1}{2}(\theta - \theta')^{T} H(\theta - \theta')
21(θ−θ′)TH(θ−θ′),简写成
v
T
H
v
v^{T}Hv
vTHv
L
(
θ
)
≈
L
(
θ
′
)
+
1
2
(
θ
−
θ
′
)
T
H
(
θ
−
θ
′
)
L(\theta) \approx L(\theta') + \frac{1}{2}(\theta - \theta')^{T} H(\theta - \theta')
L(θ)≈L(θ′)+21(θ−θ′)TH(θ−θ′)
根据海森矩阵
H
H
H 来判断,若
H
H
H 正定(特征值全正)那么任何
v
v
v 都有
v
T
H
v
>
0
v^{T}Hv>0
vTHv>0,即为局部极小值;若
H
H
H 负定(特征值全负),则是局部极大值;若特征值有正有负,就是鞍点。(再次注意,这都是在梯度为零的前提下进行的判断!)
如果处于鞍点,通过海森矩阵还能得到参数更新的方向。
注:当然这只是个理论方法,实际上海森矩阵这个二次微分的矩阵运算量极大,还得算特征值/特征向量,实际上会使用一些其他逃离鞍点的方法
逃离鞍点的方法
实际神经网络参数极多,也即误差表面维度极高。(书上的三维损失图其实只是示例,实际上的损失形状图根本无法直接想象 —— 应该是用其他方法探究,反正我们只需要能有办法找到个足够优化的位置即可)
鞍点不可怕,仍有优化的路径,局部极小值可能比较麻烦,“四周”都是损失更高的。幸运的是实际中确实遇到鞍点较多,局部极小值较少(这个研究结果不错,相当于给了训练神经网络一种信心 —— 会不会其实大佬们设计网络结构就是有意识的设计更容易优化的结构?或许难以训练的网络根本没法流行起来?比如Transformers作者们其实是探索了很多结构然后发现这种结构容易训练好? —— 有待进一步了解这些网络结构是怎么设计出来的)。
有研究探索临界点海森矩阵中正特征值占比(正特征值越多越像局部极小值),也即
正特征值数量
/
总特征值数量
正特征值数量/总特征值数量
正特征值数量/总特征值数量, 经验发现这个比例最大也在0.5~0.6的范围。下图横轴就是这个最小值比例。
批量和动量
介绍两种优化方法,分批和动量。
批量大小对梯度下降法的影响
实际上没法一次把所有数据总的算损失来迭代(硬件条件不够),因此会分批(batch),并且还会随机打乱(shuffle)。完整遍历一次所有数据的过程就是一个回合(epoch)。
两种常见优化算法:批量梯度下降(Batch Gradient Descent)和随机梯度下降(Stochastic Gradient Descent),前者是整个epoch数据都遍历完再更新下参数,后者是一小批样本(甚至一个样本)计算后就更新下参数。
前者更新更稳定准确,后者更新更频繁,引入了随机噪声(有趣的是某些非凸优化问题中随机噪声可能让训练更容易逃离局部最小值!)
额外的感叹:硬件的进步,17年980,20年V100,同一个batch,前者要跑几分钟,后者只要十秒左右。
批量大小对训练结果的研究:大批量让训练能很快(比如一分钟训练ImageNet等),但是从最后的优化结果和泛化性上面,似乎是相对小批量的效果更好。
研究1(优化结果方面):在MNIST和CIFAR-10上,发现总体上批量过大,训练和验证的误差准确率都会较差(注意不是过拟合,过拟合是训练准确率高但验证准确率差)
研究2(泛化性方面)1:该研究在不同数据集上训练六种不同网络,用小批量(一批256)和大批量(一批是整个数据集的0.1)训练,发现即使训练准确率差不多,测试准确率小批量的更好,也即小批量训练结果泛化性更好,
论文给出的一种解释:局部最小值也有好坏之分。好的局部最小值形状像“平原”,能同时较好的代表训练数据和测试数据的分布;坏的局部最小值像“峡谷”,只能代表一小部分数据的分布情况。
我的理解:“峡谷”状就是对一小撮数据拟合极好,对其他数据拟合一般;“平原”状就是对所有数据都拟合较好(没有极好)。前者可能计算出整体loss会更小(因为那小撮数据上loss极小!),但即使这样,这种训练结果不能广泛的体现数据集的真实分布,也就导致泛化效果不好。
总而言之小批量带来的这种随机噪声有奇效!
不过已有很多论文23456研究在较大的批量大小情况下仍然保证较好的训练结果,可以进一步查阅下。(这些论文还是比较重要的,毕竟涉及到训练结果好坏,要了解下这些训练技巧是否已经直接融入在PyTorch这样的框架中了?)
动量法
从数学公式上看,仿佛就是给训练加上了一定“惯性”的感觉,让训练更容易跳出一些不太合适的局部最小点。(这只是定性的理解,感觉真正做起来得各种调参!)可以认为比较合适的最小点能够消耗到之前赋予的“动量”。
具体计算上,动量就是之前所有计算的梯度的加权和。
不过实际上各种学习框架把学习算法都写好了,可能对于绝大多数人来说,对算法有个定性理解就好,太抠细节可能也不一定有用(毕竟感觉深度学习里目前“玄学”的东西太多,很多东西就是通过结果反推某种操作可能不错,不一定有真正翔实的理论指引)
学习率相关
(本章节对应书上的3.3 3.4 3.5节)
对于整个梯度下降优化过程,学习率相关的调整可能会更重要!
比如一个训练过程中,一般看着loss到不再下降,一般人以为训练就快结束了。实际上如果画出梯度的变化,可能梯度仍然在震荡 —— 损失不再下降不意味着到达局部最小值这类点,也可能是来回震荡。
示意图如下,黑色曲线是损失函数形状,梯度震荡导致实际上还能优化,但是梯度的数值太大使得训练无法继续 —— 要通过学习率来控制梯度数值!
用一个贯穿始终的例子来看各种优化,该例子是
w
w
w 和
b
b
b 两个参数的凸优化。
马上就能发现,即使在一个单纯凸函数上做优化,学习率调整也可能带来很多麻烦 —— 更何况深度学习里的优化大多是非凸优化问题!
(最近才发现原来很多深度学习里的优化方法都是凸优化里来的,比如正则化 动量法 各种自适应学习率方法等,从凸优化获得启发,应用到深度学习这种非凸优化问题)
自适应学习率
梯度下降的迭代公式如下:
θ
t
+
1
i
←
θ
t
i
−
η
g
t
i
\theta_{t+1}^{i} \leftarrow \theta_{t}^{i} - \eta g_{t}^{i}
θt+1i←θti−ηgti
其中
i
i
i 表示第
i
i
i 个参数,
t
t
t 表示第
t
t
t 次迭代,
g
g
g 就是梯度(gradient),
η
\eta
η 就是学习率。
我们希望学习率有个自适应的变化(而不是人盯着训练的过程,发现训练“不对劲”手动调整下学习率再试),意味着公式如下:
θ
t
+
1
i
←
θ
t
i
−
η
σ
t
i
g
t
i
\theta_{t+1}^{i} \leftarrow \theta_{t}^{i} - \frac{\eta}{\sigma_{t}^{i}} g_{t}^{i}
θt+1i←θti−σtiηgti
这个
σ
\sigma
σ 就是不断修改学习率以达到自适应的参数。大概的目的就是在梯度小(平坦)的时候学习率大一些,在梯度大的地方(陡峭)学习率小一些。接下来介绍下的各种方法就是搞个不同的
σ
\sigma
σ。
(但是最后接近局部最小值的时候,学习率不是要小一些吗 —— 这个应该是通过learning rate decay实现)
AdaGrad
最简单的一个想法就是让 σ \sigma σ 是所有历史梯度的均方根(root mean square):
σ
t
+
1
i
=
1
t
+
1
∑
i
=
0
t
(
g
t
i
)
2
\sigma_{t+1}^{i} = \sqrt{\frac{1}{t+1} \sum_{i=0}^{t} \left(g_{t}^{i}\right)^{2}}
σt+1i=t+11i=0∑t(gti)2
公式中把每个历史梯度都看作有同等重要性。
RMSProp
这个方法没有公开论文,但Hinton老师在一门课上讲到过。
σ
t
i
=
α
(
σ
t
−
1
i
)
2
+
(
1
−
α
)
(
g
t
i
)
2
\sigma_{t}^{i} = \sqrt{\alpha \left(\sigma_{t-1}^{i}\right)^{2} + (1-\alpha) \left(g_{t}^{i}\right)^{2}}
σti=α(σt−1i)2+(1−α)(gti)2
其中
α
\alpha
α 是一个0~1之间的超参数。公式中突出了当前梯度的重要性,历史的梯度体现在
σ
t
−
1
i
\sigma_{t-1}^{i}
σt−1i 中。
Adam
现在最常用的,可以认为是RMSProp+Momentum。
算法看着挺复杂,不过PyTorch已写好,有些需要认为决定的超参数,不过往往PyTorch预设的就足够好了。
李老师的例子挺不错,能感受到Adam的自动调整能力极强,可能一开始某些参数迭代要快些,之后另一阶段另一些参数要迭代快些。接近真正合适位置时可能会有震荡,要消耗掉RMSProp累积的梯度加权和。(这个例子很直观,好好感受!对使用者来说这种定性理解更重要!)
学习率随时间变化
lr decay
Adam加上learning rate decay就能让之前例子更平滑。
不过这个例子毕竟不是真实深度学习训练的例子
lr warm up
神奇的warm up,ResNet BERT Transformer都使用了warm up
一种解释:
σ
\sigma
σ 一开始是不精准的,所以先用较小的学习率收集一些关于误差表面的情况,得到比较精准的
σ
\sigma
σ 统计值之后再让学习率慢慢爬升 —— 更进一步信息参考RAdam。
(深度学习论文中这种细节可真重要,如果没注意论文中写到的warm up这种小细节,自己训练复现模型就无法达到原有效果了 —— 这些tricks太依靠经验了!—— 而且自己看ResNet论文或许好多遍都不会注意到这个细节,有李老师带路太重要了)
总结
三个优化都加在一起,注意动量
m
m
m 和
σ
\sigma
σ 不会抵消!因为动量是纯粹历史gradient的加权和,是考虑gradient方向的(不同方向的会抵消);而
σ
\sigma
σ是历史gradients数值的平方和,不考虑方向只考虑数值。
分类任务损失函数
如果把分类任务损失函数弄成像回归一样,比如类别0输出数值0,类别1输出数值1,以此类推。这样不妥,因为数值间有大小相似关系,模型很可能认为类别0/类别1比起类别0/类别2更相近!但实际上分类任务每个类别应该是独立平等的,损失函数要体现这一点。(有些例外,比如分类结果是小学六个年级,那么分类结果之间还真有年龄远近关系!)
独立的类别,往往用one-hot编码来表示;进一步多分类使用softmax,让输出各类结果可以认为是各类的概率,总和为1。
为了方便叙述统一下符号,网络直接输出
y
i
^
\hat{y_{i}}
yi^ (数值范围不限),经softmax后输出为
y
i
′
y_{i}^{\prime}
yi′ (数值范围0~1),真实的one-hot标签为
y
i
y_{i}
yi (要么0,要么1)
分类任务的损失函数适合用cross-entropy:
e
=
−
∑
i
y
i
ln
y
i
′
e = -\sum_{i} y_{i} \ln y_{i}^{\prime}
e=−i∑yilnyi′
y
i
y_{i}
yi 是真实标签,
y
i
′
y_{i}^{\prime}
yi′ 是模型softmax后的输出(要注意,模型的直接输出数值范围是不限的,但是经过softmax后数值范围都在0~1之间
在分类问题上,交叉熵比回归问题的均方差要好的原因探究:通过画损失函数随着
y
1
^
\hat{y_{1}}
y1^ 和
y
2
^
\hat{y_{2}}
y2^ 变化的图,假设标签是
y
1
=
1
,
y
2
=
0
y_{1}=1, y_{2}=0
y1=1,y2=0。画出来图像如下。
注意,只要
y
2
^
<
y
1
^
\hat{y_{2}} < \hat{y_{1}}
y2^<y1^,分类结果其实就是正确的,所以损失函数图右下方loss较小(蓝色小,红色大)。那么优化的目的就是要让
y
1
^
,
y
2
^
\hat{y_{1}}, \hat{y_{2}}
y1^,y2^ 组合尽量往右下角走,而图中均方误差在的左上方梯度变化都很缓,但是使用交叉熵左上方的梯度变化都很大,所以分类任务用交叉熵误差更容易进行梯度下降优化。
实际上,可以计算出通过softmax+交叉熵的反向传播梯度如下,
L
L
L 是损失函数:
∂
L
∂
y
i
^
=
y
i
′
−
y
i
\frac{\partial L}{\partial \hat{y_{i}}} = y_{i}^{\prime} - y_{i}
∂yi^∂L=yi′−yi
可以认为梯度的传播会很稳定,只要网络输出和独热标签之间有差距,就会有稳定的梯度。
Batch Normalization批量归一化
batch normalization有种把误差平面变得更“平整”更容易训练的感觉 —— 原论文中对比可以看到确实加上batch normalization会加速不少(即使是用sigmoid也是)
如果输入数据不同维度的数值范围相差很大,就导致误差平面相对不同参数斜率非常不同 —— 通过feature normalization将不同维度输入特征的数值范围控制到一样
(始终记住是数据在batch_size的维度进行归一化,比如输入是
B
∗
H
∗
W
∗
C
B*H*W*C
B∗H∗W∗C,规划化的均值和方差是
H
∗
W
∗
C
H*W*C
H∗W∗C)
z ~ i = z i − μ σ \tilde{z}^{i} = \frac{z^{i}-\mu}{\sigma} z~i=σzi−μ
这样做的话均值一定是0,标准差一定是1,但实际上有些数据可能会均值不为0,为了反映这种情况,做以下修正,其中 γ \gamma γ 和 β \beta β 是作为网络参数学习的(初值可以设为 γ \gamma γ为1, β \beta β为0)。
z ^ i = γ ⊙ z ~ i + β \hat{z}^{i} = \gamma \odot \tilde{z}^{i} + \beta z^i=γ⊙z~i+β
注意,是对一个batch数据进行归一化(所以叫batch norm) —— 奇怪,为啥不直接对整个数据集算出个 μ \mu μ 和 σ \sigma σ (据说是引入一定随机性;还有测试时输入数据分布可能和训练集不一样,用固定均值和方差就无法反映这种变化)
不光输入特征要进行归一化,之后跟weight矩阵相乘前也要归一化
在激活函数前还是后做归一化差别不大(好理解,激活函数不会把数据范围弄得很乱)
但Sigmoid的话推荐在激活函数前归一化,因为sigmoid函数在0附近梯度更大
注意计算这个均值和标准差的话每批数据不能太少,每批一个就不行
但是测试的时候根本就不再有batch的概念,甚至可能有时候一批只有一个 —— pytorch的方法是直接在训练中就计算出一个关于 μ \mu μ 和 σ \sigma σ 的moving average,测试时直接用固定的 μ ˉ \bar{\mu} μˉ 和 σ ˉ \bar{\sigma} σˉ
进一步:batch normalization用于CNN(见原论文)
进一步:batch normalization有效的理论原因
HW3 跑通CNN
理解下整个代码的过程
1 下载数据和导入各种库
2 设置随机种子,保证每次都能同样复现结果(不然结果很可能有差别,因为训练过程随机性较多,数据顺序不一样可能就影响训练最终结果),除了numpy torch和cuda,还有cudnn的两个属性(卷积运算的确定性?之后再了解下)
3 图像预处理,目前训练集和测试集都一样是resize成128*128然后转成Tensor
4 数据集定义,PyTorch自定义数据集类,继承自Dataset并实现len/getitem两个魔术方法即可,getitem中传入一个索引idx,返回第idx张图片和标签,图片就直接用Image.open打开并预处理后返回,训练集标签蕴含在图片标题上,比如0_125.jpg,表示类别是0,所以通过一个字符串解析函数把图片路径最开头的数字提取出来即可
5 模型定义,模型有5个卷积块(每块都是Conv2d BatchNorm然后ReLU再MaxPool,只是输入输出通道不同,其他参数一致),最后两层全连接网络,输出11个分类
6 定义损失函数,优化器,然后一轮轮训练
训练中GPU占用如下,服务器上有其他人一起用所有还有其他任务,本模型占显存3088MB(之后可以尝试理论计算下,为什么会是这么大)
训练日志如下,可以看到第五轮中获得了最好结果
深度学习究竟好在哪里
通过多层网络,能够逼近任何函数(前面讲过 逼近分段线性函数 => 逼近任何曲线)
理论上一个hidden layer的网络,足够宽也能逼近任何函数 —— 为什么要多层叠加,而不是要一个很宽的? —— 实验7表明同样参数量下,多层效果更好!多层结构反而更高效(可以理解成多层网络是从简单feature逐步叠加到复杂feature,相当于前面学习到的简单features都是被后面所有层共享的! - 有种模块化的感觉)(所以deep networks反而是相同参数 相同数据量情况下能最有效学习的模型)
或者可以这么理解,同样参数,训练一个shallow network需要更多数据量才能训练好,deep structure只需要更少数据量就能训练好
一些其他领域类似事物
逻辑电路中多层叠加,同一个功能还可以形成一个模块复用
剪窗花的例子,先把纸折叠多层再剪,比直接剪搞笑的多(感觉这个例子不好,这个是单纯的重复)
第四章 卷积神经网络
基本CNN结构
RGB图片可以认为是一个三维Tensor,大小是 H ∗ W ∗ C H*W*C H∗W∗C,其中 C C C 是通道数,最初有红绿蓝三通道。
最简单的方式就是三维Tensor拉直成一维,然后用全连接网络得到结果,但是效果不会好。
有了一些观察之后,要转换成具体的网络结构来体现这类观察。
几个重要观察:
观察1:图片的某些特征只在一部分位置出现,不需要每次都看整张图片
简化1:感受野(receptive field),每个neuron只关注自己感受野里发生的事情 —— 感受野可以通过kernel size控制。并且同一个感受野会被多个neuron学习(毕竟同一个感受野可能有多种模式)
不同感受野之间关系 —— 通过步幅(stride)控制
一些边界位置可以进行填充(padding)
观察2:同样模式可能出现在图像的不同区域(平移不变性)
简化2:共享参数(比如不同位置的有个同样参数的鸟嘴检测器)
通过卷积核共享参数,让CNN比起全连接网络所需参数大大减少,且符合图像的特征
综合感受野和参数共享,就是卷积层(convolutional layer),用到conv layer的就叫卷积神经网络。
(用于非图像任务的时候,要想想新任务是否也有上述的两个观察,不然卷积可能不合适)
下图总结了neuron角度和filter角度对conv layer的理解
每个滤波器(filter)宽度和高度可自己设置(一般就是3*3,不会太大),filter通道和图片输入通道一致。(注意,感受野不是一直都是3*3,因为往往会是多层卷积层叠加)
图片经过filter得到特征映射(feature map),
H
∗
W
∗
C
i
n
H*W*C_{in}
H∗W∗Cin 大小图片(或特征映射),经过n个filter的卷积层后,输出新的特征映射为
H
′
∗
W
′
∗
C
o
u
t
H^{\prime}*W^{\prime}*C_{out}
H′∗W′∗Cout,输出通道数量
C
o
u
t
C_{out}
Cout 就是n。
比如输入feature map是
H
∗
W
∗
C
i
n
H*W*C_{in}
H∗W∗Cin 大小,有
C
o
u
t
C_{out}
Cout 个
k
h
∗
k
w
∗
C
i
n
k_{h}*k_{w}*C_{in}
kh∗kw∗Cin 大小的filters,得到
H
′
∗
W
′
∗
C
o
u
t
H^{\prime}*W^{\prime}*C_{out}
H′∗W′∗Cout 的输出feature map,其中
H
′
=
H
−
k
h
+
2
P
S
+
1
H^{\prime} = \frac{H - k_{h} + 2P}{S} + 1
H′=SH−kh+2P+1,
W
′
=
W
−
k
w
+
2
P
S
+
1
W^{\prime} = \frac{W - k_{w} + 2P}{S} + 1
W′=SW−kw+2P+1
(我感觉理解了这个计算,卷积层也就掌握了)
观察3:下采样(downsampling)不影响模式检测
下采样把图片变小,减少运算量
简化3:池化(pooling)
据老师说近年来算力大大增强,pooling做的越来越少,很多网络都是full convolutional layers全卷积网络(这种信息很重要!)
整个卷积网络:多个卷积块(卷积+池化),最后拉直经过全连接网络输出
AlphaGo案例
实际案例:AlphaGo下围棋,棋盘可以看作19*19矩阵,并且原论文说有48个通道,48个通道表示石头颜色信息、双方玩家最近8步棋位置和特征平面信息(是否为禁着点 被提的可能性等),可以看到他们也是很努力想让输入包括更多的人类总结的技巧信息,并且AlphaGo第一层用5*5卷积(应该是认为最小的pattern可以在5*5范围内被捕捉)
AlphaGo网络结构中就没有用pooling,毕竟这19*19的棋盘,哪怕少一横一竖都完全不同了(对网络的结构终归还是要符合任务本身的特征)
给人感觉就是要想网络结构有效,还是要加入很多人类对问题本身的理解,让网络能够更有效的学习;反过来AlphaGo也对人类对围棋的理解有了更深的进展
对AlphaGo的一些感想:人工智能的进展也让人类反思什么是“智能”。同样大家都学习的是一些有限的词语有限的音符,有人就能组合成美妙的文章音乐;同样大家有学习一些基本定理,有些人就能从某些前提经过复杂推理得到一些惊人的结论。这种组合的能力 推理的能力都慢慢的为机器所掌握(即使还不是完美的掌握)。这也让人类对什么是“智能”有了更深的理解,或许未来能诞生更新更高形式的“智能”形态。
CNN在语音 NLP上也有应用,但不要生搬硬套,或许要根据任务进行一些结构上的改进。
额外注意:CNN is not invarant to scaling and rotation,需要通过数据增强来解决这两个问题!(相当于让CNN有意识的去学习scaling和rotation)—— 不过还有Spatial Transformer Layer结构能直接处理这两个问题
Spatial Transformer Layer
(这个transformer跟NLP的transformer好像不是一个意思?)
CNN不是scale invariant和rotation invariant的
(意思是图片里只有小只的狗那就只会检测小只的,要想检测大只的狗就只能图片里有大只的狗)
在CNN前面多叠加一层,对输入图片做某种transform(也能对feature map进行transform)
insight:全连接网络其实是有办法做平移 旋转 缩放这些的,因为这只是个线性变化(不过怎么往这方面训练出来呢?)—— 得到启发,想要对图片进行旋转缩放,只要对weight进行某种特定设计
总归对一个图片进行affine transformation,就只要六个参数:
所以要加入的这个spatial transformer模块,就只要输入一张图片,输出是这6个值,然后输入下一卷积层前进行下仿射变换。(原论文不是完全这样做,但大概意思一致)
这个spatial transformer模块,可以加在原始图片和特征映射之后,还能一次加多个。
为了让这个模块能够进行梯度下降求解,要使用interpolation插值。
细节不管了,总归就是加上了一个能够进行仿射变换的模块,对图片进行平移 旋转 缩放,并且和CNN一起训练。
第五章 循环神经网络
第六章 自注意力机制
之前网络都是输入一个向量,输出类别or数值
下面研究输入是向量序列(vector set as input)的情况,语音/NLP等等都有这个需求;并且图也可以看作是一个向量序列(每个节点用一个向量表示);药物发现中,分子也可以看作很多节点组成的图,也可以用一个向量序列表示
(研究输入是向量序列的情况应用很广泛!)
表示每个向量最简单办法是 one-hot encoding,但是不能体现单词之间的联系 => 更好方法是用词嵌入word embedding,能表达向量之间的联系
输入是向量序列,输出有三种情况,对应三种类型任务(label表示类别或数值)
类型1:输出是和输入同样长度的label序列,称sequence labeling,比如判断句子中每个单词的词性(词性标注)
类型2:输出是一个标签,比如句子情感分析,判断语音是谁说的,预测某分子的亲水性
类型3:输出一个不定长的向量序列,称seq2seq,如翻译,语音识别
类型1 sequence labeling,最重要的就是预测某个向量的label时需要考虑上下文,最好就是考虑整个句子上下文,也即 self-attention 注意力模型
(我理解是,设计的网络结构需要让每个token与整个句子其他tokens都产生联系,并抓住跟当前token联系最紧密的一些词汇)
self-attention的思想很早就有,但被Attention Is All You Need论文发扬光大,还提出了Transformer架构(self-attention模块就是transformer最重要的模块)
self-attention的大致结构示意图如下,self-attention模块让各个词汇间产生联系
自注意力模块最重要的就是怎么计算两个向量之间的关联程度。方法一是两个向量各自乘以权重再点积,方法二是加性模型(具体方式待看),transformer使用前者
下面展示整个自注意力模块的运作示意:
每个输入向量
a
i
a^{i}
ai 各自乘以
W
q
,
W
k
,
W
v
W^{q},W^{k},W^{v}
Wq,Wk,Wv 矩阵后得到
q
i
,
k
i
,
v
i
q^{i},k^{i},v^{i}
qi,ki,vi 三个向量(Query, Key, Value)。通过
q
q
q 和
k
k
k 之间关系得到两个向量之间的关联程度(attention score),最后就能得出整个输出序列了
具体每个输出的计算如下图
b
1
=
(
q
1
⊙
k
1
)
v
1
+
(
q
1
⊙
k
2
)
v
2
+
(
q
1
⊙
k
3
)
v
3
+
.
.
.
b^1 = (q^1 \odot k^1)v^1 + (q^1 \odot k^2)v^2 + (q^1 \odot k^3)v^3 + ...
b1=(q1⊙k1)v1+(q1⊙k2)v2+(q1⊙k3)v3+...
α
i
,
j
=
q
i
⊙
k
j
\alpha_{i,j} = q^i \odot k^j
αi,j=qi⊙kj
α
1
,
1
α
1
,
2
α
1
,
3
.
.
.
\alpha_{1,1} \quad \alpha_{1,2} \quad \alpha_{1,3} \quad...
α1,1α1,2α1,3... 经过softmax后得到
α
1
,
1
′
α
1
,
2
′
α
1
,
3
′
.
.
.
\alpha^\prime_{1,1} \quad \alpha^\prime_{1,2} \quad \alpha^\prime_{1,3} \quad...
α1,1′α1,2′α1,3′...
要对得到的注意力得分矩阵
A
A
A 的每一列进行归一化(比如softmax)得到
A
′
A^\prime
A′,因为
A
′
A^\prime
A′ 的第
i
i
i 列为系数对
v
1
,
v
2
,
v
3
.
.
.
v^1, v^2, v^3...
v1,v2,v3... 加权和结果就是输出的
b
i
b^i
bi
整个过程中,要学习的参数其实就 W q , W k , W v W^{q},W^{k},W^{v} Wq,Wk,Wv 这三个矩阵!
(之前第一次学的时候觉得云里雾里,这次看好理解多了)
注意,注意力矩阵的复杂度是输入序列长度的平方
多头注意力机制
上面讲的注意力机制,只有一组
W
q
,
W
k
,
W
v
W^{q},W^{k},W^{v}
Wq,Wk,Wv,也就是说只有一组关联计算方式
多头注意力机制就是多几组这样的参数,下图就是two head情形
注意到一个细节,为什么不直接两个矩阵乘以
a
i
a^i
ai 得到
q
i
,
1
,
q
i
,
2
q^{i,1}, q^{i,2}
qi,1,qi,2 而是先一个矩阵得到
q
i
q^i
qi,再用两个矩阵得到
q
i
,
1
,
q
i
,
2
q^{i,1}, q^{i,2}
qi,1,qi,2 —— 直观感觉跟之前shallow network v.s. deep network一样,先学习到一些共同的基本特征
这几个头得学习到不同的结果,那就要引入一定的随机性!(这个随机性怎么引入的?)
位置编码
目前这种注意力机制对待序列中每个token都是公平的,但实际上要体现不同位置会更好(比如句首是动词的概率比较低等等)
目前的方法是直接在word embedding中额外加入个位置向量8,比如用正弦/余弦函数
截断自注意力 truncated self-attention
计算注意力矩阵 A A A 的复杂度是输入序列长度 L L L 的平方,输入序列极长就需要非常大的内存保存该矩阵,不易处理不易训练—— 向量序列过长时,不看整句话,只看一个小范围 —— 截断自注意力
自注意力 v.s. CNN
图像也可以视作 vector set!图像问题也可以用self-attention
CNN可以视作简化版的self-attention9 10,因为CNN只处理某个感受野范围,而self-attention处理整张图片 —— attention太妙了!(但是attention应该会需要更多计算资源吧)
(随着认识的深入,对网络结构的认识日益深刻! 不过这样发展有其原因,更简单的网络更容易训练,只有在软硬件条件和数据量条件更好的情况下,更复杂的transformers才会被提出)
自注意力 v.s. RNN11
RNN计算下一个输出需要前一个输出,无法并行;self-attention直接并行把
q
,
k
,
v
q,k,v
q,k,v 都算出来,又并行把
A
A
A 各部分算出来
RNN中不相邻的两个token没法直接联系,只能一层层传递联系;self-attention任何两个token都能直接计算联系程度
自注意力用于图
图不光有节点作为vector set,节点间还有联系——边edges
只需要计算有边连接的两个节点之间的attention score
第七章 Transformer
transformers计算资源需要更多,有一个benchmark12和综述13
On large-batch training for deep learning: Generalization gap and sharp minima, 2016 ↩︎
Stochastic weight averaging in parallel: Large-batch training that generalizes well, 2020 ↩︎
Large batch training of convolutional networks, 2017 ↩︎
Large batch optimization for deep learning: Training bert in 76 minutes, 2019 ↩︎
Extremely large minibatch sgd: Training resnet-50 on imagenet in 15 minutes, 2017 ↩︎
Accurate, large minibatch sgd: Training imagenet in 1 hour, 2017 ↩︎
Conversational Speech Transcription Using Context-Dependent Deep Neural Networks, 2011 ↩︎
Learning to Encode Position for Transformer with Continuous Dynamical Model ↩︎
On the Relationship between Self-Attention and Convolutional Layers, 2019 ↩︎
An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale, 2020 ↩︎
Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention, 2020 ↩︎
Long Range Arena: A Benchmark for Efficient Transformers, 2020 ↩︎
Efficient Transformers: A Survey, 2020 ↩︎