[深度学习笔记(2)]LeNet、AlexNet、GoogleNet模型详解


本系列是博主刚开始接触深度学习时写的一些笔记,写的很早了一直没有上传,趁着假期上传一下,作为分享,希望能帮助到你。

目录

一、LeNet

二、AlexNet

三、GoogleNet

总结


一、LeNet

        LeNet是早期最成功的神经网络,先使用卷积层来学习图片空间信息,然后使用全连接层来转换到类别空间。

        从上图可以看出,LeNet网络:(1)输入是一个28×28的图像;(2)卷积层1:卷积核大小为5×5,kernel_size=6,输出是28×28;(3)polling层1:卷积核大小为2×2,kernel_size=6,输出是14×14;(4)卷积层2:卷积核大小为5×5,kernel_size=16,输出是10×10;(5)polling层2:卷积核大小为2×2,kernel_size=16,输出是5×5;(6)卷积层3:卷积核大小为5×5,kernel_size=120,输出是1×1;(7)全连接层1;(8)输出层;

        LeNet是一种在手写体字符数据识别任务上效果好的卷积神经网络,MNIST数据集为手写体图片数据集,该数据集中包含60 000个训练数据,10000个测试数据,图像大小28×28,分为10类。


        对代码中x = x.view(x.size(0),-1)的理解:这句代码一般在调用分类器之前,分类器是一个简单的nn.Linear()结构,输入输出都是维度为1 的值,x = x.view(x.size(0),-1)是为了将前面多维度的tensor展平成1维。View()函数的功能和reshape类似,用来转换size大小。x=x.view(batchsize,-1)中batchsize指转换后有几行,而-1指在不告诉函数有多少列的情况下,根据原tensor数据和batchsize自动分配列数。


代码实现:

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(1, 6, 5), 
            nn.Sigmoid(),
            nn.MaxPool2d(2, 2), 
            nn.Conv2d(6, 16, 5),
            nn.Sigmoid(),
            nn.MaxPool2d(2, 2)
        )
        self.fc = nn.Sequential(
            nn.Linear(16*4*4, 120),
            nn.Sigmoid(),
            nn.Linear(120, 84),
            nn.Sigmoid(),
            nn.Linear(84, 10)
        )

    def forward(self, img):
        feature = self.conv(img)
        output = self.fc(feature.view(img.shape[0], -1))
        return output

二、AlexNet

 

 (图片来自互联网)

        AlexNet是Alex Krizhevsky等在2012年ImageNet比赛上首次提出的,最终该网络夺得冠军。AlexNet是一种简单且高效的卷积神经网络,整个网络模型由5个卷积层、3个全连接层以及相应的池化层构成,最终由softmax函数完成分类。下表为AlexNet网络模型的参数信息。

参数设置

输出

Input

kernel-size=11×11, stride=4

227×227×3

Conv1

kernel-size=3×3, stride=2

55×55×96

Polling1

kernel-size=5×5, stride=2

27×27×96

Conv2

kernel-size=5×5, stride=2

27×27×256

Polling2

kernel-size=3×3, stride=2

13×13×256

Conv3

kernel-size=3×3, stride=2

13×13×384

Conv4

kernel-size=3×3

13×13×384

Conv5

kernel-size=3×3

13×13×256

Polling3

kernel-size=3×3, stride=2

6×6×256

Fc6

/

9216

Fc7

/

4096

Fc8

/

4096

Output

/

1000


代码实现:

class AlexNet(nn.Module):

    def __init__(self, num_classes: int = 1000) -> None:
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 192, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

三、GoogleNet

        GoogleNet是google公司推出深度神经网络模型,该网络在2014年的ImageNet竞赛中夺得了冠军。网络主体是由Inception模块拼接而成。

(1)关于Inception模块

 

        整个inception结构就是由多个这样的incepion模块串联起来的。inception结构主要由有两个贡献:1、使用1×1的卷积来进行升降维;2、在多个尺寸上同时进行卷积再聚合。

1:1×1卷积模块(黄色):在相同尺寸的感受野中叠加更多的卷积,能提取到更丰富的特征;降低了计算复杂度。

2:在直觉感受上在多个尺度上同时进行卷积,能提取到不同尺度的特征,特征更为丰富也意味着最后分类判断时更加准确;利用稀疏矩阵分解成密集矩阵计算的原理来加快收敛速度;

(2)GoogleNet网络详解

a、输入:224×224×3(进行了零均值化预处理操作,即图像中的每个像素减去均值)

b、第一模块:卷积层、最大池化层

 

