当前位置:   article > 正文

深度学习理论基础

深度学习理论基础

深度学习理论

线性回归

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

多层感知机

仿射变换:加权特征组合,带偏置项的线性组合

Softmax回归:通过单个仿射变换将我们的输入直接映射到输出,然后进行softmax操作

在这里插入图片描述

问题:如果我们的标签通过仿射变换后确实与我们的输入数据相关,那么这种方法确实足够了。 但是,仿射变换中的线性是一个很强的假设

想办法解决:数据可能会有一种表示,这种表示会考虑到我们在特征之间的相关交互作用,在此表示的基础上建立一个线性模型可能会是合适的, 但我们不知道如何手动计算这么一种表示

  • 由此推出,使用观测数据来联合学习隐藏层表示和应用于该表示的线性预测器

隐藏层的意义:

我们可以通过在网络中加入一个或多个隐藏层来克服线性模型的限制, 使其能处理更普遍的函数关系类型

—》多层感知机:

最简单的方法是将许多全连接层堆叠在一起。 每一层都输出到上面的层,直到生成最后的输出。 我们可以把前层看作表示,把最后一层看作线性预测器

何谓全连接?

全连接,即每个输入都会影响隐藏层中的每个神经元, 而隐藏层中的每个神经元又会影响输出层中的每个神经元

在这里插入图片描述
这是3层的感知机模型,全连接层有两个,隐藏层和输出层

隐藏层的目的:使得多层感知机可以通过隐藏神经元,捕捉到输入之间复杂的相互作用

  • 但是具有全连接层的多层感知机的参数开销可能会高得令人望而却步。
  • 可能的解决办法:在不改变输入或输出大小的情况下, 可能在参数节约和模型有效性之间进行权衡

在这里插入图片描述

关于初始化权重为0,为1情形的数学证明
在这里插入图片描述
在这里插入图片描述

激活函数

ReLu函数

ReLu : ReLU函数通过将相应的活性值设为0,仅保留正元素并丢弃所有负元素,即 R e L u ( x ) = m a x ( x , 0 ) ReLu(x)=max(x,0) ReLu(x)=max(x,0)

在这里插入图片描述

  • 当输入为负时,ReLU函数的导数为0,而当输入为正时,ReLU函数的导数为1。
  • 注意,当输入值精确等于0时,ReLU函数不可导。
  • 在此时,我们默认使用左侧的导数,即当输入为0时导数为0。 我们可以忽略这种情况,因为输入可能永远都不会是0

在这里插入图片描述

sigmoid函数

它将范围(-inf, inf)中的任意输入压缩到区间(0, 1)中的某个值

s i g m o i d ( x ) = 1 1 + e − x sigmoid(x) = \frac{1}{1+e^{-x}} sigmoid(x)=1+ex1

  • 在其输入低于某个阈值时取值0,当输入超过阈值时取值1。

导数很有意思:
在这里插入图片描述
在这里插入图片描述

  • 是个二次函数,最大值为0.25,在x=0时取到

tanh函数

tanh(双曲正切)函数也能将其输入压缩转换到区间(-1, 1)上

t a n h ( x ) = 1 − e − 2 x 1 + e − 2 x tanh(x)=\frac{1-e^{-2x}}{1+e^{-2x}} tanh(x)=1+e2x1e2x

函数的形状类似于sigmoid函数, 不同的是tanh函数关于坐标系原点中心对称。

在这里插入图片描述
导数:
在这里插入图片描述

  • 当输入接近0时,tanh函数的导数接近最大值1

在这里插入图片描述
在这里插入图片描述

避免过拟合

首先,线性模型的过拟合不必多说,两点

  1. 样本不足,特征足够,必然过拟合
  2. 样本足够,特征不够,肯定不会过拟合

但是神经网络的过拟合是个玄学

举个栗子:

即使我们有比特征多得多的样本,深度神经网络也有可能过拟合。 2017年,一组研究人员通过在随机标记的图像上训练深度网络。 这展示了神经网络的极大灵活性,因为人类很难将输入和随机标记的输出联系起来, 但通过随机梯度下降优化的神经网络可以完美地标记训练集中的每一幅图像。 想一想这意味着什么?
1.标签是随机均匀分布分配的,10个类别
2.基于1的假设,理论上分类器分对的概率应小等于0.1
分类器在测试数据上很难取得高于10%的精度, 那么这里的泛化差距就高达90%,如此严重的过拟合。

权值衰退

余所谓之,权值衰退,其实就是L2正则化

  • L2正则化:通过函数与零的距离来衡量函数的复杂度
  • 先验假设,权值W服从均值为0的正态分布

神经网络中的L2正则化,体现在隐藏层中,由于L2正则化会将某些特征压缩到很小,但没有扔弃掉这些特征。也就是当前层的某些神经元作用很小,逐层衰减,使得我们的学习算法偏向于在大量特征上均匀分布权重的模型

特征之间的不同:

  • 线性模型没有考虑到特征之间的交互作用。 对于每个特征,线性模型必须指定正的或负的权重,而忽略其他特征
  • 与线性模型不同,神经网络并不局限于单独查看每个特征,而是学习特征之间的交互

补充一下:

初始化的W可以假设为均值为0的正态分布,框架默认W的初始化分布为随机初始化

Xavier初始化

现在,让我们假设层 x j x_j xj的输入也具有零均值和方差 σ 2 \sigma^2 σ2, 并且它们 w i j w_{ij} wij独立于并且彼此独立。 在这种情况下,我们可以按如下方式计算的平均值和方差

在这里插入图片描述

  • n i n n_{in} nin是该层的输入, n o u t 是该层的输出 n_{out}是该层的输出 nout是该层的输出

保持方差不变的一种方法是设置 n i n σ 2 = 1 n_{in}\sigma^2=1 ninσ2=1,但是不满足这个条件,梯度将会增加(因为梯度是N个X和W的矩阵乘积)

  • 好像这是个不可能的问题$n_{in}\sigma^2$和$\gamma^2$同时减小,那总方差必然减小!

咋解决?

Xavier初始化:均值为0, σ 2 = 2 n i n + n o u t \sigma^2=\frac{2}{n_{in} + n_{out}} σ2=nin+nout2的正态分布,也可以是均匀分布

在这里插入图片描述
均匀分布:

U ( − a , a ) U(-a,a) U(a,a) , σ 2 = a 2 / 3 \sigma^2 =a^2/3 σ2=a2/3 , μ = 0 \mu=0 μ=0 带入上式
在这里插入图片描述

  • Xavier初始化解决的问题,对于每一层,输出的方差不受输入数量的影响,任何梯度的方差不受输出数量的影响

DropOut

平滑性:即函数不应该对其输入的微小变化敏感

当我们对图像进行分类时,我们预计向像素添加一些随机噪声应该是基本无影响的

  • 已有证明 具有输入噪声的训练等价于Tikhonov正则化
    即,“要求函数光滑”和“要求函数对输入的随机噪声具有适应性”等价

如何将这个随机噪声加入隐藏层中呢?

在训练过程中,他们建议在计算后续层之前向网络的每一层注入噪声。 因为当训练一个有多层的深层网络时,注入噪声只会在输入-输出映射上增强平滑性

暂退法DropOut : 在前向传播过程中,计算每一内部层的同时注入噪声

DropOut的由来:

因为我们从表面上看是在训练过程中丢弃(drop out)一些神经元。 在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零

问题又来了!

暂退法的原始论文提到了一个关于有性繁殖的类比: 神经网络过拟合与每一层都依赖于前一层激活值相关,称这种情况为“共适应性”。 那么,暂退法会破坏共适应性,就像有性生殖会破坏共适应的基因一样

接下来,噪声如何注入?

一种想法是以一种无偏向(unbiased)的方式注入噪声。 这样在固定住其他层时,每一层的期望值等于没有噪音时的值。

  • 即,注入高斯噪声(噪声服从均值为0的高斯分布)

