观察torchvision自带的alexnet模型

1. pytorch中AlexNet模型的实现与原始论文的不同

pytorch中实现的AlexNet,有些不同的地方。需要注意的事项如下:

(1)论文中图像是224*224*3的RGB图像。但是模型接受的是4D的张量。所以,必须将图像变换为4D的张量,多出来的一维是Batch这个维度。要把H*W*C的RGB 转化为 (B, C, H , W)的形式。

(2)pytorch提供的官方AlexNet已经弃用了LRN。这个技巧已经过时。

(3)增加了AdaptiveAvgPool2d这个trick。自适应的池化。这个trick就是使得图片,不管多大的分辨率,输出都是6*6的。为什么要都统一成6*6呢?因为后面接了FC层,如果前面的输入的神经元个数不匹配的话,那么这个FC层是没办法运行的。所以接了一个自适应的池化,把尺寸不匹配的特征图都池化到6*6.

(4)卷积核数量发生了改变。没有完全按照paper中的卷积核数量去设置。

观察torchvision自带的alexnet模型_第1张图片

卷积核的数量改变了。

2.pytorch中AlexNet的实现 

下面是torchvision中的AlexNet的实现。代码没有修改。加了一些注释。

torchvision/models/alexnet.py

import torch
import torch.nn as nn
from .utils import load_state_dict_from_url


__all__ = ['AlexNet', 'alexnet']


model_urls = {
    'alexnet': 'https://download.pytorch.org/models/alexnet-owt-4df8aa71.pth',
}


class AlexNet(nn.Module):

    def __init__(self, num_classes=1000):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            #conv1
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),  #3表示输入的图像是3通道的。64表示输出通道数,也就是卷积核的数量。
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),

            #conv2
            nn.Conv2d(64, 192, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),

            #conv3
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),

            #conv4
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),

            #conv5
            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)) #如果输入的图片不是224*224,那上面的池化输出的就可能不是6*6。而下面的FC层要求的输入必须是6*6,否则就没法运算。
        self.classifier = nn.Sequential(
            #FC1
            nn.Dropout(),   #用于减轻过拟合
            nn.Linear(256 * 6 * 6, 4096),  #全连接层。输入的是前面的特征图。特征图是6*6大小的,有256个特征图。自己有4096个神经元。
            nn.ReLU(inplace=True),

            #FC2
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),

            #FC3
            nn.Linear(4096, num_classes), #num_classes:这里是1000.
        )

    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)       #liupc:自适应的池化。
        x = torch.flatten(x, 1)   #拉成一个向量的形式。
        x = self.classifier(x)
        return x


def alexnet(pretrained=False, progress=True, **kwargs):
    r"""AlexNet model architecture from the
    `"One weird trick..." `_ paper.

    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
        progress (bool): If True, displays a progress bar of the download to stderr
    """
    model = AlexNet(**kwargs)
    if pretrained:
        state_dict = load_state_dict_from_url(model_urls['alexnet'],
                                              progress=progress)
        model.load_state_dict(state_dict)
    return model

我们手动分析一下:

1. conv1 --> ReLU --> Pool 

   conv1:   nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2) 

                其中3表示输入的图像是3通道的。64表示输出通道数,也就是卷积核的数量。输入的图片是224*224的。得到的特征图尺寸= (224+4-11)/4 + 1 = 55。所以特征图为55*55*64。所需参数个数为:(11*11*3+1)*64 = 23296个。

   ReLU:   nn.ReLU(inplace=True),

   Pool:     nn.MaxPool2d(kernel_size=3, stride=2),

                输入的特征图是55*55*64的。得到的特征图数量是不变的还是64个。尺寸变为(55-3)/2+1 = 27。所以输出为27*27*64。参数个数为0.
           

2. conv2 --> ReLU --> Pool

    conv2:  nn.Conv2d(64, 192, kernel_size=5, padding=2),
            输入通道数是64,是因为上面的输出有64个特征图。输出通道数为192。输出尺寸为(27+4-5)/1 + 1 = 27。输出:27*27*192。参数个数为(5*5*64+1)*192 = 307392个。

    ReLU: nn.ReLU(inplace=True)

    Pool:   nn.MaxPool2d(kernel_size=3, stride=2)

             输出尺寸为(27-3)/2 + 1 = 13。输出13*13*192。


 3. Conv3 --> ReLU

     conv3: nn.Conv2d(192, 384, kernel_size=3, padding=1)

              输出尺寸(13+2-3)/1 + 1 = 13。输出13*13*384。参数个数:(3*3*192+1) * 384 = 663936。

     ReLU: nn.ReLU(inplace=True)

4. Conv4 --> ReLU

    conv4: nn.Conv2d(384, 256, kernel_size=3, padding=1)

              输出尺寸(13+2-3)/1 +1 = 13。输出13*13*256。参数个数(3*3*384+1)*256 = 884992。

    ReLU: nn.ReLU(inplace=True)           

5. conv5 --> ReLU --> Pool

    conv5: nn.Conv2d(256, 256, kernel_size=3, padding=1)
               输出尺寸:(13+2-3)/1 +1 = 13。输出13*13*256。参数个数(3*3*256+1)*256 = 590080。

    ReLU:nn.ReLU(inplace=True)

    Pool: nn.MaxPool2d(kernel_size=3, stride=2)
              输出尺寸:(13-3)/2 + 1 = 6。输出6*6*256。

6. avgpool : nn.AdaptiveAvgPool2d((6,6))

    如果输入的图片不是224*224,那上面的池化输出的就可能不是6*6。而下面的FC层要求的输入必须是6*6,否则就没法运算。

7. x = torch.flatten(x, 1)   #拉成一个向量的形式。

8. FC1: dropout --> Linear --> ReLU

    dropout: nn.Dropout(),   #用于减轻过拟合

    Linear: nn.Linear(256 * 6 * 6, 4096),  

                全连接层。输入的节点数是256*6*6。因为前面输入的是特征图,特征图是6*6大小的,有256个特征图。自己有4096个神经元。参数个数:(256*6*6 +1) * 4096 = 37,752,832

     ReLU: nn.ReLU(inplace=True),

9. FC2: dropout --> linear -->ReLU

    dropout: nn.Dropout()

    linear: nn.Linear(4096,4096)

             全连接层,输入节点数是4096,输出节点数4096。参数个数:4097*4096 = 16,781,312

    ReLU: nn.ReLU(inplace=True)

10. FC3: linear

      linear: nn.Linear(4096, num_classes)

                 num_classes为1000分类。需要的参数个数:4097*1000 = 4097000.

与下面代码输出的网络结构的参数完全一致!

观察torchvision自带的alexnet模型_第2张图片

你可能感兴趣的:(11,Python/DL/ML,pytorch,深度学习,神经网络)