xinmeng_wit 发表于 2024-11-6 21:01

《动手学深度学习(PyTorch版)》6、现代卷积神经网络

<p><span style="font-size:16px;"><strong>一、深度卷积神经网络(AlexNet)</strong></span></p>

<p><strong>1、背景</strong></p>

<p>在 AlexNet 出现之前,卷积神经网络虽已被提出,但面临诸多限制。早期数据集规模小,存储和研究预算有限,导致研究多基于小型公开数据集,图像分辨率低且数量少。同时,神经网络加速卡性能不足,关键训练技巧缺失,使得深度学习发展受阻。而传统机器学习方法在计算机视觉领域占主导,其流水线依赖手工设计特征,计算机视觉研究人员更重视数据特征而非学习算法。</p>

<p><strong>2、AlexNet</strong></p>

<p>2012年,AlexNet横空出世。它首次证明了学习到的特征可以超越手工设计的特征。它一举打破了计算机视觉研究的现状。 AlexNet使用了8层卷积神经网络,并以很大的优势赢得了2012年ImageNet图像识别挑战赛。</p>

<p>AlexNet和LeNet的架构非常相似,如下所示。&nbsp;</p>

<div style="text-align: left;"></div>

<div style="text-align: left;">
<p>AlexNet和LeNet的设计理念非常相似,但也存在显著差异。</p>

<ul>
        <li>AlexNet比相对较小的LeNet5要深得多。AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。</li>
        <li>AlexNet使用ReLU而不是sigmoid作为其激活函数。</li>
</ul>

<p>AlexNet通过暂退法控制全连接层的模型复杂度,而LeNet只使用了权重衰减。 为了进一步扩充数据,AlexNet在训练时增加了大量的图像增强数据,如翻转、裁切和变色。 这使得模型更健壮,更大的样本量有效地减少了过拟合。&nbsp;</p>

<pre>
<code class="language-python">import torch
from torch import nn
from d2l import torch as d2l

net = nn.Sequential(
    # 这里使用一个11*11的更大窗口来捕捉对象。
    # 同时,步幅为4,以减少输出的高度和宽度。
    # 另外,输出通道的数目远大于LeNet
    nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
    nn.MaxPool2d(kernel_size=3, stride=2),
    # 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
    nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
    nn.MaxPool2d(kernel_size=3, stride=2),
    # 使用三个连续的卷积层和较小的卷积窗口。
    # 除了最后的卷积层,输出通道的数量进一步增加。
    # 在前两个卷积层之后,汇聚层不用于减少输入的高度和宽度
    nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
    nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
    nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
    nn.MaxPool2d(kernel_size=3, stride=2),
    nn.Flatten(),
    # 这里,全连接层的输出数量是LeNet中的好几倍。使用dropout层来减轻过拟合
    nn.Linear(6400, 4096), nn.ReLU(),
    nn.Dropout(p=0.5),
    nn.Linear(4096, 4096), nn.ReLU(),
    nn.Dropout(p=0.5),
    # 最后是输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000
    nn.Linear(4096, 10))</code></pre>

<p>&nbsp;</p>

<p><span style="font-size:16px;"><strong>二、使用块的网络(VGG)</strong></span></p>
</div>

<div style="text-align: left;">虽然AlexNet证明深层神经网络卓有成效,但它没有提供一个通用的模板来指导后续的研究人员设计新的网络。使用块的想法首先出现在牛津大学的<a href="http://www.robots.ox.ac.uk/~vgg/">视觉几何组(visual geometry group)</a>的<em>VGG网络</em>中。通过使用循环和子程序,可以很容易地在任何现代深度学习框架的代码中实现这些重复的架构。</div>

<div style="text-align: left;"><strong>1、VGG块</strong></div>

<div style="text-align: left;">VGG块玉经典卷积神经网络的组成部分基本一致,由一系列卷积层组成,后面再加上用于空间下采样的最大汇聚层。</div>

<p>在下面的代码中,我们定义了一个名为<span style="background-color: rgb(250, 250, 250); color: rgb(56, 58, 66); font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; font-size: 12px;">vgg_block</span>的函数来实现一个VGG块。</p>

<pre>
<code class="language-python">import torch
from torch import nn
from d2l import torch as d2l