标准暂退法正则化中,通过按保留(未丢弃)的节点的分数进行规范化来消除每一层的偏差。

  • 换言之,每个中间活性值(激活值) h h h以暂退概率 p p p由随机变量 h ′ h^{'} h替换

在这里插入图片描述

暂退概率:以多少的概率扔掉该神经元

为什么是h除以1-p?

  • 保持期望一样

如果暂退概率为p,随机变量为0,扔掉该神经元
如果暂退概率不为p,加入随机变量,保留该神经元

  • 关键是 E ( h ′ ) = h E(h^{'})=h E(h)=h,期望不变,不会影响整体的结果

在这里插入图片描述
总结:

  1. 暂退法在前向传播过程中,计算每一内部层的同时丢弃一些神经元。

  2. 暂退法可以避免过拟合,它通常与控制权重向量的维数和大小结合使用的。

  3. 暂退法将活性值h替换为具有期望值的随机变量。

  4. 暂退法仅在训练期间使用。

  5. 暂退法可以打破隐藏层神经元的对称性,小批量随机梯度下降不行!

学习的环境

当数据分布突然改变时,模型在部署中会出现灾难性的失败

举个栗子:

我们训练了一个贷款申请人违约风险模型,用来预测谁将偿还贷款或违约。 这个模型发现申请人的鞋子与违约风险相关(穿牛津鞋申请人会偿还,穿运动鞋申请人会违约)。 此后,这个模型可能倾向于向所有穿着牛津鞋的申请人发放贷款,并拒绝所有穿着运动鞋的申请人

后果:

一旦模型开始根据鞋类做出决定,顾客就会理解并改变他们的行为。 不久,所有的申请者都会穿牛津鞋,而信用度却没有相应的提高

  • 通过将基于模型的决策引入环境,我们可能会破坏模型

分布偏移的含义:
训练集是一个分布 , 测试集是另一个分布 ,然后一些有趣的事情就发生了

协变量偏移:

虽然输入的分布可能随时间而改变, 但标签函数 P ( y ∣ x ) P(y|x) P(yx)(即条件分布)没有改变

标签偏移:

标签偏移(label shift)描述了与协变量偏移相反的问题.标签边缘概率 P ( y ) P(y) P(y)可以改变, 但是类别条件分布 P ( x ∣ y ) P(x|y) P(xy)在不同的领域之间保持不变

思考的栗子:

一所大学校园内的学生征集献血,作为开发测试的健康对照样本。 然后我们是否可以帮助他们建立一个用于检测疾病的分类器。
正如我们向他们解释的那样,用近乎完美的精度来区分健康和患病人群确实很容易。 然而,这可能是因为受试者在年龄、激素水平、体力活动、 饮食、饮酒以及其他许多与疾病无关的因素上存在差异。 这对检测疾病的分类器可能并不适用。 这些抽样可能会遇到极端的协变量偏移。 此外,这种情况不太可能通过常规方法加以纠正

非平稳分布:

当分布变化缓慢并且模型没有得到充分更新时,就会出现更微妙的情况: 非平稳分布

  1. 训练一个计算广告模型,但却没有经常更新(例如,一个2009年训练的模型不知道一个叫iPad的不知名新设备刚刚上市)。

  2. 建立一个垃圾邮件过滤器,它能很好地检测到所有垃圾邮件。但是,垃圾邮件发送者们变得聪明起来,制造出新的信息,看起来不像我们以前见过的任何垃圾邮件。

  3. 建立一个产品推荐系统,它在整个冬天都有效,但圣诞节过后很久还会继续推荐圣诞帽。

逼着我去学强化学习,呜呜呜~

如何纠正分布偏移

协变量偏移的纠正:
需要根据数据来自正确分布与来自错误分布的概率之比, 来重新衡量每个数据样本的权重

标签偏移的纠正:
观测源数据上的标签,所以很容易估计原数据标签分布,再用原数据标签和目标数据的比率就行更正

优化算法

优化提供了一种最大限度地减少深度学习损失函数的方法,但实质上,优化和深度学习的目标是根本不同的。

  • 最优化:主要关注的是最小化目标。
    • 优化算法的目标函数通常是基于训练数据集的损失函数,因此优化的目标是减少训练误差
  • 深度学习:关注在给定有限数据量的情况下寻找合适的模型
    • 除了使用优化算法来减少训练误差之外,我们还需要注意过拟合

经验风险是训练数据集的平均损失,而风险则是整个数据群的预期损失

局部最小值

深度学习模型的目标函数通常有许多局部最优解。

在这里插入图片描述

当优化问题的数值解接近局部最优值时,随着目标函数解的梯度接近或变为零,通过最终迭代获得的数值解可能仅使目标函数局部最优,而不是全局最优。

  • 只有一定程度的噪声可能会使参数超出局部最小值。

即,随机梯度下降

事实上,这是小批量随机梯度下降的有利特性之一,在这种情况下,小批量上梯度的自然变化能够将参数从局部极小值中移出

鞍点

在这里插入图片描述

  • 鞍点(saddle point)是指函数的所有梯度都消失但既不是全局最小值也不是局部最小值的任何位置
  • 一阶导数、二阶导数均为0
  • 优化停止,非最小值

在这里插入图片描述

  • 如何解决这个问题? 海森矩阵
  • Hessian矩阵正定:当函数在零梯度位置处的Hessian矩阵的特征值全部为正值时,我们有该函数的局部最小值。

  • Hessian矩阵负定:当函数在零梯度位置处的Hessian矩阵的特征值全部为负值时,我们有该函数的局部最大值。

  • Hessian矩阵半正定:当函数在零梯度位置处的Hessian矩阵的特征值为负值和正值时,我们对函数有一个鞍点。

梯度消失与爆炸

根源:链式求导
对激活函数进行求导,如果此部分大于1,那么层数增多的时候,最终的求出的梯度更新将以指数形式增加,即发生梯度爆炸,如果此部分小于1,那么随着层数增多,求出的梯度更新信息将会以指数形式衰减,即发生了梯度消失
在这里插入图片描述
w 1 的更新的结果是梯度累成的,如果每一层的梯度项均大于 1 ,结果将成指数级别增加,反之小于 1 同理 w1的更新的结果是梯度累成的,如果每一层的梯度项均大于1,结果将成指数级别增加,反之小于1同理 w1的更新的结果是梯度累成的,如果每一层的梯度项均大于1,结果将成指数级别增加,反之小于1同理

  • 越深的隐藏层,权值更新速度越慢,反之越快
    在这里插入图片描述

在x=4之后,梯度几乎为0,但不为0,导致很长一段时间内无法取得较大进展

总结:

从深层网络角度来讲,不同的层学习的速度差异很大,表现为网络中靠近输出的层学习的情况很好,靠近输入的层学习的很慢,有时甚至训练了很久,前几层的权值和刚开始随机初始化的值差不多
因此,梯度消失、爆炸,其根本原因在于反向传播训练法则,本质在于方法问题,另外多说一句,对于人来说,在大脑的思考机制里是没有反向传播的,Hinton提出capsule的原因就是为了彻底抛弃目前基于反向传播的深度学习算法,如果真能大范围普及,那真是一个革命。

解决方法:

  • 局部最优解或其近似解仍然非常有用
  • 更换激活函数,sigmoid函数问题严重,换成relu、leakrelu、elu等激活函数
  • batch normalization,简称BN,即批规范化,通过规范化操作将输出信号x规范化到均值为0,方差为1保证网络的稳定性
  • 残差结构
  • 梯度剪切这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度剪切阈值,然后更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。这可以防止梯度爆炸
  • 采用权重正则化(weithts regularization)比较常见的是l1正则,和l2正则在这里插入图片描述其中, a a a 是指正则项系数,因此,如果发生梯度爆炸,权值的范数就会变的非常大,通过正则化项,可以部分限制梯度爆炸的发生。

梯度下降法,则每个自变量迭代的计算代价为 O ( n ) O(n) O(n),它随线性增长。因此,当训练数据集较大时,每次迭代的梯度下降计算代价将较高。

随机梯度下降(SGD)可降低每次迭代时的计算代价。在随机梯度下降的每次迭代中,我们对数据样本随机均匀采样一个索引 i i i, 也就是随机抽取一个数据然后计算梯度。

  • 关键点:随机梯度是完整梯度的无偏估计
    在这里插入图片描述
  • 缺点:也许随机梯度很快的解决最小值,但是由于随机性,可能后面的50次迭代始终取不到最小值

小批量

之前我们会理所当然地读取数据的小批量,而不是观测单个数据来更新参数

处理单个观测值需要我们执行许多单一矩阵-矢量(甚至矢量-矢量)乘法,这耗费相当大,而且对应深度学习框架也要巨大的开销。 这既存在于计算梯度以更新参数时,也适用于用神经网络预测。

  • 换句话说,我们在权值更新时需要巨大的计算开销,因为每一条数据都有进行计算、更新
  • 我们可以通过将其应用于一个小批量观测值来提高此操作的计算效率。 也就是说,我们将梯度替换为一个小批量而不是单个观测值

单个梯度:

在这里插入图片描述
小批量:

在这里插入图片描述
统计性质:

  1. 无偏性:由于 x i x_i xi和小批量的所有元素都是从训练集中随机抽出的,因此梯度的期望保持不变
  2. 方差显著降低: 由于小批量梯度由正在被平均计算的小批量( b b b)个独立梯度组成,其标准差降低了 b − 1 / 2 b^{-1/2} b1/2。 这本身就是一件好事,因为这意味着更新与完整的梯度更接近了

然而,经过一段时间后,与计算代价的线性增长相比,标准差的额外减少是微乎其微的。 在实践中我们选择一个足够大的小批量,它可以提供良好的计算效率同时仍适合GPU的内存

  • 小批量的核心就是,小批量抽取的平均梯度,它会减小方差

对于嘈杂的梯度,我们在选择学习率需要格外谨慎。 如果衰减速度太快,收敛就会停滞。 相反,如果太宽松,我们可能无法收敛到最优解

动量法

较大的 β \beta β相当于长期平均值,而较小的 β \beta β相对于梯度法只是略有修正。 新的梯度替换不再指向特定实例下降最陡的方向,而是指向过去梯度的加权平均值的方向。 这使我们能够实现对单批量计算平均值的大部分好处,而不产生实际计算其梯度的代价

此外,它们允许我们对随后的梯度计算平均值,以获得更稳定的下降方向。 诚然,即使是对于无噪声凸问题,加速度这方面也是动量如此起效的关键原因之一

条件不佳:

优化问题条件不佳的情况下(例如,有些方向的进展比其他方向慢得多,类似狭窄的峡谷

  • 也就是在x2的方向上下降的快,在x1的方向上下降的慢.因此,我们陷入两难:如果选择较小的学习率,我们会确保解不会在 x 2 x2 x2方向发散,但要承受在 x 1 x1 x1方向的缓慢收敛。相反,如果学习率较高,我们在 x 2 x2 x2方向上进展很快,但在 x 1 x1 x1方向将会发散

动量法(momentum)使我们能够解决上面描述的梯度下降问题。 观察上面的优化轨迹,我们可能会直觉到计算过去的平均梯度效果会很好。 毕竟,在 x 1 x1 x1方向上,这将聚合非常对齐的梯度,从而增加我们在每一步中覆盖的距离。 相反,在梯度振荡的 x 2 x2 x2方向,由于相互抵消了对方的振荡,聚合梯度将减小步长大小。

在这里插入图片描述

深度前馈神经网络

稀疏连接

  • 即CNN的卷积原理,卷积核的参数就是连接的权重
  • 深一层的神经元不一定要和上一层的所有神经元全部连接
    在这里插入图片描述

卷积神经网络

这个栗子简直太香了!!!

  • 将图像分割成多个区域,并为每个区域包含沃尔多的可能性打分

卷积神经网络正是将空间不变性(spatial invariance)的这一概念系统化,从而基于这个模型使用较少的参数来学习有用的表示

神经网络的架构设计:

  • 平移不变性(translation invariance):不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应,即为“平移不变性”。

    • 检测对象在输入中的平移,应该仅导致隐藏表示中的平移
    • 每一层有每一层的权重参数
    • 检测对象在移动,层的输入在变化,W(四阶张量矩阵的索引也跟着走)索引开销变小
    • 检测对象=卷积核=滤波器=该卷积层的权重
  • 局部性(locality):神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系,这就是“局部性”原则。最终,可以聚合这些局部特征,以在整个图像级别进行预测。

CNN分析:

  1. 多层感知机可能需要数十亿个参数来表示网络中的一层,而现在卷积神经网络通常只需要几百个参数,而且不需要改变输入或隐藏表示的维数
  2. 参数大幅减少的代价是,我们的特征现在是平移不变的,并且当确定每个隐藏活性值时,每一层只包含局部的信息
  3. 以上所有的权重学习都将依赖于归纳偏置
  4. 当这种偏置与现实相符时,我们就能得到样本有效的模型,并且这些模型能很好地泛化到未知数据中。 但如果这偏置与现实不符时,比如当图像不满足平移不变时,我们的模型可能难以拟合我们的训练数据

图像一般包含三个通道/三种原色(红色、绿色和蓝色)

图像不是二维张量,而是一个由高度、宽度和颜色组成的三维张量,比如包含个像素(1024x1024x3)

前两个轴与像素的空间位置有关,而第三个轴可以看作是每个像素的多维表示

  • 多个输入和输出通道使模型在每个空间位置可以获取图像的多方面特征

  • 输出大小 = 输入的size(height,width) - 卷积核size(h,w)
  • 卷积层对输入和卷积核权重进行互相关运算,并在添加标量偏置之后产生输出

严格卷积:需水平和垂直翻转二维卷积核张量,然后对输入张量执行互相关运算
由于卷积核是从数据中学习到的,因此无论这些层执行严格的卷积运算还是互相关运算,卷积层的输出都不会受到影响

  • 输出的卷积层有时被称为特征映射(feature map),因为它可以被视为一个输入映射到下一层的空间维度的转换器

何谓卷积?

这篇文章通俗理解卷积!

卷积网络的计算

在这里插入图片描述
我们看到输入层的宽度和高度对应于输入图像的宽度和高度,而它的深度为1。接着,第一个卷积层对这幅图像进行了卷积操作(后面我们会讲如何计算卷积),得到了三个Feature Map。这里的"3"可能是让很多初学者迷惑的地方,实际上,就是这个卷积层包含三个Filter,也就是三套参数,每个Filter都可以把原始输入图像卷积得到一个Feature Map,三个Filter就可以得到三个Feature Map。至于一个卷积层可以有多少个Filter,那是可以自由设定的。

  • 也就是说,卷积层的Filter个数也是一个超参数。我们可以把Feature Map可以看做是通过卷积变换提取到的图像特征,三个Filter就对原始图像提取出三组不同的特征,也就是得到了三个Feature Map,也称做三个通道(channel)。

在第一个卷积层之后,Pooling层对三个Feature Map做了下采样(后面我们会讲如何计算下采样),得到了三个更小的Feature Map。接着,是第二个卷积层,它有5个Filter。每个Fitler都把前面下采样之后的3个Feature Map卷积在一起,得到一个新的Feature Map。这样,5个Filter就得到了5个Feature Map。接着,是第二个Pooling,继续对5个Feature Map进行下采样,得到了5个更小的Feature Map。

最后两层是全连接层。第一个全连接层的每个神经元,和上一层5个Feature Map中的每个神经元相连,第二个全连接层(也就是输出层)的每个神经元,则和第一个全连接层的每个神经元相连,这样得到了整个网络的输出。

图像大小、步幅和卷积后的Feature Map大小是有关系的:

W 2 = ( W 1 − F + 2 P ) / S + 1 W_2=(W_1-F+2P)/S+1 W2=(W1F+2P)/S+1
H 2 = ( H 1 − F + 2 P ) / S H_2=(H_1-F+2P)/S H2=(H1F+2P)/S

  • W2是卷积之后Feature Map的宽度,W1是卷积之前的宽度,
  • H2是卷积之后Feature Map的宽度,H1是卷积之前图像的宽度
  • F是卷积核的宽度,P是在原始图像周围补充几圈0,P=1就是补一圈0,S是步幅,

下图体现了多个卷积核一起做卷积的过程:

包含了局部连接和参数共享的思想

局部连接:每个神经元仅与输入神经元的一块区域连接,这块局部区域称作感受野(receptive field)。在图像卷积操作中,即神经元在空间维度(spatial dimension,即上图示例H和W所在的平面)是局部连接,但在深度上是全部连接。对于二维图像本身而言,也是局部像素关联较强。这种局部连接保证了学习后的过滤器能够对于局部的输入特征有最强的响应。局部连接的思想,也是受启发于生物学里面的视觉系统结构,视觉皮层的神经元就是局部接受信息的。

权重共享:计算同一个深度切片的神经元时采用的滤波器是共享的。例上图中计算o[:,:,0]的每个每个神经元的滤波器均相同,都为W0,这样可以很大程度上减少参数。共享权重在一定程度上讲是有意义的,例如图片的底层边缘特征与特征在图中的具体位置无关。
注意权重只是对于同一深度切片的神经元是共享的,在卷积层,通常采用多组卷积核提取不同特征,即对应不同深度切片的特征,不同深度切片的神经元权重是不共享。另外,偏重对同一深度切片的所有神经元都是共享的。

请添加图片描述

FLOPs计算

FLOPs是模型的计算次数

在这里插入图片描述

  • 卷积层计算:

( 2 ∗ C i ∗ K 2 − 1 ) ∗ H ∗ W ∗ C 0 (2*C_i*K^2-1)*H*W*C_0 (2CiK21)HWC0

Ci = input channel,
k = kernel size,
H,W = output feature map size
Co = output channel.

  • 2是因为一个MAC算2个operations
  • 不考虑bias时有-1,有bias时没有-1
  • 上面公式针对一个input feature map,没考虑batch size

公式理解:

括号内: 计算出output feature map的一个pixel,即相乘再相加的操作
( 2 ∗ C i ∗ K 2 − 1 ) = ( C i ∗ K 2 ) + ( C i ∗ K 2 − 1 ) (2*C_i * K^2 -1) = (C_i * K^2)+(C_i*K^2-1) (2CiK21)=(CiK2)+(CiK21)

括号外:乘以HWCo拓展到整个output feature map。

  • 全连接层

( 2 ∗ I − 1 ) ∗ O (2*I-1)*O (2I1)O

I=input neuron numbers, O=output neuron numbers.

2是因为一个MAC算2个operations。

括号内是一个输出神经元的计算量,拓展到O了输出神经元。
不考虑bias时有-1,有bias时没有-1。

  • LSTM/RNN

( R + H ) ∗ H ∗ 4 ∗ 2 (R+H)*H*4*2 (R+H)H42

E是embedding dimension, 也就是词向量维度(不是时间维度)

H是hidden state dimension, 也就是LSTM有多少cell4是4 个非线性变换块(3 个 门 + 1 个 tanh)

2是将MAC数转为FLOPsLSTM里其实还有三个乘法块,一个加法块和非线性部分,计算量占用不大,这里近似忽略。

GPU计算(模型大小)

  • 包括模型输出层的大小和模型的大小

在这里插入图片描述

模型内存占用

在这里插入图片描述
params:

总参数量 = 138,344,128 = 138MB
参数占用内存:float32 占用4个字节
138,344,128 * 4 (字节数)/ (1024 * 1024)= 527.79MB = 528MB

layers:

每一层输出参数数量总和= 15,237,608 /(1024*1024)= 15.2MB
每一层输出所需内存总和:15,237,608 4 / (10241024) = 58.12MB

运行时内存占用

在这里插入图片描述
模型内存:528 * 3 = 1.54 GB

  • params(1 mem) , SGD(1 mem),momentum(1 mem),Adam(4 mem) 要对模型参数进行改变的地方都需要内存

输出内存:batch_size * layer_memory * 2 = 14.53 GB


LeNet

在这里插入图片描述

卷积层计算

输入图片:32*32

卷积核大小:5*5

卷积核种类:6

输出featuremap大小:28*28 (32-5+1)=28

神经元数量:28286

可训练参数:(55+1) * 6(每个滤波器55=25个unit参数和一个bias参数,一共6个滤波器)

连接数:(55+1)62828=122304

详细说明:对输入图像进行第一次卷积运算(使用 6 个大小为 55 的卷积核),得到6个C1特征图(6个大小为2828的 feature maps, 32-5+1=28)。我们再来看看需要多少个参数,卷积核的大小为55,总共就有6(55+1)=156个参数,其中+1是表示一个核有一个bias。对于卷积层C1,C1内的每个像素都与输入图像中的55个像素和1个bias有连接,所以总共有1562828=122304个连接(connection)。有122304个连接,但是我们只需要学习156个参数,主要是通过权值共享实现的。

池化层(下采样层)计算

输入:28*28

采样区域:2*2

采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid

采样种类:6

输出featureMap大小:14*14(28/2)

神经元数量:14146

连接数:(22+1)61414

S2中每个特征图的大小是C1中特征图大小的1/4。

详细说明:第一次卷积之后紧接着就是池化运算,使用 22核 进行池化,于是得到了S2,6个1414的 特征图(28/2=14)。S2这个pooling层是对C1中的2*2区域内的像素求和乘以一个权值系数再加上一个偏置,然后将这个结果再做一次映射。同时有5x14x14x6=5880个连接。

C3层-卷积层

输入:(14 * 16 *6)S2中所有6个或者几个特征map组合

卷积核大小:5*5

卷积核种类:16

输出featureMap大小:10*10 (14-5+1)=10

神经元个数:1600 = 10* 10* 16

C3中的每个特征map是连接到S2中的所有6个或者几个特征map的,表示本层的特征map是上一层提取到的特征map的不同组合

存在的一个方式是:C3的前6个特征图以S2中3个相邻的特征图子集为输入。接下来6个特征图以S2中4个相邻特征图子集为输入。然后的3个以不相邻的4个特征图子集为输入。最后一个将S2中所有特征图为输入。

则:可训练参数:6*(355+1)+6*(455+1)+3*(455+1)+1*(655+1)=1516

连接数:10101516=151600

详细说明:第一次池化之后是第二次卷积,第二次卷积的输出是C3,16个10x10的特征图,卷积核大小是 55. 我们知道S2 有6个 1414 的特征图,怎么从6 个特征图得到 16个特征图了? 这里是通过对S2 的特征图特殊组合计算得到的16个特征图。

在这里插入图片描述
C3的前6个feature map(对应上图第一个红框的6列)与S2层相连的3个feature map相连接(上图第一个红框),后面6个feature map与S2层相连的4个feature map相连接(上图第二个红框),后面3个feature map与S2层部分不相连的4个feature map相连接,最后一个与S2层的所有feature map相连。卷积核大小依然为55,所以总共有6(355+1)+6*(455+1)+3*(455+1)+1*(655+1)=1516个参数。而图像大小为10*10,所以共有151600个连接。

在这里插入图片描述
在这里插入图片描述
为什么采用上述这样的组合了?论文中说有两个原因:

  • 1)减少参数
  • 2)这种不对称的组合连接的方式有利于提取多种组合特征。

C5层-1x1卷积层

输入:S4层的全部16个单元特征map(与s4全相连)

卷积核大小:5*5

卷积核种类:120

输出featureMap大小:1*1(5-5+1)

可训练参数/连接:120*(1655+1)=48120

详细说明:C5层是一个卷积层。由于S4层的16个图的大小为5x5,与卷积核的大小相同,所以卷积后形成的图的大小为1x1。这里形成120个卷积结果。每个都与上一层的16个图相连。所以共有(5x5x16+1)x120 = 48120个参数,同样有48120个连接。

F6层-全连接层

输入:c5 120维向量

神经元个数:84

计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置,结果通过sigmoid函数输出。

可训练参数:84*(120+1)=10164

详细说明:6层是全连接层。F6层有84个节点,对应于一个7x12的比特图,-1表示白色,1表示黑色,这样每个符号的比特图的黑白色就对应于一个编码。该层的训练参数和连接数是(120 + 1)x84=10164。

AlexNet

比LeNet层数更多,更深

  • 局部响应归一化层
    侧抑制: 被激活的神经元抑制周围的神经元。
    归一化的目的就是抑制
    LRU就是借鉴并实现这种效果,尤其是RELU激活函数的时候效果很明显

VGG

经典卷积神经网络的基本组成部分是下面的这个序列:

  1. 带填充以保持分辨率的卷积层;

  2. 非线性激活函数,如ReLU;

  3. 汇聚层,如最大汇聚层。

在这里插入图片描述

VGG神经网络连接几个VGG块(在vgg_block函数中定义)。其中有超参数变量conv_arch。该变量指定了每个VGG块里卷积层个数和输出通道数。全连接模块则与AlexNet中的相同

原始VGG网络有5个卷积块,其中前两个块各有一个卷积层,后三个块各包含两个卷积层。 第一个模块有64个输出通道,每个后续模块将输出通道数量翻倍,直到该数字达到512。

  • 由于该网络使用8个卷积层和3个全连接层,因此它通常被称为VGG-11。

  • 深层且窄的卷积(即)比较浅层且宽的卷积更有效。

NiN

  • 卷积层的输入和输出由四维张量组成,张量的每个轴分别对应样本、通道、高度和宽度
  • 全连接层的输入和输出通常是分别对应于样本和特征的二维张量

ideas:

  1. NiN的想法是在每个像素位置(针对每个高度和宽度)应用一个全连接层。 如果我们将权重连接到每个空间位置,我们可以将其视为卷积层,或作为在每个像素位置上独立作用的全连接层

  2. 从另一个角度看,即将空间维度中的每个像素视为单个样本,将通道维度视为不同特征(feature)。

NiN块以一个普通卷积层开始,后面是两个1x1的卷积层。这两个1x1卷积层充当带有ReLU激活函数的逐像素全连接层。 第一层的卷积窗口形状通常由用户设置。 随后的卷积窗口形状固定为1x1。

在这里插入图片描述
NiN和AlexNet之间的一个显著区别是NiN完全取消了全连接层。 相反,NiN使用一个NiN块,其输出通道数等于标签类别的数量。最后放一个全局平均汇聚层(global average pooling layer),生成一个对数几率 (logits)。NiN设计的一个优点是,它显著减少了模型所需参数的数量。然而,在实践中,这种设计有时会增加训练模型的时间。

优点:

  1. 更好的局部抽象能力
  2. 更小的参数空间
  3. 更小的全局over-fitting

GeogleNet

  • 有时使用不同大小的卷积核组合是有利的

在GoogLeNet中,基本的卷积块被称为Inception块(Inception block)。这很可能得名于电影《盗梦空间》(Inception),因为电影中的一句话“我们需要走得更深”(“We need to go deeper”)。

在这里插入图片描述
Inception块由四条并行路径组成。 前三条路径使用窗口大小为1x1、3x3和5x5的卷积层,从不同空间大小中提取信息。 中间的两条路径在输入上执行1x1卷积,以减少通道数,从而降低模型的复杂性。 第四条路径使用3x3最大汇聚层,然后使用1x1卷积层来改变通道数。 这四条路径都使用合适的填充来使输入与输出的高和宽一致,最后我们将每条线路的输出在通道维度上连结,并构成Inception块的输出。在Inception块中,通常调整的超参数是每层输出通道数。

那么为什么GoogLeNet这个网络如此有效呢?

  1. 首先我们考虑一下滤波器(filter)的组合,它们可以用各种滤波器尺寸探索图像,这意味着不同大小的滤波器可以有效地识别不同范围的图像细节。
  2. 同时,我们可以为不同的滤波器分配不同数量的参数。

GoogLeNet一共使用9个Inception块和全局平均汇聚层的堆叠来生成其估计值。Inception块之间的最大汇聚层可降低维度。 第一个模块类似于AlexNet和LeNet,Inception块的组合从VGG继承,全局平均汇聚层避免了在最后使用全连接层。

在这里插入图片描述

批量规范化

  1. 数据预处理的方式通常会对最终结果产生巨大影响
    • 使用真实数据时,我们的第一步是标准化输入特征,使其平均值为0,方差为1。 直观地说,这种标准化可以很好地与我们的优化器配合使用,因为它可以将参数的量级进行统一。
  2. 对于典型的多层感知机或卷积神经网络。当我们训练时,中间层中的变量(例如,多层感知机中的仿射变换输出)可能具有更广的变化范围:不论是沿着从输入到输出的层,跨同一层中的单元,或是随着时间的推移,模型参数的随着训练更新变幻莫测。 批量规范化的发明者非正式地假设,这些变量分布中的这种偏移可能会阻碍网络的收敛。 直观地说,我们可能会猜想,如果一个层的可变值是另一层的100倍,这可能需要对学习率进行补偿调整
  3. 更深层的网络很复杂,容易过拟合。 这意味着正则化变得更加重要

批量规范化应用于单个可选层(也可以应用到所有层)

其原理如下:

在每次训练迭代中,我们首先规范化输入,即通过减去其均值并除以其标准差,其中两者均基于当前小批量处理。 接下来,我们应用比例系数和比例偏移。 正是由于这个基于批量统计的标准化,才有了批量规范化的名称。

请注意,如果我们尝试使用大小为1的小批量应用批量规范化,我们将无法学到任何东西。

  • 这是因为在减去均值之后,每个隐藏单元将为0。

所以,只有使用足够大的小批量,批量规范化这种方法才是有效且稳定的。 请注意,在应用批量规范化时,批量大小的选择可能比没有批量规范化时更重要

  1. μ \mu μ小批量的样本均值, σ \sigma σ是小批量的样本标准差。
  2. 应用标准化后,生成的小批量的平均值为0和单位方差为1。
  3. 由于单位方差(与其他一些魔法数)是一个主观的选择,因此我们通常包含 拉伸参数 γ \gamma γ(scale)和偏移参数 β \beta β(shift), γ \gamma γ , β \beta β它们的形状与相同。
  4. 请注意,和是需要与其他模型参数一起学习的参数。
  • 由于在训练过程中,中间层的变化幅度不能过于剧烈,而批量规范化将每一层主动居中,并将它们重新调整为给定的平均值和大小

在这里插入图片描述

  • 我们在方差估计值中添加一个小的常量,以确保我们永远不会尝试除以零,即使在经验方差估计值可能消失的情况下也是如此。

估计值 μ \mu μ σ \sigma σ,通过使用平均值和方差的噪声(noise)估计来抵消缩放问题。 你可能会认为这种噪声是一个问题,而事实上它是有益的。

噪声有益

由于尚未在理论上明确的原因,优化中的各种噪声源通常会导致更快的训练和较少的过拟合:这种变化似乎是正则化的一种形式

另外,批量规范化层在”训练模式“(通过小批量统计数据规范化)和“预测模式”(通过数据集统计规范化)中的功能不同。

  1. 在训练过程中,我们无法得知使用整个数据集来估计平均值和方差,所以只能根据每个小批次的平均值和方差不断训练模型。
  2. 而在预测模式下,可以根据整个数据集精确计算批量规范化所需的平均值和方差。

卷积层规范化

通常,我们将批量规范化层置于全连接层中的仿射变换和激活函数之间。 设全连接层的输入为x,权重参数和偏置参数分别 W W W为和 b b b,激活函数为 ϕ \phi ϕ,批量规范化的运算符为 B N BN BN。 那么,使用批量规范化的全连接层的输出的计算详情如下:
h = ϕ ( B N ( W x + b ) ) h = \phi (BN(Wx+b)) h=ϕ(BN(Wx+b))
均值和方差是根据小批量计算的,所以会随小批量的变化而变化

卷积层

  1. 对于卷积层,我们可以在卷积层之后和非线性激活函数之前应用批量规范化。
  2. 当卷积有多个输出通道时,我们需要对这些通道的“每个”输出执行批量规范化,每个通道都有自己的拉伸(scale)和偏移(shift)参数,这两个参数都是标量。
  3. 假设我们的小批量包含个样本,并且对于每个通道,卷积的输出具有高度p和宽度q。 那么对于卷积层,我们在每个输出通道的 m ∗ p ∗ q m* p* q mpq个元素上同时执行每个批量规范化。
  4. 因此,在计算平均值和方差时,我们会收集所有空间位置的值,然后在给定通道内应用相同的均值和方差,以便在每个空间位置对值进行规范化。

  • 批量规范化在训练模式和预测模式下的行为通常不同。
  1. 首先,将训练好的模型用于预测时,我们不再需要样本均值中的噪声以及在微批次上估计每个小批次产生的样本方差了
  2. 其次,例如,我们可能需要使用我们的模型对逐个样本进行预测。 一种常用的方法是通过移动平均估算整个训练数据集的样本均值和方差,并在预测时使用它们得到确定的输出。 可见,和暂退法一样,批量规范化层在训练模式和预测模式下的计算结果也是不一样的

为了方便起见,我们并不担心在这里自动推断输入形状,因此我们需要指定整个特征的数量

ResNet

target : f*

在这里插入图片描述
在这里插入图片描述

  • 非嵌套函数会远离target(可能是先近后远)
  • 嵌套函数不会发生

残差网络的核心思想是:每个附加层都应该更容易地包含原始函数作为其元素之一。 于是,残差块(residual blocks)便诞生了。

左图虚线框中的部分需要直接拟合出该映射 f ( x ) f(x) f(x),而右图虚线框中的部分则需要拟合出残差映射 f ( x ) − x f(x)-x f(x)x。 残差映射在现实中往往更容易优化

右图虚线框内上方的加权运算(如仿射)的权重和偏置参数设成0,那么 f ( x ) f(x) f(x)即为恒等映射。 实际中,当理想映射 f ( x ) f(x) f(x)极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动。 右图是ResNet的基础架构–残差块(residual block)。 在残差块中,输入可通过跨层数据线路更快地向前传播。

在这里插入图片描述
ResNet沿用了VGG完整的3x3卷积层设计。

  1. 残差块里首先有2个有相同输出通道数的3x3卷积层。
  2. 每个卷积层后接一个批量规范化层和ReLU激活函数。
  3. 然后我们通过跨层数据通路,跳过这2个卷积运算,将输入直接加在最后的ReLU激活函数前。
  4. 这样的设计要求2个卷积层的输出与输入形状一样,从而使它们可以相加。
  5. 如果想改变通道数,就需要引入一个额外的1x1卷积层来将输入变换成需要的形状后再做相加运算。

此代码生成两种类型的网络:

  • 一种是当use_1x1conv=False时,应用ReLU非线性函数之前,将输入添加到输出。
  • 另一种是当use_1x1conv=True时,添加通过卷积调整通道和分辨率。

Inception块 --》残差块

GoogLeNet在后面接了4个由Inception块组成的模块。 ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。 第一个模块的通道数同输入通道数一致。 由于之前已经使用了步幅为2的最大汇聚层,所以无须减小高和宽。 之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。

DenseNet

ResNet将网络函数f分解为两部分( f ( x ) = x + g ( x ) f(x)=x+g(x) f(x)=x+g(x)):一个简单的线性项和一个复杂的非线性项。 那么再向前拓展一步,如果我们想将拓展成超过两部分的信息呢? 一种方案便是DenseNet。

在这里插入图片描述

  • 和残差网络在跨层连接上的主要区别:使用相加和使用连结
    什么是连结?
    在这里插入图片描述
    最后一层与之前的所有层紧密相连
    在这里插入图片描述

稠密网络主要由2部分构成:

稠密块(dense block):定义如何连接输入和输出

过渡层(transition layer): 控制通道数量,使其不会太复杂。


稠密块:

DenseNet使用了ResNet改良版的“批量规范化、激活和卷积”架构

一个稠密块由多个卷积块组成,每个卷积块使用相同数量的输出通道。 然而,在前向传播中,我们将每个卷积块的输入和输出在通道维上连结。

  • 卷积块的通道数控制了输出通道数相对于输入通道数的增长,因此也被称为增长率(growth rate)。

过渡层:

由于每个稠密块都会带来通道数的增加,使用过多则会过于复杂化模型。 而过渡层可以用来控制模型复杂度。 它通过1x1卷积层来减小通道数,并使用步幅为2的平均汇聚层减半高和宽,从而进一步降低模型复杂度。


DenseNet首先使用同ResNet一样的单卷积层和最大汇聚层。

  • 在每个模块之间,ResNet通过步幅为2的残差块减小高和宽,DenseNet则使用过渡层来减半高和宽,并减半通道数

循环神经网络 RNN

  • 核心就是讲究一个预测
  • 这个模型的命名已经说明了数据处理方法,是按顺序按步骤读取的。与人类理解文字的道理差不多,看书都是一个字一个字,一句话一句话去理解的。

在这里插入图片描述
在这里插入图片描述

单词编码

1-of-N Emcoding

在这里插入图片描述

Word Embedding

在这里插入图片描述
在这里插入图片描述

原理

RNN vs CNN

  1. CNN 需要固定长度的输入、输出,RNN 的输入和输出可以是不定长且不等长的
  2. CNN 只有 one-to-one 一种结构,而 RNN 有多种结构,如下图
    在这里插入图片描述
one to one

在这里插入图片描述

one to n

用处:

  1. 从图像生成文字(image caption),此时输入的X就是图像的特征,而输出的y序列就是一段句子,就像看图说话等
  2. 从类别生成语音或音乐等

在这里插入图片描述
在这里插入图片描述

n to one
  • 序列输入(例如,将给定句子分类为表达积极或消极情绪的情感分析)

在这里插入图片描述

n to n

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 每一层都有隐状态(中间状态)和每一层的输出
n to m = Encoder-Decoder
  • 输入、输出为不等长的序列

  • 这种结构是Encoder-Decoder,也叫Seq2Seq,是RNN的一个重要变种。原始的n-to-n的RNN要求序列等长,然而我们遇到的大部分问题序列都是不等长的,如机器翻译中,源语言和目标语言的句子往往并没有相同的长度。为此,Encoder-Decoder结构先将输入数据编码成一个上下文语义向量c:

在这里插入图片描述
语义向量c可以有多种表达方式:

  1. 最简单的方法就是把Encoder的最后一个隐状态赋值给c
  2. 还可以对最后的隐状态做一个变换得到c,
  3. 也可以对所有的隐状态做变换。
  • 拿到c之后,就用另一个RNN网络对其进行解码,这部分RNN网络被称为Decoder。

Decoder的RNN可以与Encoder的一样,也可以不一样。具体做法就是将c当做之前的初始状态输入到Decoder中:
在这里插入图片描述
由于这种Encoder-Decoder结构不限制输入和输出的序列长度,因此应用的范围非常广泛,比如:

  • 机器翻译:Encoder-Decoder的最经典应用,事实上这结构就是在机器翻译领域最先提出的。
  • 文本摘要:输入是一段文本序列,输出是这段文本序列的摘要序列。
  • 阅读理解:将输入的文章和问题分别编码,再对其进行解码得到问题的答案。
  • 语音识别:输入是语音信号序列,输出是文字序列。

Encoder:将 input序列 →转成→ 固定长度的向量
Decoder:将 固定长度的向量 →转成→ output序列
Encoder:与 Decoder 可以彼此独立使用,实际上经常一起使用

Encoder-Decoder 缺点

最大的局限性:

  • 编码和解码之间的唯一联系是固定长度的语义向量c
  • 编码要把整个序列的信息压缩进一个固定长度的语义向量c
  • 语义向量c无法完全表达整个序列的信息
  • 先输入的内容携带的信息,会被后输入的信息稀释掉,或者被覆盖掉
  • 输入序列越长,这样的现象越严重,这样使得在Decoder解码时一开始就没有获得足够的输入序列信息,解码效果会打折扣
RNN的权值共享

在这里插入图片描述

  • 输入和输出序列必须要是等长的。

由于这个限制的存在,经典RNN的适用范围比较小,但也有一些问题适合用经典的RNN结构建模,如:计算视频中每一帧的分类标签。因为要对每一帧进行计算,因此输入和输出序列等长。
输入为字符,输出为下一个字符的概率。这就是著名的Char RNN(详细介绍请参考:The Unreasonable Effectiveness of Recurrent Neural Networks,Char RNN可以用来生成文章,诗歌,甚至是代码,非常有意思)。

char Rnn

我们将给 RNN 一大块文本,并要求它在给定一系列先前字符的情况下,对序列中下一个字符的概率分布进行建模。这将允许我们一次生成一个字符的新文本。

作为一个工作示例,假设我们只有四个可能的字母“helo”的词汇表,并且想在训练序列“hello”上训练 RNN。该训练序列实际上是 4 个独立训练示例的来源:

  1. 给定“h”的上下文,“e”的概率应该很可能。
  2. 在“he”的上下文中,“l”应该是可能的。
  3. “l”也应该有可能给出“hel”的上下文.
  4. “o”应该有可能给出“hell”的上下文。

具体来说,我们将使用 1-of-k 编码将每个字符编码到一个向量中(即除了词汇表中字符索引处的单个 1 外全为 0),并使用函数一次一个地将它们送入 RNN . 然后我们将观察一系列 4 维输出向量(每个字符一维),我们将其解释为 RNN 当前分配给序列中下一个字符的置信度。这是一个图表:

在这里插入图片描述

引入Attention机制

Attention机制通过在每个时间输入不同的c来解决问题,下图是带有Attention机制的Decoder:

在这里插入图片描述

  • 每一个c会自动去选取与当前所要输出的y最合适的上下文信息
  • 具体来说,我们用衡量Encoder中第j阶段的和解码时第i阶段的相关性,最终Decoder中第i阶段的输入的上下文信息 就来自于所有对 的加权和。

例子:

在这里插入图片描述

  • a i j 就是权重,即注意力 a_{ij} 就是权重,即注意力 aij就是权重,即注意力

输入的序列是“我爱中国”,因此,Encoder中的就可以分别看做是“我”、“爱”、“中”、“国”所代表的信息。在翻译成英语时,即假设 a 1 i = [ 0.5 , 0.2 , 0.1 , 0.1 ] a_{1i}= [0.5,0.2,0.1,0.1] a1i=[0.5,0.2,0.1,0.1],第一个上下文(h1)应该和 “我” 这个字最相关,因此对应的 a 11 a_{11} a11就比较大,而相应的 a 12 , a 13 a_{12},a_{13} a12,a13 就比较小。第二个上下文(h2)应该和“爱”最相关,因此对应的 a 22 a_{22} a22 就比较大。最后的h3和“中国”最相关,因此 a 33 , 34 a_{33,34} a33,34的值就比较大。

Attention 从何而来

事实上,同样是从模型中学出的,它实际和Decoder的第 i − 1 i-1 i1阶段的隐状态、Encoder第 j j j个阶段的隐状态有关。

  • a 1 j a_{1j} a1j来自h0和h1这隐状态

在这里插入图片描述

  • a 2 j a_{2j} a2j
    在这里插入图片描述

  • Attention 的优点:

  1. 在机器翻译时,让生词不只是关注全局的语义向量c,增加了“注意力范围”。表示接下来输出的词要重点关注输入序列种的哪些部分。根据关注的区域来产生下一个输出。
  2. 不要求编码器将所有信息全输入在一个固定长度的向量中。
  3. 将输入编码成一个向量的序列,解码时,每一步选择性的从序列中挑一个子集进行处理。
  4. 在每一个输出时,能够充分利用输入携带的信息,每个语义向量不一样,注意力焦点不一样。
  • Attention 的缺点
  1. 需要为每个输入输出组合分别计算attention。50个单词的输出输出序列需要计算2500个attention。
  2. attention在决定专注于某个方面之前需要遍历一遍记忆再决定下一个输出是以什么。

LSTM

  • 传统RNN:
    在这里插入图片描述

  • RNN最大优点就是能利用之前的信息来预测当前内容。比如要对句子 “云朵漂浮在天空” 的最后一个词“天空”进行预测。其实,光靠“云朵”这个词就能明显的知道预测词是“天空”。如下图,“云朵”和“天空”的位置是很近的。这样的操作在短文本里是比较有效实用的。

  • 短期预测:
    在这里插入图片描述
    在这里插入图片描述

但是还有很多是长文本的预测。比如,“我 从小在中国长大,我很喜欢那里的美食……我能说很流利的中文。” 对这个长文本进行最后一个词“中文”的预测。根据附近的文字“说很流利的”,预测词很可能是一个语种,至于是什么语种还需要回归到最初的文本才能确定下来,如下图。然而这个预测词与最初的文本的距离可能是非常大的。

  • 长期预测:
    在这里插入图片描述

然而,预测词与相关文本的间距越大,RNN就越难去获取相关信息。这就是Long-Term Dependencies 问题。

在这里插入图片描述

核心结构
  • 门:是对信息筛选的一种操作,有选择性的让信息流过。“门”由一个sigmoid层(a sigmoid neural net layer)和一个点乘组成

在这里插入图片描述

遗忘门

是用来决定在C线上丢弃哪些信息,由一个叫“forget gate layer”的sigmoid层构成,取决于 h t − 1 h_{t−1} ht1 x t x_t xt,它的输出是一个0-1的数值。比如在文本中,前一个句子的主角是个男性,后一个句子的主角是个女性,那么在前一个句子里获取的性别信息应该舍弃。

在这里插入图片描述

输入门

第二个门是用来确定保留哪些信息。这里由两部分组成

第一个是由一个叫“input gate layer”的sigmoid层来决定更新哪些数值

第二个是一个tanh层来创建一个候选C向量,这个向量可以被加到C线中。这两个的组合就形成了C线信息的更新。就像文本主角的性别信息更新。
在这里插入图片描述

输出门

现在来更新 C t − 1 → C t C_{t−1}→C_t Ct1Ct , f t × C t − 1 f_t×C_{t−1} ft×Ct1
是丢弃信息操作, i t × C t ′ i_t×C^′_t it×Ct是信息更新操作。这样就完成了主角性别信息的更新。

最后我们需要根据C线决定输出内容。

  1. 首先是一个sigmoid层进行输出内容筛选。
  2. 其次使其经过tanh处理再对sigmoid的输出进行点乘,就输出我们决定输出的内容。比如更新的是性别信息是一个名词,但预测词要求可能是一个代词,就需要进行将“男/女”信息转换成“他/她”的预测。

在这里插入图片描述

变种

  • 不仅上一层的隐层加入下一层的预测,把上一层的输出也拿进来参与下一层的决策

在这里插入图片描述

  • 把遗忘门和输入门合并预测,我们不是单独决定要忘记什么以及我们应该向什么添加新信息,而是一起做出这些决定。我们只会忘记什么时候要输入一些东西来代替它。当我们忘记旧的东西时,我们只会向状态输入新值。

在这里插入图片描述

GRU:它将遗忘门和输入门组合成一个“更新门”。它还合并了细胞状态和隐藏状态

  • 遗忘 + 重置
    在这里插入图片描述

RNN + Attention

将注意力集中在给定的信息子集的一部分上。例如,一个 RNN 可以参与另一个 RNN 的输出。在每个时间步,它都关注另一个 RNN 中的不同位置。

在这里插入图片描述
注意力分布通常是由基于内容的注意力产生的。参与的 RNN 生成一个查询,描述它想要关注的内容。每个项目都与查询进行点积以产生分数,描述它与查询的匹配程度。分数被送入 softmax 以创建注意力分布。

在这里插入图片描述

  • RNN 之间注意力的一种用途是翻译[11]. 传统的序列到序列模型必须将整个输入归结为单个向量,然后再将其展开。Attention 通过允许 RNN 处理输入来传递关于它看到的每个单词的信息,然后让 RNN 生成输出以在它们变得相关时关注单词来避免这种情况。

在这里插入图片描述

在这里插入图片描述

  • RNN 之间的这种注意力还有许多其他应用。可用于语音识别[12],允许一个 RNN 处理音频,然后让另一个 RNN 略过它,在生成转录本时专注于相关部分。
    在这里插入图片描述

在这里插入图片描述

后续变种

Convolutional Sequence to Sequence Learning

用CNN取代RNN作机器翻译

对比

RNN是链式结构,CNN是层级结构

  • CNN处理的input受限,需要是等长的文本,fixed size,但是只要往上堆叠层数就能处理更长的文本。
  • 这样的结构能控制最大长度数值。
  • 句子中的每个单词并行运算,不依赖前个词的计算。与RNN的隐藏状态相反。
  • 为了获取更多的信息,使用的是多层的CNN结构。越低层的处理的词在句子中更近,越高层处理的词距更大。
  • 这种层级结构缩短了获取长距离的信息步骤。

CNN处理数据:常量个kernel和非线性处理
RNN: 变量个n步骤,第一个词非线性,最后一个词做单一处理

  • 用CNN做,完全靠卷积,顺带GLU/residual connections/attention

RNN的一般过程:

在这里插入图片描述


注意力机制

自主性的与非自主性的注意力提示解释了人类的注意力的方式

  • “是否包含自主性提示”将注意力机制与全连接层或汇聚层区别开来。

在注意力机制的背景下,自主性提示被称为查询(query)

给定任何查询,注意力机制通过注意力汇聚(attention pooling) 将选择引导至感官输入(sensory inputs,例如中间特征表示)。

感官输入被称为值(value)。 更通俗的解释,每个值都与一个键(key)配对, 这可以想象为感官输入的非自主提示。

可以通过设计注意力汇聚的方式, 便于给定的查询(自主性提示)与键(非自主性提示)进行匹配, 这将引导得出最匹配的值(感官输入)。

在这里插入图片描述
平均汇聚层可以被视为输入的加权平均值, 其中各输入的权重是一样的。
实际上,注意力汇聚得到的是加权平均的总和值, 其中权重是在给定的查询和不同的键之间计算得出的。

通俗理解Attention

在这里插入图片描述
第一步: query 和 key 进行相似度计算(注意力评分函数),得到权值

第二步:将权值进行归一化,得到直接可用的权重

第三步:将权重和 value 进行加权求和

本质思想

在这里插入图片描述

将Source中的构成元素想象成是由一系列的<Key,Value>数据对构成。

此时给定Target中的某个元素Query,通过计算Query和各个Key的相似性或者相关性,得到每个Key对应Value的权重系数,然后对Value进行加权求和,即得到了最终的Attention数值。

所以本质上Attention机制是对Source中元素的Value值进行加权求和,而Query和Key用来计算对应Value的权重系数。即可以将其本质思想改写为如下公式:
在这里插入图片描述


  • 直接平均汇聚的效果不行

  • 利用对汇聚层进行加权,据输入的位置对输出进行加权。

对于任何查询,模型在所有键值对注意力权重都是一个有效的概率分布: 它们是非负的,并且总和为1。 这就印出来了概率分布了

  • 基于高斯核的概率分布函数

在这里插入图片描述

在这里插入图片描述

  • 效果就是高斯核函数进行平滑

  • 高斯核函数可以用参数模型表示,训练过程中学习参数(带参型)

总结:高斯核来对查询和键之间的关系建模。 (10.2.6)中的 高斯核指数部分可以视为注意力评分函数(attention scoring function), 然后把这个函数的输出结果输入到softmax函数中进行运算。 通过上述步骤,将得到与键对应的值的概率分布(即注意力权重)。 最后,注意力汇聚的输出就是基于这些注意力权重的值的加权和。

在这里插入图片描述

注意力评分函数

选择不同的注意力评分函数会导致不同的注意力汇聚操作。

  • 硬性注意力:只关注到某一个位置上的信息

    • 一种是选取最高概率的输入信息;
    • 另一种硬性注意力可以通过在注意力分布式上随机采样的方式实现
    • 硬性注意力的一个缺点是基于最大采样或随机采样的方式来选择信息。因此最终的损失函数与注意力分布之间的函数关系不可导,因此无法使用在反向传播算法进行训练。为了使用反向传播算法,一般使用软性注意力来代替硬性注意力。硬性注意力需要通过强化学习来进行训练
  • 软性注意力:注意力是一个概率分布

(2)另一种硬性注意力可以通过在注意力分布式上随机采样的方式实现。

掩蔽softmax操作

softmax操作用于输出一个概率分布作为注意力权重。 在某些情况下,并非所有的值都应该被纳入到注意力汇聚中。

例如,为了高效处理小批量数据集, 某些文本序列被填充了没有意义的特殊词元。 为了仅将有意义的词元作为值来获取注意力汇聚, 可以指定一个有效序列长度(即词元的个数), 以便在计算softmax时过滤掉超出指定范围的位置。

下面的masked_softmax函数 实现了这样的掩蔽softmax操作(masked softmax operation), 其中任何超出有效长度的位置都被掩蔽并置为0。

考虑由两个2x4的矩阵表示的样本, 这两个样本的有效长度分别为2
和3。 经过掩蔽softmax操作,超出有效长度的值都被掩蔽为0。

加性注意力

在这里插入图片描述

将查询和键连结起来后输入到一个多层感知机(MLP)中, 感知机包含一个隐藏层,其隐藏单元数是一个超参数h。 通过使用 t a n h tanh tanh作为激活函数,并且禁用偏置项。

#@save
class AdditiveAttention(nn.Module):
    """加性注意力"""
    def __init__(self, key_size, query_size, num_hiddens, dropout, **kwargs):
        super(AdditiveAttention, self).__init__(**kwargs)
        self.W_k = nn.Linear(key_size, num_hiddens, bias=False)
        self.W_q = nn.Linear(query_size, num_hiddens, bias=False)
        self.w_v = nn.Linear(num_hiddens, 1, bias=False)
        self.dropout = nn.Dropout(dropout)

    def forward(self, queries, keys, values, valid_lens):
        queries, keys = self.W_q(queries), self.W_k(keys)
        # 在维度扩展后,
        # queries的形状:(batch_size,查询的个数,1,num_hidden)
        # key的形状:(batch_size,1,“键-值”对的个数,num_hiddens)
        # 使用广播方式进行求和
        features = queries.unsqueeze(2) + keys.unsqueeze(1)
        features = torch.tanh(features)
        # self.w_v仅有一个输出,因此从形状中移除最后那个维度。
        # scores的形状:(batch_size,查询的个数,“键-值”对的个数)
        scores = self.w_v(features).squeeze(-1)
        self.attention_weights = masked_softmax(scores, valid_lens)
        # values的形状:(batch_size,“键-值”对的个数,值的维度)
        return torch.bmm(self.dropout(self.attention_weights), values)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

点积注意力

在这里插入图片描述

  • 其实就是softmax一下

缩放点积注意力

使用点积可以得到计算效率更高的评分函数, 但是点积操作要求查询和键具有相同的长度d。

假设查询和键的所有元素都是独立的随机变量, 并且都满足零均值0和单位方差d, 那么两个向量的点积的均值为
0,方差为d。 为确保无论向量长度如何, 点积的方差在不考虑向量长度的情况下仍然是1, 我们再将点积除以 d 1 / 2 d^{1/2} d1/2, 则缩放点积注意力(scaled dot-product attention)评分函数为:
在这里插入图片描述

在这里插入图片描述

  • Key != Value:当查询和键是不同长度的矢量时(键值对的形式),可以使用可加性注意力评分函数。
  • Key = Value:当它们的长度相同时,使用缩放的“点-积”注意力评分函数的计算效率更高。

在这里插入图片描述

Bahdanau注意力

机器翻译问题: 通过设计一个基于两个循环神经网络的编码器-解码器架构, 用于序列到序列学习。 具体来说,循环神经网络编码器将长度可变的序列转换为固定形状的上下文变量, 然后循环神经网络解码器根据生成的词元和上下文变量 按词元生成输出(目标)序列词元。 然而,即使并非所有输入(源)词元都对解码某个词元都有用, 在每个解码步骤中仍使用编码相同的上下文变量。 有什么方法能改变上下文变量呢?

在预测词元时,如果不是所有输入词元都相关,模型将仅对齐(或参与)输入序列中与当前预测相关的部分。这是通过将上下文变量视为注意力集中的输出来实现的。

上下文变量c 在任何解码时间步都会被 t ′ t' t替换。 假设输入序列中有T个词元, 解码时间步 c t ′ c_{t^{'}} ct的上下文变量是注意力集中的输出:
在这里插入图片描述

  • 注意力评分函数是加性注意力
    在这里插入图片描述

