1 前言

经过前面三篇文章的介绍,我们已经知道了卷积的原理卷积的计算过程以及池化的原理和过程等。在接下来的这篇文章中,笔者将开始介绍如何通过Pytorch这一深度学习框架来实现卷积和池化的操作,以及各个API的使用介绍和说明。最后,笔者还会介绍卷积神经网络的入门级网络结构LeNet5,我们也将会通过LeNet5这个网络来完成fashion mnist数据集的分类任务。

2 API接口介绍

在Pytorch中,对于网络中的一些基本功能函数(例如:卷积、池化、激活函数等)都被放在了torch.nn.functional这个模块中。因此,为了方便后续的使用,按惯例我们都会以import torch.nn.functional as F的方式来导入各类基本功能函数,然后以F.的方式来进行调用。

2.1 卷积操作

在介绍如何使用卷积这一功能函数前,我们先来介绍一下Pytorch中如何表示(类)图像数据。在Pytorch(tensorflow)中,都是通过四个维度来对图像数据进行表示,分别是:样本个数(batch_size)、高度(heigh)、宽度(width)和通道数(channel)。但是对于不同的深度学习框架来说,其在这个维度上的默认顺序并不一样。在Pytorch中,这四个维度的顺序为[batch_size,channels,heigh,width];但是在tensorflow中,这一顺序却为[batch_size,heigh,width,channels]。可以看出,两者仅仅是把通道数这一维度放到了不同的位置上。

同样的,对于卷积核来说也需要用四个维度来进行表示,分别是:高度(heigh)、宽度(width)、上一层输入的通道数(in_channels)和输出特征图的通道数(卷积核个数)(out_channels)。在Pytorch中,这四个维度的顺序为[out_channels,in_channels,heigh,width];而在tensorflow中,这一顺序却是[heigh,width,in_channels,out_channels]。所以,需要注意的就是在使用不同的深度学习框架时一定要弄清楚输入数据的形式。

2.1.1 单卷积核卷积

如图1所示,在上一篇文章中我们以该图中的示例介绍了如何手动的来计算卷积的结果,现在我们看看如何通过框架来进行计算。

图 1. conv2d的使用

首先我们需要将卷入和卷积核这两个变量给定义出来:

inputs = torch.tensor([0, 2, 0, 1, 0, 0, 2, 0, 1, 1, 2, 1, 2, 0, 0, 1, 0, 0, 1, 0, -1, 1, 1, 0, 1,0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0,# [batch_size,in_channels,high,width]1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0]).reshape([1, 3, 5, 5])filters = torch.tensor([[[[2, 0, 0],[1, 0, 1],[0, 3, 0]],[[1, 0, 1],[0, 0, 0],[1, 1, 0]],[[0, 0, 1],[1, 1, 1],[1, 1, 0]]]])  # [1,3,3,3] [ [filter_nums/output_channels,input_channels,high,width]bias = torch.tensor([1])

可以看到,对于inputs来说我们首先定义了75个值,然后再将其reshape成了图像的表示格式;而对于filters来说,我们在定义的时候就直接写成了框架所需要的形式。

接下来,我们仅仅只需要通过下面一行代码就能够完成对于卷积的计算过程:

result = F.conv2d(inputs, filters, bias=bias, stride=1, padding=0)

其中strid=1表示移动一次的步长设为1,padding=0表示不进行填充。

最后,计算得到的结果为:

卷积后的结果: tensor([[[[ 7, 18,  6],[10, 11, 11],[15, 13,  7]]]])
结果的形状: torch.Size([1, 1, 3, 3])

2.1.2 多卷积核卷积

如图2所示为多卷积核的计算场景。同上面一样,我们只需要分别定义出输入和卷积核,通过F.conv2d()函数就能计算得到卷积后的结果。

图 2. conv2d的使用

filters = torch.tensor([[[[2, 0, 0],[1, 0, 1],[0, 3, 0]],[[1, 0, 1],[0, 0, 0],[1, 1, 0]],[[0, 0, 1],[1, 1, 1],[1, 1, 0]]],[[[0, 1, 0],[1, 1, 1],[0, 1, 0]],[[0, 1, 0],[1, 0, 1],[0, 1, 0]],[[1, 0, 1],[0, 1, 0],[1, 0, 1]]]])  # [2,3,3,3] [ [filter_nums/output_channels,input_channels,high,width]
bias = torch.tensor([1, -3])
result = F.conv2d(inputs, filters, padding=0, bias=bias)

最后得到的计算结果为:

卷积后的结果: tensor([[[[ 7, 18,  6],[10, 11, 11],[15, 13,  7]],[[ 6,  5,  5],[ 7,  4,  3],[ 3,  6,  1]]]])
结果的形状: torch.Size([1, 2, 3, 3])

2.2 池化操作

说完了卷积的操作,我们最后再来简单的看看如何进行池化操作,然后就进入到LeNet5网络的介绍。

图 3. 最大池化操作

如图3所示为最大池化的计算原理,在Pytorch中我们在定义好输入的特征图后,通过F.max_pool2d()便可以得到池化后的结果。

feature_maps = torch.tensor([5, 2, 0, 1, 0, 0, 2, 3, 0, 7, 2, 3, 2,2, 1, 1, 6, 4, 8, 1, 2, 7, 1, 5, 9,4, 2, 0, 1, 0, 2, 7, 1, 3, 6, 2, 4, 2,2, 3, 2, 6, 9, 8, 0, 10, 7, 2, 5, 7],dtype=torch.float32).reshape([1, 2, 5, 5])
print(feature_maps)
result = F.max_pool2d(input=feature_maps, kernel_size=[3, 3], stride=[1, 1])
print(result)

其中,kernel_size=[3,3]stride=[1,1]分别表示池化的窗口大小为3×33\times33×3以及移动的步长为111。当然,这种情况下还可以直接写成kernel_size=3,stride=1

最后,计算后的结果为:

tensor([[[[ 5.,  3.,  7.],[ 6.,  8.,  8.],[ 7.,  8.,  9.]],[[ 7.,  7.,  6.],[ 9.,  9.,  9.],[10.,  9.,  9.]]]])

3 LeNet5网络

3.1 网络结构

在介绍完卷积操作后,我们再来看如何实现一个简单的LeNet5网络结构。

图4. LeNet5网络结构图

如图4所示就是LeNet5的网络结构图,顺便插一句这里的5指的是包含有5个网络权重层(不含参数的层不计算在内)的网络层,即两个卷积层和三个全连接层。根据图4中各层输出的结果,我们可以推算得到其各层对应的超参数及卷积核形状应如下表所示:

表 1. LeNet5参数表

网络层 输入形状 参数 输出形状 参数量
卷积层C1 [1,32,32] kernel_size=[5,5,1,6]stride=1 [6,28,28] 5×5×1×6=1505\times5\times1\times6=1505×5×1×6=150
池化层S2 [6,28,28] kernel_size=[2,2]stride=2 [6,14,14] 000
卷积层C3 [6,14,14] kernel_size=[5,5,6,16]stride=1 [16,10,10] 5×5×6×16=24005\times5\times6\times16=24005×5×6×16=2400
池化层S4 [16,10,10] kernel_size=[2,2]stride=2 [16,5,5] 000
全连接层C5 [1,400] weight_size=[400,120] [1,120] 400×120=48000400\times120=48000400×120=48000
全连接层C6 [1,120] weight_size=[120,84] [1,84] 120×84=10080120\times84=10080120×84=10080
输出层 [1,84] weight_size=[84,10] [1,10] 84×10=84084\times10=84084×10=840

从表5可以看出每层权重参数的一个具体情况,包括参数的形状和数量。对于整个LeNet5网络来说,其参数量就应该是150+2400+48000+10080+840=61470150+2400+48000+10080+840=61470150+2400+48000+10080+840=61470

LeNet5的网络结构总体上来说还比较简单,

完整内容点击:卷积池化与LeNet5网络模型

3.2 网络实现

在介绍完了LeNet5的网络结构后,下面我们就来看看如何通过Pytorch框架对其进行实现。首先需要明白的是,我们在利用框架实现一些网络结构时,我们只需要写出网络对应的前向传播过程即可。剩余其它部分的编码基本上就是按部就班,几乎可以进行通用。

  • 前向传播

    class LeNet5(nn.Module):def __init__(self, ):super(LeNet5, self).__init__()self.conv = nn.Sequential(  # [n,1,28,28]nn.Conv2d(1, 6, 5, padding=2),  # (in_channels, out_channels, kernel_size])nn.ReLU(),  # [n,6,24,24]nn.MaxPool2d(2, 2),  # kernel_size, stride  [n,6,14,14]nn.Conv2d(6, 16, 5),  # [n,16,10,10]nn.ReLU(),nn.MaxPool2d(2, 2)  # [n,16,5,5])self.fc = nn.Sequential(nn.Flatten(),nn.Linear(16 * 5 * 5, 120),nn.ReLU(),nn.Linear(120, 84),nn.ReLU(),nn.Linear(84, 10))
    

    如上代码所示就是我们实现的LeNet5网络的前向传播部分,可以看到通过pytorch很容易的就完成了。同时,这里需要说明的是:

    nn.Sequential里面输入的必须是网络层(即要继承自类nn.Module),所以nn.Conv2d(其内部同样是通过F.conv2d来实现的)作为一个网络层其只需要按序传入in_channelsout_channelskernel_size

完整内容点击:卷积池化与LeNet5网络模型

  • 模型配置

    在定义好前向传播后,我们还可以对整个网络(或者是其中一层)的参数设置情况进行输出:

    if __name__ == '__main__':model = LeNet5()print(model)print(model.fc[3])#
    LeNet5((conv): Sequential((0): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))(1): ReLU()(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(3): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))(4): PrintLayer()(5): ReLU()(6): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(fc): Sequential((0): Flatten()(1): Linear(in_features=400, out_features=120, bias=True)(2): ReLU()(3): Linear(in_features=120, out_features=84, bias=True)(4): ReLU()(5): Linear(in_features=84, out_features=10, bias=True))
    )Linear(in_features=120, out_features=84, bias=True)
    

    同时,若是需要查看每一层计算后输出的形状,那么只需要将如下定义的打印层出入到nn.Sequential()中的相应位置即可。

    class PrintLayer(nn.Module):def __init__(self):super(PrintLayer, self).__init__()def forward(self, x):print(x.shape)return x
    
  • 训练结果

    对于其它部分的代码在此就不再赘述,可以直接参见源码[2]。

    Epochs[4/5]---batch[937/550]---acc 0.9219---loss 0.2219
    Epochs[4/5]---batch[937/600]---acc 0.9062---loss 0.2414
    Epochs[4/5]---batch[937/650]---acc 0.8438---loss 0.3668
    Epochs[4/5]---batch[937/700]---acc 0.875---loss 0.3722
    Epochs[4/5]---batch[937/750]---acc 0.9062---loss 0.4362
    Epochs[4/5]---batch[937/800]---acc 0.9688---loss 0.1389
    Epochs[4/5]---batch[937/850]---acc 0.9062---loss 0.3012
    Epochs[4/5]---batch[937/900]---acc 0.9062---loss 0.2476
    Epochs[4/5]--acc on test 0.8703
    

    可以看到,大约在5个Epochs后,模型在测试集上的准确率达到了0.87。

4 总结

在本篇文章中,笔者首先介绍了在pytorch和tensorflow中是如何来表示图片以及在两个框架中的区别;接着介绍了pytorch中卷积和池化操作两个API的使用方法,示例了如何通过pytorch来完成卷积和池化的计算过程;最后介绍了卷积网络中的LeNet5网络,包括模型的结构、参数量的计算以及如何通过pytorch来实现等。在下一篇的文章中,我们将开始学习卷积网络中的第二个经典模型AlexNet。

本次内容就到此结束,感谢您的阅读!如果你觉得上述内容对你有所帮助,欢迎关注并传播本公众号!若有任何疑问与建议,请添加笔者微信’nulls8’或文末留言进行交流。青山不改,绿水长流,我们月来客栈见!

引用

[1] LeCun, Y., Bottou, L., Bengio, Y., & Haffner, P. (1998). Gradient-based learning applied to document recognition. Proceedings of the IEEE, 86(11), 2278-2324.

[2]https://github.com/moon-hotel/DeepLearningWithMe

推荐阅读

  • 看不懂卷积或许只是因为

  • 原来卷积是这么计算的

  • 卷积操作中的填充与池化

卷积池化与LeNet5网络模型相关推荐

  1. 【模型解读】从LeNet到VGG,看卷积+池化串联的网络结构

    文章首发于微信公众号<与有三学AI> [模型解读]从LeNet到VGG,看卷积+池化串联的网络结构 从本篇开始,我们将带领大家解读深度学习中的网络的发展 这是深度学习模型解读第一篇,本篇我 ...

  2. 个人理解卷积 池化 的用处

    卷积 池化 的用处 卷积让局部特征更加明显.更加突出有用的特征. 池化,降低特征纬数,方便运算. 参考: 如何理解卷积神经网络(CNN)中的卷积和池化? - 知乎 CNN中卷积层和池化的作用和理解 - ...

  3. 卷积神经网络系列之卷积/池化后特征图大小怎么计算??

    1.卷积后的大小: W:矩阵宽,H:矩阵高,F:卷积核宽和高,P:padding(需要填充的0的个数),N:卷积核的个数,S:步长 width:卷积后输出矩阵的宽,height:卷积后输出矩阵的高 w ...

  4. 【数据挖掘】卷积神经网络 ( 池化 | 丢弃 | 批量规范化 | 卷积神经网络完整流程示例 | 卷积 | 池化 | 全连接 | 输出 | 卷积神经网络总结 )

    文章目录 I . 池化 II . 丢弃操作 III . 批量规范化 IV . 卷积神经网络 完整流程示例 ( 1 ) : 原始输入图 V . 卷积神经网络 完整流程示例 ( 2 ) : 卷积层 C1C ...

  5. 人工智能-作业5:卷积-池化-激活

    目录 实现[卷积-池化-激活]代码,并分析总结 For循环版本:手工实现 Pytorch版本:调用函数完成 卷积 池化 激活 可视化:了解数字与图像之间的关系 运行效果如下: 原图 卷积核 特征图 总 ...

  6. 一个在线卷积池化计算器-推荐不会计算的同学

    很多刚入门的身边的小伙伴不会计算卷积和池化,于是为了方便大家计算顺便根据卷积池化计算公式设计了一个在线计算器 卷积池化计算器地址 如果你很懒的话,可以用这个计算.

  7. 【卷积神经网络--卷积/池化后特征图大小计算公式】

    [卷积神经网络–卷积/池化后特征图大小计算公式] 参考链接: 卷积神经网络系列之卷积/池化后特征图大小怎么计算?? torch中的Conv2d卷积的输入的Tensor张量是:[batch, chann ...

  8. 卷积神经网络卷积,池化,全连接--笔记01

    1.图像是由像素构成的,每个像素又是由颜色构成的 假如我们处理一张 1000×1000 像素的图片,我们就需要处理3百万个参数! 1000×1000×3=3,000,000 卷积神经网络 - CNN ...

  9. 卷积池化计算 深度学习

    卷积后图片输出大小 W:图像宽,H:图像高,D:图像深度(通道数) F:卷积核宽高,N:卷积核(过滤器)个数 S:步长,P:用零填充个数 卷积后输出图像大小: Width=(W-F+2P)/S+1He ...

最新文章

  1. 基于矩阵分解的推荐算法
  2. nginx防止跨站 lnmp多站点安全设置之一
  3. 21-爬虫之scrapy框架selenium的使用08
  4. 诗与远方:无题(十八)
  5. Socket选项系列之TCP_NODELAY
  6. 网络故障排除连载之四:OSPF故障排除
  7. ubuntu16.04 pytorch 安装
  8. 沙迪克UTY编程基础操作流程
  9. 验证二叉树的前序序列化Python解法
  10. 句法分析(成分句法分析)(依存句法分析)
  11. 服务器邮箱验证失败是什么意思,发送邮件时,提示“邮件发送失败:SMTP验证失败”?...
  12. java读取文件是乱码_java读取txt文件乱码解决方案
  13. 2020大厂到底有多少程序员?腾讯研发人员占比68%
  14. Java Vs. C♯
  15. 深圳市林享科技有限公司
  16. Peer-to-Peer 综述
  17. mysql 百万级别 更新_mysql百万级数据高效插入和更新
  18. 基于c52万年历c语言,基于STC89C52单片机的可测温式电子万年历
  19. 关于CC2541蓝牙开发板的学习笔记-1
  20. 接班马云的为何是张勇?

热门文章

  1. Spine学习八 - 幻影特效
  2. Java NIO - IO多路复用
  3. python string转date类型_python转换字符串为datetime类型
  4. 基于Springboot的健身房课程预约平台
  5. C语言文件操作实现登录注册
  6. css 调字符间距和段首缩进
  7. iOS中关于行间距lineSpacing 行高lineHeight
  8. Mac 下使用 Aria2 实现迅雷离线和百度云下载
  9. Stripe支付 ios
  10. python对excel操作简书_Python 操作Excel