def vgg_block(num_convs, in_channels, out_channels):
    layers = []
    for _ in range(num_convs):
      layers.append(nn.Conv2d(in_channels, out_channels,
                              kernel_size=3, padding=1))
      layers.append(nn.ReLU())
      in_channels = out_channels
    layers.append(nn.MaxPool2d(kernel_size=2,stride=2))
    return nn.Sequential(*layers)</code></pre>

<p>该函数有三个参数,分别对应于卷积层的数量<span style="background-color: rgb(250, 250, 250); color: rgb(56, 58, 66); font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; font-size: 12px;">num_convs</span>、输入通道的数量<span style="background-color: rgb(250, 250, 250); color: rgb(56, 58, 66); font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; font-size: 12px;">in_channels</span>和输出通道的数量<span style="background-color: rgb(250, 250, 250); color: rgb(56, 58, 66); font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; font-size: 12px;">out_channels。</span><br />
&nbsp;</p>

<p><strong>2、VGG网络</strong></p>

<p>与AlexNet、LeNet一样,VGG网络可以分为两部分:第一部分主要由卷积层和汇聚层组成,第二部分由全连接层组成。</p>

<div style="text-align: left;"></div>

<div style="text-align: left;">下面的代码实现了VGG-11。可以通过在<span style="background-color: rgb(250, 250, 250); color: rgb(56, 58, 66); font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; font-size: 12px;">conv_arch</span>上执行for循环来简单实现。</div>

<pre>
<code class="language-python">def vgg(conv_arch):
    conv_blks = []
    in_channels = 1
    # 卷积层部分
    for (num_convs, out_channels) in conv_arch:
      conv_blks.append(vgg_block(num_convs, in_channels, out_channels))
      in_channels = out_channels

    return nn.Sequential(
      *conv_blks, nn.Flatten(),
      # 全连接层部分
      nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5),
      nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5),
      nn.Linear(4096, 10))

net = vgg(conv_arch)</code></pre>

<p><span style="font-size:16px;"><strong>三、含并行连结的网络(GoogLeNet)</strong></span></p>

<p>在2014年的ImageNet图像识别挑战赛中,一个名叫<em>GoogLeNet</em>&nbsp;(<a href="https://zh-v2.d2l.ai/chapter_references/zreferences.html#id162" title="Szegedy, C., Liu, W., Jia, Y., Sermanet, P., Reed, S., Anguelov, D., … Rabinovich, A. (2015). Going deeper with convolutions. Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 1–9).">Szegedy&nbsp;<em>et al.</em>, 2015</a>)的网络架构大放异彩。这篇论文的一个重点是解决了什么样大小的卷积核最合适的问题。 毕竟,以前流行的网络使用小到1&times;1,大到11&times;11的卷积核。</p>

<p><strong>1、Inception块</strong></p>

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

<div style="text-align: left;"></div>

<p>&nbsp;</p>

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

<pre>
<code class="language-python">import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l


class Inception(nn.Module):
    # c1--c4是每条路径的输出通道数
    def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):
      super(Inception, self).__init__(**kwargs)
      # 线路1,单1x1卷积层
      self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
      # 线路2,1x1卷积层后接3x3卷积层
      self.p2_1 = nn.Conv2d(in_channels, c2, kernel_size=1)
      self.p2_2 = nn.Conv2d(c2, c2, kernel_size=3, padding=1)
      # 线路3,1x1卷积层后接5x5卷积层
      self.p3_1 = nn.Conv2d(in_channels, c3, kernel_size=1)
      self.p3_2 = nn.Conv2d(c3, c3, kernel_size=5, padding=2)
      # 线路4,3x3最大汇聚层后接1x1卷积层
      self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
      self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1)

    def forward(self, x):
      p1 = F.relu(self.p1_1(x))
      p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
      p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
      p4 = F.relu(self.p4_2(self.p4_1(x)))
      # 在通道维度上连结输出
      return torch.cat((p1, p2, p3, p4), dim=1)</code></pre>

<p><strong>2、GoogLeNet模型</strong></p>

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

<div style="text-align: left;"></div>

<p>现在,我们逐一实现GoogLeNet的每个模块。</p>

<p>&nbsp;</p>

<p>第一个模块使用64个通道、7&times;7卷积层。</p>

<pre>
<code class="language-python">b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
                   nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))</code></pre>