多头注意力

当给定相同的查询、键和值的集合时, 我们希望模型可以基于相同的注意力机制学习到不同的行为, 然后将不同的行为作为知识组合起来, 捕获序列内各种范围的依赖关系 (例如,短距离依赖和长距离依赖关系)。 因此,允许注意力机制组合使用查询、键和值的不同 子空间表示(representation subspaces)可能是有益的。

  • 与其只使用单独一个注意力汇聚, 我们可以用独立学习得到的h组不同的 线性投影(linear projections)来变换查询、键和值。
  • 然后,这h组变换后的查询、键和值将并行地送到注意力汇聚中。 最后,将这h个注意力汇聚的输出拼接在一起, 并且通过另一个可以学习的线性投影进行变换, 以产生最终输出。
  • 对于h个注意力汇聚输出,每一个注意力汇聚都被称作一个头(head)。

在这里插入图片描述

在这里插入图片描述

自注意力

在深度学习中,经常使用卷积神经网络(CNN)或循环神经网络(RNN)对序列进行编码。 想象一下,有了注意力机制之后,我们将词元序列输入注意力池化中, 以便同一组词元同时充当查询、键和值。 具体来说,每个查询都会关注所有的键-值对并生成一个注意力输出。 由于查询、键和值来自同一组输入,因此被称为 自注意力(self-attention)

