深度学习/联邦学习笔记(六)卷积神经及相关案例+pytorch

深度学习/联邦学习笔记(六)

卷积神经及相关案例+pytorch

卷积神经网络不同于一般的全连接神经网络,卷积神经网络是一个3D容量的神经元,即神经元是以三个维度来排列的:宽度、高度和深度

卷积神经网络中的主要层结构有三个:卷积层、池化层和全连接层,通过堆叠这些层结构形成了一个完整的卷积神经网络结构。卷积神经网络将原始图片转化成最后的类别得分,其中一些层包含参数,一些层没有包含参数,比如卷积层和全连接层拥有参数,而激活层和池化层不含参数。

构造一个简单的多层卷积神经网络

卷积层

nn. Conv2d()就是PyTorch中的卷积模块了,里面常用的参数有5个,分别是in_channels, out_ channels, kernel size, stride, padding, 除此之外还有参数dilation, groups, bias。 下 面来解释每个参数的含义。

in_channels对应的是输人数据体的深度; out_channels 表示输出数据体的深度; kernel_ size表示滤波器(卷积核)的大小,可以使用一个数字来表示高和宽相同的卷积核,比如kernel_ size=3, 也可以使用不同的数字来表示高和宽不同的卷积核,比如kernel_ size=(3, 2); stride表示滑动的步长; padding=0 表示四周不进行零填充,而padding=1表示四周进行1个像素点的零填充; bias是一一个布尔值,默认bias=True,表示使用偏置; groups表示输出数据体深度上和输人数据体深度上的联系,默认groups=1, 也就是所有的输出和输人都是相关联的,如果groups=2, 这表示输人的深度被分割成两份,输出的深度也被分割成两份,它们之间分别对应起来,所以要求输出和输人都必须要能被groups整除; dilation 表示卷积对于输人数据体的空间间隔,默认dilation1

池化层

nn.MaxPool2d()表示网络中的最大值池化,其中的参数有kernel_size、stride、padding. dilation. return_sindices. ceil_mode.下面解释一下它们各自的含义。

  •  kernel_ size, stride, padding,dilation之前卷积层已经介绍过了,是相同的含义;
  •  retun_indices表示是否返回最大值所处的下标默认return_indices=False
  • ceil_mode表示使用一些方格代替层结构,默认ceil_mode=False, 一般都不会设置这些参数。
  • nn. AvgPool2d()表示均值池化,里面的参数和nn.MaxPool2d() 类似,但多一个参数 count_ include_pad,这个参数表示计算均值的时候是否包含零填充,默认count_ include_pad=True。

一般使用较多的就是nn.MaxPool2d() 和nn .AvgPool2d(),另外PyTorch还提供了一些别的池化层,如nn. LPPcol2d()、nn .AdaptiveMaxPool2d()等
全连接层和激活函数上一章已经介绍过, 接下来构造一个简单的多层卷积神经网络。

from torch import nn


class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()  #b,3,32,32

        #将卷积层、激活层和池化层组合在一起构成了一个层结构,定义了3个这样的层结构
        layer1 = nn.Sequential()   #nn.Sequential()是将网络的层组合在一起
        layer1.add_module('conv1',nn.Conv2d(3,32,3,1,padding=1))     #nn.Conv2d()是pytorch中的卷积模块
        #b,32,32,32
        layer1.add_module('relu1', nn.ReLU(True))            #激活层
        layer1.add_module('pool1',nn.MaxPool2d(2,2)) #b, 32, 16, 16
        self.layerl = layer1

        layer2 = nn.Sequential()
        layer2.add_module('conv2', nn.Conv2d(32, 64, 3, 1, padding=1))
        #b,64,16,16
        layer2 .add_module('relu2', nn.ReLU(True))
        layer2.add_module('pool2', nn.MaxPool2d(2, 2)) #b, 64,8, 8
        self.layer2 = layer2

        layer3 = nn. Sequential()
        layer3.add_module('conv3', nn.Conv2d(64, 128, 3, 1, padding=1))
        #b, 128, 8, 8
        layer3.add_module('relu3', nn.ReLU(True))
        layer3.add_module('pool3', nn.MaxPool2d(2, 2)) #b, 128,4, 4
        self.layer3 = layer3

        #定义全连接层,输出10
        layer4 = nn. Sequential()
        layer4.add_module('fcl', nn.Linear(2048, 512))
        layer4 .add_module('fc_relul', nn.ReLU(True))
        layer4 .add_module('fc2', nn.Linear(512, 64))
        layer4 .add_module('fc_relu2', nn.ReLU(True))
        layer4 .add_module('fc3', nn.Linear(64, 10))
        self.layer4. layer4

    def forward(self, x):
        conv1 = self.layerl(x)
        conv2 = self.layer2(conv1)
        conv3 = self.layer3(conv2)
        fc_input = conv3 .view(conv3.size(0), -1)
        fc_out = self.layer4(fc_input)
        return fc_out