<p>第二个模块使用两个卷积层:第一个卷积层是64个通道、1&times;1卷积层;第二个卷积层使用将通道数量增加三倍的3&times;3卷积层。 这对应于Inception块中的第二条路径。</p>

<pre>
<code class="language-python">b2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1),
                   nn.ReLU(),
                   nn.Conv2d(64, 192, kernel_size=3, padding=1),
                   nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))</code></pre>

<p>第三个模块串联两个完整的Inception块。 第一个Inception块的输出通道数为64+128+32+32=256,四个路径之间的输出通道数量比为64:128:32:32=2:4:1:1。 第二个和第三个路径首先将输入通道的数量分别减少到96/192=1/2和16/192=1/12,然后连接第二个卷积层。第二个Inception块的输出通道数增加到128+192+96+64=480,四个路径之间的输出通道数量比为128:192:96:64=4:6:3:2。 第二条和第三条路径首先将输入通道的数量分别减少到128/256=1/2和32/256=1/8。</p>

<pre>
<code class="language-python">b3 = nn.Sequential(Inception(192, 64, (96, 128), (16, 32), 32),
                   Inception(256, 128, (128, 192), (32, 96), 64),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))</code></pre>

<p>第四模块更加复杂, 它串联了5个Inception块,其输出通道数分别是192+208+48+64=512、160+224+64+64=512、128+256+64+64=512、112+288+64+64=528和256+320+128+128=832。 这些路径的通道数分配和第三模块中的类似,首先是含3&times;3卷积层的第二条路径输出最多通道,其次是仅含1&times;1卷积层的第一条路径,之后是含5&times;5卷积层的第三条路径和含3&times;3最大汇聚层的第四条路径。 其中第二、第三条路径都会先按比例减小通道数。 这些比例在各个Inception块中都略有不同。</p>

<pre>
<code class="language-python">b4 = nn.Sequential(Inception(480, 192, (96, 208), (16, 48), 64),
                   Inception(512, 160, (112, 224), (24, 64), 64),
                   Inception(512, 128, (128, 256), (24, 64), 64),
                   Inception(512, 112, (144, 288), (32, 64), 64),
                   Inception(528, 256, (160, 320), (32, 128), 128),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))</code></pre>

<p>第五模块包含输出通道数为256+320+128+128=832和384+384+128+128=1024的两个Inception块。 其中每条路径通道数的分配思路和第三、第四模块中的一致,只是在具体数值上有所不同。 需要注意的是,第五模块的后面紧跟输出层,该模块同NiN一样使用全局平均汇聚层,将每个通道的高和宽变成1。 最后我们将输出变成二维数组,再接上一个输出个数为标签类别数的全连接层。</p>

<pre>
<code class="language-python">b5 = nn.Sequential(Inception(832, 256, (160, 320), (32, 128), 128),
                   Inception(832, 384, (192, 384), (48, 128), 128),
                   nn.AdaptiveAvgPool2d((1,1)),
                   nn.Flatten())

net = nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 10))</code></pre>

<p>&nbsp;</p>

<p>后面还有批量规范化,残差网络和稠密连接网络等,内容实在是太多了,一时间没办法消化这么多的内容,请听下回分解。</p>

<p>&nbsp;</p>

hellokitty_bean 发表于 2024-11-7 09:56

<p>忙里偷闲来看Wit的Modern CNN.........<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/loveliness.gif" width="48" /></p>

<p>这个&ldquo;现代的&rdquo;意味,那应该有对应的传统的CNN?</p>

<p>怎么分代的呢?</p>

hellokitty_bean 发表于 2024-11-7 10:01

<p>从文字中来理解,杨立坤提出的CNN,及后面的VGG、GoogleCNN、残差网络都是在AlexNet基础上提出来的,都称为现代CNN了。<img height="63" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/victory.gif" width="61" /></p>

xinmeng_wit 发表于 2024-11-9 20:39

hellokitty_bean 发表于 2024-11-7 10:01
从文字中来理解,杨立坤提出的CNN,及后面的VGG、GoogleCNN、残差网络都是在AlexNet基础上提出来的,都称为 ...

<p>对的,后面的都属于现代卷积神经网络,<a href="https://home.eeworld.com.cn/space-uid-1144154.html" target="_blank">hellokitty</a>兄理解的非常到位<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan88.gif" width="59" /></p>
页: [1]
查看完整版本: 《动手学深度学习(PyTorch版)》6、现代卷积神经网络