在这里插入图片描述
在这里插入图片描述

  • 考虑一个卷积核大小为k的卷积层。 目前只需要知道的是,由于序列长度是n,输入和输出的通道数量都是d, 所以卷积层的计算复杂度为 O ( k n d 2 ) O(knd^2) O(knd2)
  • 当更新循环神经网络的隐状态时, d x d dxd dxd权重矩阵和d维隐状态的乘法计算复杂度为 O ( d 2 ) O(d^2) O(d2)。 由于序列长度为n,因此循环神经网络层的计算复杂度为 O ( n d 2 ) O(nd^2) O(nd2)
  • 在自注意力中,查询、键和值都是 n x d nxd nxd矩阵。 考虑 (10.3.5)中缩放的”点-积“注意力, 其中 n x d nxd nxd矩阵乘以 d x n dxn dxn矩阵。 之后输出的 n x n nxn nxn矩阵乘以 n x d nxd nxd矩阵。 因此,自注意力具有 O ( n 2 d ) O(n^2d) O(n2d)计算复杂性。
位置编码
  • 在处理词元序列时,循环神经网络是逐个的重复地处理词元的, 而自注意力则因为并行计算而放弃了顺序操作。
  • 为了使用序列的顺序信息,通过在输入表示中添加 位置编码(positional encoding)来注入绝对的或相对的位置信息。
  • 位置编码可以通过学习得到也可以直接固定得到。