model = SimpleCNN()

print(model)

使用print(model)可以查看网络中都定义了哪些层结构,上面代码运行结果如下:

SimpleCNN(
  (layerl): Sequential(
    (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu1): ReLU(inplace=True)
    (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (layer2): Sequential(
    (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu2): ReLU(inplace=True)
    (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (layer3): Sequential(
    (conv3): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu3): ReLU(inplace=True)
    (pool3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (layer4): Sequential(
    (fcl): Linear(in_features=2048, out_features=512, bias=True)
    (fc_relul): ReLU(inplace=True)
    (fc2): Linear(in_features=512, out_features=64, bias=True)
    (fc_relu2): ReLU(inplace=True)
    (fc3): Linear(in_features=64, out_features=10, bias=True)
  )
)

Process finished with exit code 0

提取层结构:

假设想要提取前面两层

new_model = nn.Sequential(*list(model.children())[:2])

print(new_model)

结果如下:

Sequential(
  (0): Sequential(
    (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu1): ReLU(inplace=True)
    (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (1): Sequential(
    (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu2): ReLU(inplace=True)
    (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
)

Process finished with exit code 0

提取参数和自定义初始化

有时候提取出的层结构并不够,还需要对里面的参数进行初始化,那么如何提取出网络的参数并对其初始化呢?

nn.Module里面有两个特别重要的关于参数的属性,分别是named_parameters()parameters() 。named_parameters()是给出网络层的名字和参数的迭代器,parameters()会给出一个网络的全部参数的迭代器。

想要得到每一层参数的名字

for param in model.named_parameters():
    print(param[0])

结果:

layerl.conv1.weight
layerl.conv1.bias
layer2.conv2.weight
layer2.conv2.bias
layer3.conv3.weight
layer3.conv3.bias
layer4.fcl.weight
layer4.fcl.bias
layer4.fc2.weight
layer4.fc2.bias
layer4.fc3.weight
layer4.fc3.bias

Process finished with exit code 0

如何对权重做初始化呢?非常简单,因为权重是一个variable, 所以只需要取出其中的data属性,然后对它进行所需要的处理就可以了。

for m in model.modules():
    if isinstance(m,nn.Conv2d):    #isinstance()可以判断这个模型是不是所需的类型实例
        init.normal(m.weight.data)
        init.xavier_normal(m.weight.data)
        init.kaiming_normal(m.weight.data)
        m.bias.data.fill_(0)
    elif isinstance(m,nn.Linear):
        m.weight.data.normal_()

卷积神经网络案例

LeNet

                                                                                                                                                                                          LeNet的网络结构图

 

由上图可见,LeNet一共有7层,其中2层卷积层和2层池化层交替出现,最后输出3层全连接层得到整体的结果

from torch import nn


class Lenet(nn.Module):
    def __init__(self):
        super(Lenet,self).__init__()

        layer1 = nn.Sequential()
        layer1.add_module('conv1', nn.Conv2d(1, 6, 3, padding=1))
        layer1.add_module('pooll', nn.MaxPool2d(2, 2))
        self.layer1 = layer1

        layer2 = nn. Sequential()
        layer2 .add_module('conv2', nn.Conv2d(6, 16, 5))
        layer2 .add_module('poo2', nn.MaxPool2d(2, 2))
        self .layer2 = layer2

        layer3 = nn.Sequential()
        layer3.add_module('fcl', nn.Linear(400, 120))
        layer3 .add_module('fc2', nn.Linear(120, 84))
        layer3 .add_module('fc3', nn.Linear(84, 10))
        self.layer3 = layer3

    def forward(self,x):
        x = self.layerl(x)
        x = self.layer2(x)
        x = x.view(x.size(0),-1)
        x =  self.layer3(x)
        return x

这样就实现了LeNet网络,可以发现网络的层数很浅,也没有添加激活层。

AlexNet

                                                                                                                     AlexNet网络结构图(关于该结构的详解链接:https://www.jianshu.com/p/00a53eb5f4b3

AlexNet网络相对于LeNet,层数更深,同时第一次引入了激活层ReLU,在全连接层引入了Dropout层防止过拟合

from torch import nn


class AlexNet (nn.Module):

    def __init__(self, num_classes):

        super(AlexNet,self).__init__()

        self.features = nn.Sequential(    #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_eize=5, padding=2),
            nn. ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, Btride=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.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256*6*6, 4096),
            nn.ReLU(inplace=True),
            nn. Dropoutl(),
            nn. Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes), )

    def forward(self,x):
        x . self.features(x)
        x- x.view(x.size(0), 256*6* 6)
        x . self.classifier(x)
        return x

VGGNet

VGGNet使用了更小的滤波器和更深的结构,AlexNet只有8层网络,而VGGNet有16-19层网络,也不像AlexNet使用11x11那么大的滤波器,它只使用3x3的卷积滤波器和2x2的大池化层

VGGNet之所以使用很多小的滤波器,是因为层叠很多小的滤波器和感受也和一个大的感受野是相同的,还能减少参数,同时有更深的网络结构

from torch import nn


class VGG (nn .Module):

    def __init__(self, num_classes):

        super(VGG, self).__init__()

        self.features = nn.Sequential(
            nn. Conv2d(3, 64, kernel_size=3, padding=1),
            nn.ReLU(True),
            nn. Conv2d(64, 64, kernel_size=3, padding=1),
            nn. ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn. ReLU(True),
            nn.Conv2d(128, 128, kernel_eize=3,padding=1),
            nn. ReLU(True),
            nn.MaxPool2d(kernel_size=2, atride=2),
            nn.Conv2d(128, 256, kernel_size=3, padding=1),
            nn. ReLU(True),
            nn.Conv2d(256, 256, kermel_siz=3, padding=1),
            nn. ReLU(True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn. ReLU(True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(256, 512, kernel_size=3, padding=1),
            nn.ReLU(True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn. ReLU(True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(True),
            nn .MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn. ReLU(True),
            nn.MaxP0ol2d(kernel_size=2, stride=2),)

        self .classifier = nn. Sequential(
            nn. Linear(512 * 7.7, 4096),
            nn. ReLU(True),
            nn. Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, num_classes), )

        self._initialize_weights()

    def forward(self, x):
        x = self. features(x)
        x = x.view(x.nize(0), -1)
        x = self.claseifier(x)



可以看出,VVG只是对网络层进行不断的堆叠,增加深度确实可以一定程度改善模型效果

实现MNIST手写数字分类

下面写一个多层卷积神经网络,使它在MNIST数据集上达到99%的验证集准确率

from torch import nn


class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        #有4层卷积,2层赤化,卷积之后使用批标准化加快收敛速度,使用ReLU激活函数增加费线性,最后使用全连接层输出分类得分
        self.layer1 = nn.Sequential(
            nn.Conv2d(1, 16, kernel_size=3),# b, 16, 26, 26
            nn.BatchNorm2d(16),
            nn.ReLU(inplace=True))

        self.layer2 = nn.Sequential(
            nn.Conv2d(16, 32, kernel_size=3),# b, 32, 24, 24
            nn.BatchNorm2d(32),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2))#b, 32, 12, 12

        self.layer3 = nn.Sequential(
            nn.Conv2d(32, 64, kernel_size=3), #b, 64, 10, 10
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True))

        self.layer4 = nn.Sequential(
            nn.Conv2d(64, 128, kernel_size=3), #b, 128, 8, 8
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2)#b, 128, 4, 4
            )

        self.fc = nn.Sequential(
            nn.Linear(128*4*4, 1024),
            nn.ReLU(inplace=True),
            nn.Linear(1024, 128),
            nn.ReLU(inplace=True),
            nn.Linear(128, 10))

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值