卷积核:7×7,stride=2,padding=3,通道数=64,

Output=(224+2*3-1*(7-1)-1)/2 +1=112

输出:112×112×64 卷积后进行Relu操作

池化层:3×3,stride=2   output=(112-1*(3-1)-1)/2 +1=56

输出:56×56×64

c、第二模块:2个卷积层,最大池化层

 

64个1×1的卷积核将输入的特征图(56×56×64)变为56×56×64,然后Relu()

卷积核:3×3,stride=1,padding=1,通道数192,

Output=(56+1*2-1*(3-1)-1)/1 +1=56

输出:56×56×192,然后Relu()

最大池化层:3×3,stride=2,通道数192, output=(56-1*(3-1)-1)/2 +1=28

输出:28×28×192

d、第三模块 inception 3a层

1)使用64个1×1的卷积核 输出:28×28×64 然后Relu()

2)96个1×1的卷积核 输出:28×28×96 然后Relu()

     128个3×3的卷积核 输出28×28×128

3)16个1×1的卷积核 输出:28×28×16 然后Relu()

     32个5×5的卷积核 输出:28×28×32

4)3×3的最大池化层,28×28×192

     32个1×1的卷积核 输出:28×28×32

将上面4个结果进行连接,对输出结果的第三维进行并联,64+128+32+32=256

最终的输出:28×28×256

e、第三模块 inception 3b

1)128个1×1的卷积核 输出:28×28×128,然后Relu()

2)128个1×1的卷积核 输出:28×28×128,然后Relu()

     192个3×3的卷积核 输出:28×28×192

3)32个1×1的卷积核 输出:28×28×32,然后Relu()

     96个5×5的卷积核 输出:28×28×96

4)3×3的最大池化层 28×28×256

     64个1×1的卷积核 输出28×28×64

将上面4个结果进行连接,对这四部分输出结果的第三维并联,128+192+86+64=480

最终的输出是:28×28×480

f、第四模块 inception 4a、4b、4c、4e

 

g、第五模块 inception 5a、5b

 

h、输出层

        GoogleNet采用的是全局平均池化,得到的是高和宽均为1的卷积层,然后添加丢弃概率为40%的Dropout,输出层激活函数采用的是softmax。

I、激活函数

Relu()函数

j、辅助分类器

        神经网络的中间层也具有很强的识别能力,为了利用中间层抽象的特征,在某些中间层中添加含有多层的分类器。GoogleNet中共增加了两个辅助的softmax分支:一是为了避免梯度消失,用于向前传导梯度。二是将中间某一层输出用作分类,起到模型融合作用。

(图片来自互联网) 


代码实现:

class GoogLeNet(nn.Module):
    __constants__ = ['aux_logits', 'transform_input']

    def __init__(
        self,
        num_classes: int = 1000,
        aux_logits: bool = True,
        transform_input: bool = False,
        init_weights: Optional[bool] = None,
        blocks: Optional[List[Callable[..., nn.Module]]] = None
    ) -> None:
        super(GoogLeNet, self).__init__()
        if blocks is None:
            blocks = [BasicConv2d, Inception, InceptionAux]
        if init_weights is None:
            warnings.warn('The default weight initialization of GoogleNet will be changed in future releases of '
                          'torchvision. If you wish to keep the old behavior (which leads to long initialization times'
                          ' due to scipy/scipy#11299), please set init_weights=True.', FutureWarning)
            init_weights = True
        assert len(blocks) == 3
        conv_block = blocks[0]
        inception_block = blocks[1]
        inception_aux_block = blocks[2]

        self.aux_logits = aux_logits
        self.transform_input = transform_input

        self.conv1 = conv_block(3, 64, kernel_size=7, stride=2, padding=3)
        self.maxpool1 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
        self.conv2 = conv_block(64, 64, kernel_size=1)
        self.conv3 = conv_block(64, 192, kernel_size=3, padding=1)
        self.maxpool2 = nn.MaxPool2d(3, stride=2, ceil_mode=True)

        self.inception3a = inception_block(192, 64, 96, 128, 16, 32, 32)
        self.inception3b = inception_block(256, 128, 128, 192, 32, 96, 64)
        self.maxpool3 = nn.MaxPool2d(3, stride=2, ceil_mode=True)

        self.inception4a = inception_block(480, 192, 96, 208, 16, 48, 64)
        self.inception4b = inception_block(512, 160, 112, 224, 24, 64, 64)
        self.inception4c = inception_block(512, 128, 128, 256, 24, 64, 64)
        self.inception4d = inception_block(512, 112, 144, 288, 32, 64, 64)
        self.inception4e = inception_block(528, 256, 160, 320, 32, 128, 128)
        self.maxpool4 = nn.MaxPool2d(2, stride=2, ceil_mode=True)

        self.inception5a = inception_block(832, 256, 160, 320, 32, 128, 128)
        self.inception5b = inception_block(832, 384, 192, 384, 48, 128, 128)

        if aux_logits:
            self.aux1 = inception_aux_block(512, num_classes)
            self.aux2 = inception_aux_block(528, num_classes)
        else:
            self.aux1 = None  # type: ignore[assignment]
            self.aux2 = None  # type: ignore[assignment]

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.dropout = nn.Dropout(0.2)
        self.fc = nn.Linear(1024, num_classes)

        if init_weights:
            self._initialize_weights()

    def _initialize_weights(self) -> None:
        for m in self.modules():
            if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
                torch.nn.init.trunc_normal_(m.weight, mean=0.0, std=0.01, a=-2, b=2)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

    def _transform_input(self, x: Tensor) -> Tensor:
        if self.transform_input:
            x_ch0 = torch.unsqueeze(x[:, 0], 1) * (0.229 / 0.5) + (0.485 - 0.5) / 0.5
            x_ch1 = torch.unsqueeze(x[:, 1], 1) * (0.224 / 0.5) + (0.456 - 0.5) / 0.5
            x_ch2 = torch.unsqueeze(x[:, 2], 1) * (0.225 / 0.5) + (0.406 - 0.5) / 0.5
            x = torch.cat((x_ch0, x_ch1, x_ch2), 1)
        return x

    def _forward(self, x: Tensor) -> Tuple[Tensor, Optional[Tensor], Optional[Tensor]]:
        # N x 3 x 224 x 224
        x = self.conv1(x)
        # N x 64 x 112 x 112
        x = self.maxpool1(x)
        # N x 64 x 56 x 56
        x = self.conv2(x)
        # N x 64 x 56 x 56
        x = self.conv3(x)
        # N x 192 x 56 x 56
        x = self.maxpool2(x)

        # N x 192 x 28 x 28
        x = self.inception3a(x)
        # N x 256 x 28 x 28
        x = self.inception3b(x)
        # N x 480 x 28 x 28
        x = self.maxpool3(x)
        # N x 480 x 14 x 14
        x = self.inception4a(x)
        # N x 512 x 14 x 14
        aux1: Optional[Tensor] = None
        if self.aux1 is not None:
            if self.training:
                aux1 = self.aux1(x)

        x = self.inception4b(x)
        # N x 512 x 14 x 14
        x = self.inception4c(x)
        # N x 512 x 14 x 14
        x = self.inception4d(x)
        # N x 528 x 14 x 14
        aux2: Optional[Tensor] = None
        if self.aux2 is not None:
            if self.training:
                aux2 = self.aux2(x)

        x = self.inception4e(x)
        # N x 832 x 14 x 14
        x = self.maxpool4(x)
        # N x 832 x 7 x 7
        x = self.inception5a(x)
        # N x 832 x 7 x 7
        x = self.inception5b(x)
        # N x 1024 x 7 x 7

        x = self.avgpool(x)
        # N x 1024 x 1 x 1
        x = torch.flatten(x, 1)
        # N x 1024
        x = self.dropout(x)
        x = self.fc(x)
        # N x 1000 (num_classes)
        return x, aux2, aux1

    @torch.jit.unused
    def eager_outputs(self, x: Tensor, aux2: Tensor, aux1: Optional[Tensor]) -> GoogLeNetOutputs:
        if self.training and self.aux_logits:
            return _GoogLeNetOutputs(x, aux2, aux1)
        else:
            return x   # type: ignore[return-value]

    def forward(self, x: Tensor) -> GoogLeNetOutputs:
        x = self._transform_input(x)
        x, aux1, aux2 = self._forward(x)
        aux_defined = self.training and self.aux_logits
        if torch.jit.is_scripting():
            if not aux_defined:
                warnings.warn("Scripted GoogleNet always returns GoogleNetOutputs Tuple")
            return GoogLeNetOutputs(x, aux2, aux1)
        else:
            return self.eager_outputs(x, aux2, aux1)

总结

        以上就是今天要讲的内容,本文仅仅简单介绍了LeNet、AlexNet、GoogleNet模型的基本网络构成,代码均来自Pytorch官网,图片来自互联网,侵权删。如有错误,请及时指出,我们一起进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值