在位置嵌入矩阵P中, 行代表词元在序列中的位置,列代表位置编码的不同维度。

Why

对于任何一门语言,单词在句子中的位置以及排列顺序是非常重要的,它们不仅是一个句子的语法结构的组成部分,更是表达语义的重要概念。一个单词在句子的位置或排列顺序不同,可能整个句子的意思就发生了偏差。即,词序信息

  • Transformer模型抛弃了RNN、CNN作为序列学习的基本模型。我们知道,循环神经网络本身就是一种顺序结构,天生就包含了词在序列中的位置信息。当抛弃循环神经网络结构,完全采用Attention取而代之,这些词序信息就会丢失,模型就没有办法知道每个词在句子中的相对和绝对的位置信息。因此,有必要把词序信号加到词向量上帮助模型学习这些信息,位置编码(Positional Encoding)就是用来解决这种问题的方法。
What

位置编码(Positional Encoding)是一种用词的位置信息对序列中的每个词进行二次表示的方法,另一种表示位置的方法。

How
  1. 一种做法就是分配一个0到1之间的数值给每个时间步,其中,0表示第一个词,1表示最后一个词。这种方法虽然简单,但会带来很多问题。其中一个就是你无法知道在一个特定区间范围内到底存在多少个单词。换句话说,不同句子之间的时间步差值没有任何的意义。

  2. 另一种做法就是线性分配一个数值给每个时间步。也就是,1分配给第一个词,2分配给第二个词,以此类推。这种方法带来的问题是,不仅这些数值会变得非常大,而且模型也会遇到一些比训练中的所有句子都要长的句子。此外,数据集中不一定在所有数值上都会包含相对应长度的句子,也就是模型很有可能没有看到过任何一个这样的长度的样本句子,这会严重影响模型的泛化能力。

  3. 一种好的位置编码方案需要满足以下几条要求:

    • 它能为每个时间步输出一个独一无二的编码;
    • 不同长度的句子之间,任何两个时间步之间的距离应该保持一致;
    • 模型应该能毫不费力地泛化到更长的句子。它的值应该是有界的;
    • 它必须是确定性的。

