第 3 章 经典卷积神经网络 在过去十多年里,深度学习领域经历了飞速的发展,尤其是卷积神经网络 的不断创新,涌生出诸如AlexNet[30]、VGGNet[32]、GoogleNet[33]、ResNet[34]和 t[47]等一系列经典模型。这些模型在问世之初, DenseNe均在图像分类任务中表 现卓越,奠定了卷积神经网络发展的坚实基础。而后续的GAN[14]以及 Transformer[48]网络则在结构上相比传统神经网络又做出了重大创新,为卷积 神经网络开辟了两个新的发展方向。至今这两种模型的先进思想仍然被用于 许多新的图像和视频处理神经网络上。为了更全面地理解卷积神经网络,本章 介绍这些经典网络的基本结构并探讨相关细节。 ..3.eNt 1 Alxe 3.1.1 AlexNet的网络结构 2012 年,AlexNet在ImageNet图像分类竞赛中一举夺冠,相较于上一年的 冠军模型,AlexNet将错误率降低了近10 个百分点。AlexNet不仅在网络结构 上做出了重要创新,还提出了Dropout正则化方法和ReLU 激活函数,这些突 破为计算机视觉领域开启了一个新的篇章。 AlexNet网络由多个卷积层、池化层和全连接层构成。具体而言,AlexNet 首先使用了1个11×11 的卷积层,随后接入2个3×3 的池化层。接下来,网络 依次采用了1个5×5 的卷积层和3个3×3 的卷积层。需要注意的是,这3个 3×3 的卷积层每次输出的尺寸均保持不变。在特征图进入全连接层之前,需对 其进行扁平化处理。最终,全连接层输出一个包含 N 个类别的向量,通过 Softmax函数对各类别进行概率分类,依据最大概率值确定最终分类结果。其 结构如图3-1所示。 从图3-1中可以看到,AlexNet网络首先使用一张尺寸为227×227×3 的 图片作为输入。在AlexNet的原始论文中,输入图像的尺寸为224×224×3,但 227×227 的尺寸在实际应用中效果更好。网络的第一层使用了96 个11×11 的卷积核,步幅为4,因此输出的尺寸缩小到55×55,大约缩小了4倍。接下来, 网络使用1个3×3 的过滤器构建最大池化层,步幅为2,使得尺寸进一步缩小 48 深度学习技术基础(微课版) 图3-1 AlexNet的网络结构 至27×27×96。接着,网络执行了1个5×5的卷积操作,经过填充后,输出尺寸为27× 27×256。然后,再次进行最大池化,输出尺寸缩小至13×13。接下来的两层继续使用相 同的3×3卷积核和相同的填充策略,输出尺寸保持为13×13,但滤波器数量增加至384。 再进行一次相同的卷积操作,最后通过一次最大池化操作,最终输出尺寸为6×6×256。 将6×6×256的特征图展开为9216个单元,并依次通过全连接层进行处理。最后,网络 使用Softmax函数输出分类结果,从1000个可能的对象中确定最终类别。 3.1.2 AlexNet的改进 相比于此前的深度神经网络,AlexNet引入了多项关键改进。例如,网络使用了 ReLU 作为激活函数。与Sigmoid和Tanh函数不同,ReLU 能够有效缓解在训练过程中 因饱和区导致的梯度消失问题。同时,ReLU 通过使部分神经元失活,增加了网络的稀疏 性,从而降低了过拟合的风险。此外,AlexNet原文中提出的Dropout技术也是为了增加 网络的稀疏性。由于Dropout在训练时随机失活部分神经元,每一轮训练的神经网络结 构都会有所不同,这种随机性不仅增强了模型的泛化能力,还能避免部分神经元在训练过 程中始终无法得到充分训练的问题,进而提高了模型的学习能力。 除了这些改进,AlexNet还引入了局部响应归一化(LocalResponseNormalization, LRN)[30]技术,有效促进了模型的泛化能力。在实验中,LRN 显著提高了模型的准确率。 LRN 的计算如式(3-1)所示: di x,y =ci x,y/k +a Σ min(N-1,i+n/2) j=max(0,i-n/2)(cj ( x,y ))b (3-1) 其中,di x,y 为归一化后的输出,表示通道i 上坐标(x,y)处的激活值;ci x,y 为归一化前的输 入,表示通道i 上坐标(x,y)处的激活值。k、a、b 为常数超参数,k 起到平滑作用,防止分 母为零;a 控制归一化的强度;b 控制归一化的指数。N 表示输入特征图的通道总数。n 表示局部归一化的窗口大小。另外,在AlexNet中的池化操作中,采用的步幅小于卷积 第3章经典卷积神经网络49 核的大小。这种重叠的池化层设计在实验中被证明能够有效降低过拟合风险。 在训练中作者对256×256大小的RGB图像进行随机224×224的裁剪和水平翻转, 同时也改变训练图片中RGB通道的强度。具体做法是,首先对图像的3个通道进行变形 操作,将其转换为一个矩阵。该矩阵的列数等于通道数,行数为每个通道矩阵边长的平 方。接着,使用主成分分析(PrincipalComponentAnalysis,PCA)[49]对每个通道进行归 一化处理,即减去每列的均值,然后乘以矩阵的转置,并除以每个通道矩阵维度的平方减 1,从而得到协方差矩阵。然后,分别计算协方差矩阵的特征值和特征向量。最终,对每个 通道的所有像素加上一个值,该值由式(3-2)得到: [P1,P2,P3][a1λ1,a2λ2,a3λ3]T (3-2) 其中,Pi表示第i个主成分向量;λi表示与第i个主成分Pi对应的特征值;ai是第i个 N(0,0.1)高斯分布中采样得到的随机值。这样计算就得到了1个(3×1)的向量,即加在 每个像素的RGB3个通道上的值。 ..3.2VGGNet3.2.1VGGNet的网络结构 VGGNet是由牛津大学和谷歌公司于2014年提出的一种深度卷积神经网络。它在 AlN主要体现在两方面:使用3×3卷积核代替AlN exet的基础上进行了重要的改进, exet中 的大卷积核;其次,它采用池化核代替AlexNet的3×3池化核。VGGNet有多种不同层次的网 络结构,论文中提出了6种不同层次的网络结构,从 1 层~19层不等。其中,VGG19结构以 及其他VGGNet的结构参数如表3-1所示。从表3-1中可以看出,4种VGGNet都由5个 VGG模块和5个最大池化层构成,每一个VGG模块都包含多个相同的卷积层。而每个最大 池化层操作将前一层的输出特征缩减一半。VGG19网络结构如图3-2所示。 表3- 1 VGG11~VGG19的网络结构参数表 VGG11~VGG19网络结构参数 A A-LRN B C D E 11层11层13层16层16层19层 input(224×224RGBimage) conv3-64 conv3-64 LRN conv3-64 conv3-64 conv3-64 conv3-64 conv3-64 conv3-64 conv3-64 conv3-64 最大池化层 conv3-128 conv3-128 conv3-128 conv3-128 conv3-128 conv3-128 conv3-128 conv3-128 conv3-128 conv3-128 最大池化层 conv3-256 conv3-256 conv3-256 conv3-256 conv3-256 conv3-256 conv3-256 conv3-256 conv1-256 conv3-256 conv3-256 conv3-256 conv3-256 conv3-256 conv3-256conv3-256 续表 50深度学习技术基础(微课版) VGG11~VGG19网络结构参数 最大池化层 conv3-512 conv3-512 conv3-512 conv3-512 conv3-512 conv3-512 conv3-512 conv3-512 conv1-512conv3-512 conv3-512 conv3-512conv3-512 conv3-512 conv3-512conv3-512 最大池化层 最大池化层 FC-4096 FC-4096 FC-1000 Softmax conv3-512 conv3-512 conv3-512 conv3-512 conv3-512 conv3-512 conv3-512 conv3-512 conv1-512conv3-512 conv3-512 conv3-512conv3-512 conv3-512 conv3-512conv3-512 图3- 2 VGG19 网络结构 3.2 VGGNt的特点 2.e VGGNet相比于AlexNet的最大特点是使用较小的卷积核(3×3)替代较大的卷积 核。具体而言,2个3×3 卷积堆叠相当于1个5×5 卷积,而3个3×3 堆叠相当于1个 7×7 卷积,感受野大小不变。例如,如果步长为1,填充为0,那么2个3×3 卷积后的图 像尺寸为((( N -3)/1+1)-3)/1+1=(( N -3+1)-3+1)= N -4=( N -5)/1+1 。 且做卷积后得到的特征,都是从原图像上相同的像素点提取的(原图像每5×5 的空域像 第3章经典卷积神经网络51 素点对应一个新的特征), 因此感受野大小不变。故2个3×3 的卷积核等效于5×5 的卷 积核。感受野计算公式如式(3-3): F(i)=(F(i+1)-1)×S+K(3-3) 其中,K代表卷积核大小;S表示步幅。由于用多个小卷积替代大卷积,故使用了多个非 线性激活函数,每个VGGNet在经过所有卷积池化后还有3个全连接层,在全连接层中 间采用Dropout来防止过拟合。最后会通过Softmax输出各类概率。 VGGNet主要通过对图像重新缩放并随机裁剪到224×224 的尺寸,此外还会对图像 进行随机水平翻转和随机RGB 来进行数据增强。在训练时,需要注意对前4层卷积层和 后3层进行初始化,而中间层则采用随机初始化。 然而,VGGNet主要是通过增加卷积网络深度来提升性能,因此在不断增加 VGGNet层数的过程中,可能会出现性能退化、内存占用增大,以及梯度消失或梯度爆炸 等问题。 ..3.3GoogLeNet3.3.1Inception结构 GoogLeNet与VGGNet均诞生于2014 年。在同年的ImageNet比赛中,GoogLeNet 凭借更低的错误率获得了第一名,相较于VGGNet表现更加出色。与AlexNet相比, GoogLeNet不仅参数更少,同时也提高了准确率。与VGGNet相比,GoogLeNet不仅在 深度上有所增加,还特别注重了网络宽度的影响,同时显著降低了参数量。GoogLeNet 的核心创新在于引入了Inception结构,通过使用不同大小的卷积核进行降维,从而提高 计算效率[43]。此外,GoogLeNet还引入了两个辅助分类器来辅助训练,去除了中间的全 连接层,采用了平均池化层,并减少了Dropout的数量。GoogLeNet在后续的研究中经 历了多次改进,先后推出了多个版本,主要区别在于Inception结构的演进,分别为V1 、 V2 、V3 、V4 以及结合ResNet的版本。 Inception结构通过使用3个不同大小的卷积核进行扫描和卷积运算,同时结合一个 最大池化操作。最终,这4部分的输出按通道拼接,然后传递至下一层。具体来说,第1 个分支为1×1 的卷积层,步幅为1;第2个分支为3×3 的卷积层,步幅为1,并设置填充 为1;第3个分支为5×5 的卷积层,步幅为1,填充设置为2;第4个分支为3×3 的最大池 化层,步幅为1,填充为1。读者可以根据需要对这些参数进行调整,但所有分支的输出特 征矩阵尺寸需与输入特征矩阵尺寸相等。为了避免直接使用Inception结构时计算量过 大,通常会在3个卷积层前加入1个1×1 的卷积层进行降维,并在池化层之后再加入1 个1×1 的卷积层。Inception结构如图3-3所示,而GoogLeNet整体由多个Inception模 块构成,其详细结构如表3-2所示。 52深度学习技术基础(微课版) 图3-3Inception结构 表3-2GoogLeNet各层通道及尺寸 网络层 卷积核 大小/步幅 输出尺寸depth#1×1#3×3reduce#3×3#5×5reduce#5×5poolproj 参数量flopsconvolution 7×7/2 112×112×64 1 2.7K 34M maxpool 3×3/2 56×56×64 0 convolution 3×3/1 56×56×192 2 64 192 112K360M maxpool 3×3/2 28×28×192 0 inception(3a) 28×28×256 2 64 96 128 16 32 32 159K 28M inception(3b) 28×28×480 2 128 128 192 32 96 64 380K304M maxpool 3×3/2 14×14×480 0 inception(4a) 14×14×512 2 192 96 208 16 48 64 364K 73M inception(4b) 14×14×512 2 160 112 224 24 64 64 437K 88M inception(4c) 14×14×512 2 128 128 256 24 64 64 463K100M inception(4d) 14×14×528 2 112 14428832 64 64 580K119M inception(4e) 14×14×832 2 256 160 320 32 128 128 840K170M maxpool 3×3/2 7×7×832 0 inception(5a) 7×7×832 2 256 160 320 32 128 1281072K54M inception(5b) 7×7×1024 2 384 192 384 48 128 1281388K71M avgpool 7×7/1 1×1×1024 0 dropout(40%) 1×1×1024 0 linear 1×1×10001 1000K 1M Softmax 1×1×10000 第3章经典卷积神经网络53 可以看到,每个Inception模块的深度均为2,即各卷积操作重复两次。可以通过一 个简单的比较来理解其效果:假设没有使用1×1卷积进行降维处理,最终输出的特征图 维度将为64+128+32+192=416层;而加入1×1卷积后,特征图的维度则为64+ 128+32+32=256层。由此可以看出,经过1×1卷积处理后,特征图的维度显著减少。 感兴趣的读者还可以进一步计算卷积操作的计算次数。加入1×1卷积后,计算量几乎减 少为原来的十分之一。这种Inception结构的设计不仅降低了计算量,还提升了网络的效 率和性能。 3.3.2辅助分类器 为了缓解深层网络训练中的梯度消失问题,可以在Inception(4a)和Inception(4d)处 分别添加一个辅助分类器。这个辅助分类器通过池化、卷积和全连接层进行处理,最后执 行Softmax操作以计算分类概率。在训练过程中,辅助分类器的损失函数会被加权后合 并到总损失函数中。研究发现,辅助分类器在训练早期并没有显著提升收敛速度:在模 型尚未达到高精度时,两种网络的训练进度几乎相同;然而,接近训练结束时,带有辅助分 支的网络开始表现出更高且更稳定的准确性。因此,辅助分类器的主要作用是作为正则 化手段,有助于防止过拟合。 ..3.4残差网络 在神经网络的设计中,随着层数的加深,模型性能可能会下降。例如,在实验中, VGGNet在达到19层时,进一步增加层数反而导致性能下降。过度增加层数会引发梯度 爆炸和梯度消失的问题,最终导致网络性能饱和甚至退化,进而导致过拟合并降低泛化能 力[50]。为了应对深层网络中的退化问题,研究者提出了一种新方法,即人为地让神经网 络的某些层跳过下一层神经元的连接,直接与更深层的神经元相连。这种弱化层间强联 系的结构被称为ResNet。 ResNet的核心创新之一是引入了跳跃连接,形成残差结构。直观上,这意味着将多 个卷积网络基本单元的输入与输出相加,以产生新的输出。常见的残差结构有两种类型: 一种是由2个3×3卷积层组成的双层BasicBlock;另一种是由3×3卷积层和1×1卷积 层组成的3层BotleNeck。在这两种结构中,ReLU激活函数用于连接每一层的前后网 络。它们的结构如图3-4所示。 事实上,BotleNeck是对BasicBlock的一种改进,以减少参数数量。BotleNeck结 构中的前后1×1卷积主要用于先降低维度,然后再恢复到原始输入数据的维度。通过计 算可以得出,BotleNeck的参数数量为1×1×256×64+3×3×64×64+1×1×256×64= 69632,而BasicBlock的参数数量则为256×256×3×3×2=1179648 。显然,BasicBlock 的参数数量远远超过了BotleNeck。因此,在更深层的网络中,BotleNeck的结构更为适 用,例如ResNet50 。而BasicBlock则更适合应用于ResNet18等层数相对较少的残差网 络中。 带有残差结构的网络与传统网络的本质区别在于,前者拟合的是残差,而后者则是直 54深度学习技术基础(微课版) 图3-4残差结构示例 接拟合目标值。残差相对更容易学习,当上层网络接近最优值时,带有Shortcut连接的 ResNet网络相比没有Shortcut连接的网络更容易进行调整[51]。同时,当误差已经非常 小时,残差网络能够放大误差,这不仅增大了优化空间,还有效避免了优化过程趋于饱和, [3 从而解决了梯度消失问题47]。可以通过残差网络的前向传播公式(-4)对这一现象进行 更深入的分析: F(=δ(x))+Wx 34) x)f1(f2(( 其中,f1 表示第一层卷积; δ 表示激活函数; f2 表示第二层; W 为线性变化矩阵。假设目 01, 02, 标值为10.而在某轮训练后,非残差网络的输出值为10.此时的变化幅度非常小,几 乎可以忽略不计。然而,对于残差网络,由于输出由两部分构成,即输入直接连接和残差 部分,假设输入部分为10, x) 02调整到0.这意味着残差部分的变 那么此时F(需从0.01, 化率达到了100%,仍有非常大的优化空间,远未达到饱和状态。某种程度上,ResNet通 过放大相对误差,使得优化过程不易陷入饱和状态,从而有效避免了因网络层数过多导致 的过拟合问题。在理想情况下,当输入部分已与目标值相符时,网络可以直接令F(x)为 0,相当于跳过了中间的网络层,从而节省了计算时间和资源。而在非残差网络中,即便输 出已与目标值相等,仍需继续将其输入下一层进行训练,这往往会打破原先的最优状态, 不仅可能引发过拟合,还会浪费计算时间和资源。 ResNet完整结构主要由多个BasicBlock或BotleNeck模块构成,基于BasicBlock 的ResNet如图3-5所示。可以从图中发现在不同BasicBlock之间的shortcut为虚线,这 是因为在不同BasicBlock之间特征矩阵尺寸大小会减小一半,由于 x 与F(x)需要具有 相同的维度,因此 x 需要通过1×1卷积层进行降维。其他ResNet的网络结构参数如 表3-3所示。 第3章 经典卷积神经网络 55 图3-5 ResNet18网络结构 表3-3 其他ResNet的网络结构参数 网络层输出大小18层34层50层101层152层 conv1 112×112 64个7×7卷积核,步幅为2 conv2_x 56×56 3×3最大池化,步幅为2 3×3,64 3×3,64 [ ]×3 3×3,64 3×3,64 [ ]×3 1×1,64 3×3,64 1×1,256 é . êêê ù . úúú ×3 1×1,64 3×3,64 1×1,256 é . êêê ù . úúú ×3 1×1,64 3×3,64 1×1,256 é . êêê ù . úúú ×3 续表 56 深度学习技术基础(微课版) 网络层输出大小18层34层50层101层152层 conv3_x 28×28 3×3,128 3×3,128 [ ]×2 3×3,128 3×3,128 [ ]×4 1×1,128 3×3,128 1×1,512 é . êêê ù . úúú ×4 1×1,128 3×3,128 1×1,512 é . êêê ù . úúú ×4 1×1,128 3×3,128 1×1,512 é . êêê ù . úúú ×8 conv4_x 14×14 3×3,256 3×3,256 [ ]×2 3×3,256 3×3,256 [ ]×6 1×1,256 3×3,256 1×1,1024 é . êêê ù . úúú ×6 1×1,256 3×3,256 1×1,1024 é . êêê ù . úúú ×23 1×1,256 3×3,256 1×1,1024 é . êêê ù . úúú ×36 conv5_x 7×7 3×3,512 3×3,512 [ ]×2 3×3,512 3×3,512 [ ]×3 1×1,512 3×3,512 1×1,2048 é . êêê ù . úúú ×3 1×1,512 3×3,512 1×1,2048 é . êêê ù . úúú ×3 1×1,512 3×3,512 1×1,2048 é . êêê ù . úúú ×3 1×1 平均池化,输出维度为1000的线性网络层,用Softmax进行分类 FLOPs 1.8×109 3.6×109 3.8×109 7.6×109 11.3×109 .. 3.5 密集连接网络 DenseNet在2016年由华人学者黄俊等提出,并在CVPR2017上获得最佳论文奖。 这种深度卷积神经网络借鉴了ResNet的残差结构,但通过密集连接进一步提升了网络 性能。DenseNet的核心思想是让每一层网络直接接收前面所有层的特征图,并将其输出 作为后续所有层的输入。这种密集连接方式使得网络能够更充分地利用浅层特征,从而 提升整体表现。DenseNet的结构与ResNet有所不同。ResNet的残差结构通常应用于 两到三层的网络中,而DenseNet则将每一层的输入与之前所有层的输出按通道维度进 行拼接,形成新的输入,如图3-6所示。 图3-6 DenseNet与ResNet的差别 第3章 经典卷积神经网络 57 DenseNet的关键组件包括DenseBlock(Dense块)和Transition 层。一个DenseBlock 包含多个BottleNeck结构,每个BottleNeck依次通过批归一化、ReLU 激活函数、1×1卷积 层和3×3卷积层处理输入数据。通过使用1×1卷积层,DenseNet能够有效减少参数量和 通道数量。例如,可以将每个BottleNeck的输出通道数从32减至4。DenseNet-121~ DenseNet-264的结构参数如表3-4所示。DenseNet的基本网络结构如图3-7所示。 表3-4 DenseNet的结构参数 网络层输出大小DenseNet-121 DenseNet-169 DenseNet-201 DenseNet-264 卷积112×112 7×7卷积,步幅为2 池化56×56 3×3最大池化,步幅为2 密集块 (1) 56×56 1×1卷积 3×3卷积[ ]×6 1×1卷积 3×3卷积[ ]×6 1×1卷积 3×3卷积[ ]×6 1×1卷积 3×3卷积[ ]×6 过渡层 (1) 56×56 1×1卷积 28×28 2×2平均池化,步幅为2 密集块 (2) 28×28 1×1卷积 3×3卷积[ ]×12 1×1卷积 3×3卷积[ ]×12 1×1卷积 3×3卷积[ ]×32 1×1卷积 3×3卷积[ ]×12 过渡层 (2) 28×28 1×1卷积 14×14 2×2最大池化,步幅为2 密集块 (3) 14×14 1×1卷积 3×3卷积[ ]×24 1×1卷积 3×3卷积[ ]×32 1×1卷积 3×3卷积[ ]×48 1×1卷积 3×3卷积[ ]×64 过渡层 (3) 14×14 1×1卷积 7×7 2×2最大池化,步幅为2 密集块 (4) 7×7 1×1卷积 3×3卷积[ ]×16 1×1卷积 3×3卷积[ ]×32 1×1卷积 3×3卷积[ ]×32 1×1卷积 3×3卷积[ ]×48 分类层1×1 7×7全局平均池化 全连接层输出维度为1000,用Softmax进行分类 图3-7 DenseNet的基本网络结构 Transition层在DenseNet中起到压缩模型的作用。其结构包括批归一化、ReLU 激 活函数、1×1卷积以及2×2平均池化。首先,通过1×1卷积减少输出通道的数量,然后 通过2×2平均池化层降低特征图的尺寸。这样一来,在保证连接层数大幅增加的同时, 58深度学习技术基础(微课版) 模型的参数不会急剧增加,从而有效防止过拟合的发生。 ..3.6生成对抗网络 3.6.1生成对抗网络概述 生成对抗网络(GenerativeAdversarialNetwork,GAN)是由IanGoodfellow等于 2014年提出的一个经典深度学习模型,对图像和视频生成等任务具有重要意义。除了生 成图像,GAN还在图像质量提升、图像风格化、图像着色以及人脸生成等任务中发挥重 要作用。GAN主要由生成网络和判别网络组成,生成网络输入的是随机噪声并输出一 个假图像,而判别网络则是用来判别生成图像的真假,输出的是输入图片为真实图片的概 率,比如当概率为1则一定为真实图片,如图3-8所示。但是在这两个网络共同训练时, 判别网络对生成网络为真实图片的预估概率为0.5时则为最佳,此时意味着判别网络已 经无法辨别,生成网络的图像已接近于真实图片,这两个网络的训练趋于平衡状态。 图3- 8 GAN 的基本结构 3.2 生成对抗网络训练过程 6. GAN可以从理论上证明其存在全局最优值,但是由于事先无法确定真实图片分布 函数和生成网络训练出来的假图片分布函数,因此需要用式(3-5)替代。 gD(xi) + g(1-D(G(i))) (5) S= 1lo1loz3 mi== 其中,D(为判别函数;z)Σ(m) 成函数;Σ实(1) (m) z 为噪声数据。默认情况下, x) G(为生(1) x为真(m) 数据;对数函 数的底数通常是自然数。训练目标是使该表达式值达到最大。在具体算法步骤中,每一轮每 一次训练首先会分别从先验噪声数据集和真实图像数据集中随机估计获得 N 个样本,然后按 照上式梯度更新判别网络的参数,更新完后又按照式(-梯度更新生成模型参数。 36) S= Σ(m) lg(G(i))) 36) 1 o1-D(z( = 需要注意的是,GAN的判别网络(m) 和(1) 生成网络需要同步,某一个网络过早收敛都会导 致另外一个网络无法继续更新学习。 3.3 生成对抗网络的发展 6. GAN可以被视为一种模型融合的方法,因此自其诞生以来,衍生出了众多变体和改 第3章经典卷积神经网络59 进模型。例如,2016年,Radford等[52]提出了深度卷积生成对抗网络(DepConvolutional GenerativeAdversarialNetwork,DCGAN )。在DCGAN中,生成网络和判别网络均采用 标准的卷积神经网络,这使得GAN在图像生成任务中表现出色。同时,由于GAN模型 的灵活性,生成网络和判别网络可以用其他神经网络替代,为模型的进一步改进提供了广 阔的空间。针对原始GAN不能生成具有特定属性的图片的问题,MehdiMirza等[53]则 提出了条件生成对抗网络(ConditionalGenerativeAdversarialNetwork,CGAN),CGAN 是经典的条件生成对抗式网络,该模型将类别标签分别和噪声以及真实图像数据组合起 来作为生成网络和判别网络的输入,相当于通过类别属性约束了生成内容。GAN除了 与CNN网络结合用于图像,也可以与RNN等网络结构结合用于序列数据。时间生成对 抗网络(TimeGenerativeAdversarialNetwork,TimeGAN)[54]就是典型的时序序列生成 对抗网络,其判别生成网络均由门控循环单元(GatedRecurentUnit,GRU)[55]构成。此 外,GAN的衍生模型种类繁多,这里不再逐一列举。感兴趣的读者可以自行查找相关资 料,以进一步了解这些模型的特点和应用。 ..3.7 Transformer 3.1 Tasomr概述 7.rnfre Transformer[48]模型源于谷歌公司的经典论文Atetinisal yourned,其主要由 编码器和解码器两个组件构成,其整体结构如图3-9所示。(o) (n) 图3- 9 Transformer的整体结构 60 深度学习技术基础(微课版) Transformer模型中的编码器单元由多头注意力机制和前馈神经网络组成;解码器 单元则由遮掩多头注意力机制、编码器-解码器注意力机制和前馈神经网络构成。在这些 组件中,每个网络层都采用了残差连接,并在输出至下一层之前进行层归一化处理。 3.7.2 自注意力机制 编码器和解码器都包含自注意力结构,这是Transformer的核心计算机制,用于更好 地量化上下文相关信息。自注意力机制类似于人类的注意力机制,能够聚焦于重要信息, 同时忽略不相关的元素。自注意力的具体计算方式如图3-10所示。 图3-10 自注意力的具体计算方式 该结构首先会初始化3个权重矩阵并与输入矩阵相乘生成查询矩阵Q、键矩阵K 和 值矩阵V,然后Q 和K 进行点乘并除以维度的平方根,这一步的矩阵相乘得到的结果是 单词之间的相似度,反映出了单词之间的关系。对结果进行Softmax处理再乘以V,这一 步计算出的自注意力值作为词向量,能够更好地包含词与句中词的相关性。其计算如 式(3-7)所示: Attention(Q,K,V)= SoftmaxQKT dk . è . . . ÷ . è . . .÷V . è . (3-7) Transformer使用较多的是多头注意力机制,在这个结构中,会将多个注意力机制的 输出结果拼接起来,再通过线性层完成线性变换。每个注意力机制的参数不同,这意味着 每个注意力机制关注的位置也不同,因此多头注意力机制能够挖掘并整合更多特征信息 到词向量中,同时降低了每个注意力机制的维度,通过并行运算加快了计算速度。 在解码器中,还包含一层编码器-解码器注意力机制。在这一机制中,K 和V 矩阵是 由编码器的输出值分别乘以两个权重矩阵生成的,而Q 矩阵则由解码器中的多头注意力 机制输出值生成。当得到Q、K、V 矩阵后,就可以按照注意力机制的计算方式生成最终 的注意力值。 第3章经典卷积神经网络613.7.3Transformer的输入 在Transformer模型中,编码器和解码器的输入不仅由词向量构成,还需要加上位置 编码。位置编码的计算公式如式(3-8)所示: PE(pos,2i)=sin(pos/10002i/d) PE(pos,2i+1)=cos(pos/10002i/d) (3-8) 其中,pos表示单词在句子中的绝对位置,pos=0,1,2,…, 对于句子中的第i个单词,其 pos=i-1;d表示词向量的维度;2i和2i+1 分别表示偶数和奇数位置的维度,i表示词 向量中的第i维。之所以使用三角函数进行位置编码,是因为一个词在不同语境中出现, 可能具有不同的含义。而三角函数能够很好地捕捉词语之间的相对位置信息。当词向量 加上位置编码向量后,不同位置的同一词语的词向量就会有所区别,从而帮助模型更好地 学习并理解词语之间的相互关系,提升模型对词义和语句逻辑顺序的理解能力。 对于解码器来说,每次输入矩阵都会添加上一个自身预测的词向量,最开始由于没有 预测值因此只有起始符,即起始符向量加上位置编码。这表明Transformer是一种自回 归模型,利用已生成的预测值来生成下一个预测值。 3.7.4掩码机制 掩码机制是Transformer中的一种重要技术,它用于对某些值进行掩盖,防止它们在 参数更新时产生影响。Transformer模型中涉及两种主要的掩码[51]:填充掩码和序列 掩码。 填充掩码主要用于解决自注意力机制计算中输入矩阵尺寸不一致的问题。在自然语 言处理中,句子的长度通常不一致。对于较短的句子,需要用零填充来补齐;对于较长的 句子,保留左侧内容,多余部分用零替代。然而,直接使用零填充在Softmax操作中可能 会导致这些区域产生有效的特征值。为了解决这个问题,在进行Softmax操作之前,会 加上一个偏置矩阵,其中零值区域对应原矩阵的有效区域,而无穷大值对应无效区域,从 而使无效区域不会影响计算结果[56]。 在解码器的遮掩多头注意力机制中,除了使用填充掩码外,还会使用序列掩码。在训 练过程中,为了防止模型“作弊”,需要掩盖句子中待预测的所有词。具体方法是在对Q、 K、 V 矩阵完成缩放点积后,对得到的矩阵进行处理,将矩阵的上三角部分元素转化为负 无穷值,然后再进行Softmax操作。可以看到并不是直接掩盖待预测词的词向量,因为 填充掩码已经进行了数据对齐,再对待预测词进行无效化处理可能会损失一部分信息。 而Q、K、 V 矩阵完成缩放点积后得到的矩阵可以看作任意两个词之间的相似度矩阵,对 其进行上述操作后,模型只知道某个单词与其之前的单词的相似度。这样模型便能在不 损失原始信息的情况下对句子进行掩盖[57]。 3.7.5 Transformer网络 近年来,卷积神经网络在图像视觉领域逐渐遇到瓶颈,而Transformer在自然语言处 理领域的成功引起了广泛关注。将Transformer应用于图像视觉领域成为了新的发展方 62 深度学习技术基础(微课版) 向。理论上,注意力机制与人类视觉有相似之处;在实验中,Transformer也展现了许多 优势和潜力。 2020年,视觉Transformer(VisionTransformer,ViT)[58]将Transformer应用于图 像领域,用Transformer替代了标准卷积神经网络。该模型将图像转换为若干个指定大 小的图像块,再将每个图块转换为一维向量(类似于词向量),然后通过Transformer的编 码器和多层感知机进行分类。在ImageNet图像分类任务中,该模型取得了88.55%的 Top-1准确率,超越了ResNet系列模型,验证了Transformer在图像领域应用的可行性。 2021年,Liu等[59]提出了SwinTransformer,与ViT 相比性能有所提升。当前, Transformer在目标检测、图像分割、图像分类以及图像视频超分辨率等任务中均有应 用。例如,目标检测的DETR (Detection Transformer)[60] 模型融合了CNN 和 Transformer两种模型,而图像分割的SETR(SemanticSegmentationTransformer)[61] 模型也结合了这两种模型。这些模型在实验中均表现出色。如果读者对Transformer在 图像领域的应用感兴趣,可以自行查阅相关资料。 .. 3.8 例 题 例题3-1 使用PyTorch实现AlexNet,并对FashionMNIST 数据集进行分类(编写代码完成 以下任务)。 (1)加载FashionMNIST数据集并进行预处理。 (2)定义AlexNet模型。 (3)使用SGD优化器和交叉熵损失函数来训练模型。 (4)在训练过程中输出每个epoch的平均损失。 (5)在测试集上评估模型的准确性,并输出测试准确率。 解答: (1)加载FashionMNIST数据集并进行预处理。 1. import torch 2. import torch.nn as nn 3. import torch.optim as optim 4. import torchvision.transforms as transforms 5. from torch.utils.data import DataLoader 6. from torchvision.datasets import FashionMNIST 7. import matplotlib.pyplot as plt 8. transform = transforms.Compose([ 9. transforms.Resize((227, 227)), #将图像大小调整为(227, 227) 10. transforms.ToTensor(), 11. transforms.Normalize((0.5,), (0.5,)) 12. ]) 13. train_dataset = FashionMNIST(root= './data', train= True, download= True, transform=transform) 第3章 经典卷积神经网络 63 14. test_dataset = FashionMNIST(root= './data', train= False, download= True, transform=transform) 15. train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) 16. test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False) (2)定义AlexNet模型。 1. class AlexNet(nn.Module): 2. def __init__(self, num_classes=10): 3. super(AlexNet, self).__init__() 4. self.features = nn.Sequential( 5. nn.Conv2d(1, 64, kernel_size=11, stride=4, padding=2), 6. nn.ReLU(inplace=True), 7. nn.MaxPool2d(kernel_size=3, stride=2), 8. nn.Conv2d(64, 192, kernel_size=5, padding=2), 9. nn.ReLU(inplace=True), 10. nn.MaxPool2d(kernel_size=3, stride=2), 11. nn.Conv2d(192, 384, kernel_size=3, padding=1), 12. nn.ReLU(inplace=True), 13. nn.Conv2d(384, 256, kernel_size=3, padding=1), 14. nn.ReLU(inplace=True), 15. nn.Conv2d(256, 256, kernel_size=3, padding=1), 16. nn.ReLU(inplace=True), 17. nn.MaxPool2d(kernel_size=3, stride=2), 18. ) 19. self.avgpool = nn.AdaptiveAvgPool2d((6, 6)) 20. self.classifier = nn.Sequential( 21. nn.Dropout(), 22. nn.Linear(256 * 6 * 6, 4096), 23. nn.ReLU(inplace=True), 24. nn.Dropout(), 25. nn.Linear(4096, 4096), 26. nn.ReLU(inplace=True), 27. nn.Linear(4096, num_classes), 28. ) 29. def forward(self, x): 30. x = self.features(x) 31. x = self.avgpool(x) 32. x = torch.flatten(x, 1) 33. x = self.classifier(x) 34. return x (3)使用SGD优化器和交叉熵损失函数来训练模型。 1. device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 2. model = AlexNet(num_classes=10).to(device) 3. criterion = nn.CrossEntropyLoss() 4. optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9) 64 深度学习技术基础(微课版) (4)在训练过程中输出每个epoch的平均损失,如图3-11所示。 1. #记录每个epoch 的损失值 2. loss_history = [] 3. #4.训练模型并输出每个epoch 的平均损失 4. num_epochs = 10 5. for epoch in range(num_epochs): 6. model.train() 7. running_loss = 0.0 8. for images, labels in train_loader: 9. images, labels = images.to(device), labels.to(device) 10. optimizer.zero_grad() 11. outputs = model(images) 12. loss = criterion(outputs, labels) 13. loss.backward() 14. optimizer.step() 15. running_loss += loss.item() 16. epoch_loss = running_loss / len(train_loader) 17. loss_history.append(epoch_loss) 18. print(f"Epoch {epoch+1}, Loss: {running_loss / len(train_loader)}") 19. #绘制损失变化图 20. plt.plot(range(1, num_epochs + 1), loss_history, marker='o') 21. plt.xlabel('轮数') 22. plt.ylabel('损失') 23. plt.title('训练损失') 24. plt.grid(True) 25. plt.show() 图3-11 训练过程的每个epoch的平均损失 (5)在测试集上评估模型的准确性。 1. model.eval() 第3章 经典卷积神经网络 65 2. correct = 0 3. total = 0 4. with torch.no_grad(): 5. for images, labels in test_loader: 6. images, labels = images.to(device), labels.to(device) 7. outputs = model(images) 8. _, predicted = torch.max(outputs.data, 1) 9. total += labels.size(0) 10. correct += (predicted == labels).sum().item() 11. print(f"Accuracy on test set: {100 * correct / total}%") 例题3-2 请使用PyTorch实现ResNet来对FashionMNIST 数据集进行分类(编写代码完成 以下任务)。 (1)加载FashionMNIST数据集并进行预处理。 (2)定义ResNet模型。 (3)使用SGD优化器和交叉熵损失函数来训练模型。 (4)在训练过程中输出每个epoch的平均损失。 (5)在测试集上评估模型的准确性,并输出测试准确率。 解答: (1)加载FashionMNIST数据集并进行预处理。 1. import torch 2. import torch.nn as nn 3. import torch.optim as optim 4. import torchvision 5. import torchvision.transforms as transforms 6. from torchvision.datasets import FashionMNIST 7. from torch.utils.data import DataLoader 8. import matplotlib.pyplot as plt 9. #加载FashionMNIST 数据集并进行预处理 10. transform = transforms.Compose([ 11. transforms.ToTensor(), 12. transforms.Normalize((0.5,), (0.5,)) 13. ]) 14. train_dataset = FashionMNIST(root= './data', train= True, download= True, transform=transform) 15. test_dataset = FashionMNIST(root= './data', train= False, download= True, transform=transform) 16. train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) 17. test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False) (2)定义ResNet模型。 1. #定义Residual Block 2. class ResidualBlock(nn.Module): 66 深度学习技术基础(微课版) 3. def __init__(self, in_channels, out_channels, stride=1): 4. super(ResidualBlock, self).__init__() 5. self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size= 3, stride=stride, padding=1, bias=False) 6. self.bn1 = nn.BatchNorm2d(out_channels) 7. self.relu = nn.ReLU(inplace=True) 8. self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False) 9. self.bn2 = nn.BatchNorm2d(out_channels) 10. self.downsample = None 11. if stride != 1 or in_channels != out_channels: 12. self.downsample = nn.Sequential( 13. nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False), 14. nn.BatchNorm2d(out_channels) 15. ) 16. def forward(self, x): 17. identity = x 18. out = self.conv1(x) 19. out = self.bn1(out) 20. out = self.relu(out) 21. out = self.conv2(out) 22. out = self.bn2(out) 23. if self.downsample is not None: 24. identity = self.downsample(x) 25. out += identity 26. out = self.relu(out) 27. return out 28. #定义ResNet 模型 29. class ResNet(nn.Module): 30. def __init__(self, block, layers, num_classes=10): 31. super(ResNet, self).__init__() 32. self.in_channels = 64 33. self.conv1 = nn.Conv2d(1, 64, kernel_size= 7, stride= 2, padding= 3, bias=False) 34. self.bn1 = nn.BatchNorm2d(64) 35. self.relu = nn.ReLU(inplace=True) 36. self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) 37. self.layer1 = self.make_layer(block, 64, layers[0], stride=1) 38. self.layer2 = self.make_layer(block, 128, layers[1], stride=2) 39. self.layer3 = self.make_layer(block, 256, layers[2], stride=2) 40. self.layer4 = self.make_layer(block, 512, layers[3], stride=2) 41. self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) 42. self.fc = nn.Linear(512, num_classes) 43. def make_layer(self, block, out_channels, blocks, stride): 44. layers = [] 45. layers.append(block(self.in_channels, out_channels, stride)) 46. self.in_channels = out_channels