Transformer的作者们提出了一个简单但非常创新的位置编码方法,能够满足上述所有的要求。首先,这种编码不是单一的一个数值,而是包含句子中特定位置信息的d维向量(非常像词向量)。第二,这种编码没有整合进模型,而是用这个向量让每个词具有它在句子中的位置的信息。换句话说,通过注入词的顺序信息来增强模型输入。

在这里插入图片描述

  • 位置编码方法已经有了,那如何让每个词具有它们的位置信息?

在这里插入图片描述

  • 其实就是,把原来的词的embedding当中再加入位置编码
    在这里插入图片描述
    key point: 正弦曲线函数的位置编码的另一个特点是,它能让模型毫不费力地关注相对位置信息。

位置编码文章的参考链接

Transformer

在这里插入图片描述

编码器

Transformer的编码器是由多个相同的层叠加而成的,每个层都有两个子层(子层表示为sublayer)。

第一个子层是多头自注意力(multi-head self-attention)汇聚;
第二个子层是基于位置的前馈网络(positionwise feed-forward network)。

在计算编码器的自注意力时,查询、键和值都来自前一个编码器层的输出。受残差网络的启发,每个子层都采用了残差连接(residual connection)。在Transformer中,对于序列中任何位置的任何输入 x ∈ R d x \in R^d xRd,都要求满足 s u b l a y e r ( x ) ∈ R d sublayer(x)\in R^d sublayer(x)Rd,以便残差连接满足 x + s u b l a y e r ( x ) x+sublayer(x) x+sublayer(x)

在残差连接的加法计算之后,紧接着应用层规范化(layer normalization)

  • 因此,输入序列对应的每个位置,Transformer编码器都将输出一个d维表示向量。
解码器

Transformer解码器也是由多个相同的层叠加而成的,并且层中使用了残差连接和层规范化。

除了编码器中描述的两个子层之外,解码器还在这两个子层之间插入了第三个子层,称为编码器-解码器注意力(encoder-decoder attention)层。

  • 在编码器-解码器注意力中,三个输入,查询来自前一个解码器层的输出,而键和值来自整个编码器的输出。

在解码器自注意力中,查询、键和值都来自上一个解码器层的输出。但是,解码器中的每个位置只能考虑该位置之前的所有位置。

这种掩蔽(masked)注意力保留了自回归(auto-regressive)属性,确保预测仅依赖于已生成的输出词元。

基于位置的前馈网络

基于位置的前馈网络对序列中的所有位置的表示进行变换时使用的是同一个多层感知机(MLP),这就是称前馈网络是基于位置的(positionwise)的原因。

在下面的实现中,输入X的形状(批量大小,时间步数或序列长度,隐单元数或特征维度)将被一个两层的感知机转换成形状为(批量大小,时间步数,ffn_num_outputs)的输出张量。

class PositionWiseFFN(nn.Module):
    """基于位置的前馈网络"""
    def __init__(self, ffn_num_input, ffn_num_hiddens, ffn_num_outputs,
                 **kwargs):
        super(PositionWiseFFN, self).__init__(**kwargs)
        self.dense1 = nn.Linear(ffn_num_input, ffn_num_hiddens)
        self.relu = nn.ReLU()
        self.dense2 = nn.Linear(ffn_num_hiddens, ffn_num_outputs)

    def forward(self, X):
        return self.dense2(self.relu(self.dense1(X)))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 改变张量的最里层维度的尺寸,会改变成基于位置的前馈网络的输出尺寸。因为用同一个多层感知机对所有位置上的输入进行变换,所以当所有这些位置的输入相同时,它们的输出也是相同的。
残差连接和层规范化

在一个小批量的样本内基于批量规范化对数据进行重新中心化和重新缩放的调整。层规范化和批量规范化(BN)的目标相同,但层规范化是基于特征维度进行规范化。尽管批量规范化在计算机视觉中被广泛应用,但在自然语言处理任务中(输入通常是变长序列)批量规范化通常不如层规范化的效果好。

  • 残差连接要求两个输入的形状相同,以便加法操作后输出张量的形状相同。
编码&解码

在训练阶段,其输出序列的所有位置(时间步)的词元都是已知的;然而,在预测阶段,其输出序列的词元是逐个生成的。因此,在任何解码器时间步中,只有生成的词元才能用于解码器的自注意力计算中。为了在解码器中保留自回归的属性,其掩蔽自注意力设定了参数dec_valid_lens,以便任何查询都只会与解码器中所有已经生成词元的位置(即直到该查询位置为止)进行注意力计算。

代码经验

非图像数据

input shape: (sampleNum,indim)

那么第一层神经元:(indim, neuralNum1)

下层神经元:(neuarlNum1, neuarlNum2)

图像数据

图像的形状:(批量大小、通道、高度、宽度) = (Batchsize, channel, h, w)

如果是多通道输入输出:(in_channel, out_channel, h, w)

在这里插入图片描述
图像尺寸的变化:最开始:(60000,784)
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/93748
推荐阅读
相关标签
  

闽ICP备14008679号