浅谈卷积神经网络及实现


前言

卷积神经网络CNN是深度学习中的基础知识。本文对CNN的基础原理及常见的CNN网络进行了详细解读,并介绍了Pytorch构建深度网络的流程。最后对Pytorch构建CNN模型进行实现。


一、CNN原理

CNN,又称卷积神经网络,是深度学习中重要的一个分支。CNN在很多领域都表现优异,精度和速度比传统计算学习算法高很多。特别是在计算机视觉领域,CNN是解决图像分类、图像检索、物体检测和语义分割的主流模型。

1. 卷积
如下图所示,图中的X和O无论怎么旋转或者缩放,人眼其实还是很容易识别出X和O。
在这里插入图片描述但是计算机不同,它看到的其实是一个个的像素阵列,如下图。如何对像素的阵列进行特征的提取其实就是卷积神经网络要干的事情。
在这里插入图片描述再看下图,我们发现X即使进行了旋转,但是绿、橙、紫框标记的区域在两张图中还是一致的,某种程度上,这其实就是X的特征。
在这里插入图片描述因此可以将这三个特征的区间提取出来,就形成了三个卷积核,如图所示。
在这里插入图片描述既然有了卷积核,那么卷积核是如何进行卷积操作的呢?其实很简单,可以看一下图,卷积核其实就是拿着这个矩阵在图片的矩阵上一点点的平移,就像扫地一样。每扫到一处地方就可以进行卷积的运算,计算方法很简单,如图所示,左上角的卷积核扫到绿色框的位置,则卷积核矩阵的数字就和扫到的位置的矩阵的数字一一对应相乘然后相加,最后取一个均值,该值就是卷积核提取的特征。
在这里插入图片描述卷积核提取的所有的特征组成了一个长和宽变小的矩阵,这个矩阵又称为feature map,如图。使用不同的卷积核也就能提取出不同的feature map。所以可以想象的是,如果不断的进行卷积操作,那么图片的矩阵会逐步地长宽减少,厚度增加。
在这里插入图片描述可以看到卷积操作通过卷积核是可以分别提取到图片的特征的,但是如何提前知道卷积核呢?像上文的例子,很容易可以找到3个卷积核,但是假如是人脸识别这样成千上万个特征的图片,就没办法提前知道什么是合适的卷积核。其实也没必要知道,因为选择什么样的卷积核,完全可以通过训练不断优化。初始时只需要随机设置一些卷积核,通过训练,模型其实自己可以学习到合适的卷积核,这也是卷积神经网络模型强大的地方。

2. 池化(pooling)
池化,也叫下采样,本质上其实就是对数据进行一个缩小。因为我们知道,比如人脸识别,通过卷积操作得到成千上万个feature map,每个feature map也有很多的像素点,这些对于后续的运算的时间会变得很长。池化其实就是对每个feature map进一步提炼的过程。如图所示,原来4X4的feature map经过池化操作之后就变成了更小的2*2的矩阵。池化的方法包括max pooling,即取最大值,以及average pooling,即取平均值。
在这里插入图片描述
3. Normalization
这里的Normalization就是将矩阵中负数的值转成0,也就是使用一个称之为ReLu的激活函数进行负数变为0的操作。ReLu函数本质上就是max(0,x)。这一步其实也是为了方便运算。

4. 卷积神经网络理解
因此卷积、ReLu、pooling,不断重复其实也就基本上构成了卷积神经网络的框架,如图。然后将最终得到的feaure map 排成一列(如下图),接到全连接层,这样就形成了我们的卷积神经网络。值得注意的是,排成一列的数值,是有权重,而这些权重是通过训练、反向传播得到的,通过权重的计算,可以知道不同分类的概率是怎么样的。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

二、卷积神经网络

1.卷积神经网络基础:LeNet5
手写字体识别模型LeNet5诞生于1994年,是最早的卷积神经网络之一。LeNet5通过巧妙的设计,利用卷积、参数共享、池化等操作提取特征,避免了大量的计算成本,最后再使用全连接神经网络进行分类识别,这个网络也是最近大量神经网络架构的起点。
如下图所示为LeNet网络结构,总共有7层网络(不含输入层),2个卷积层、2个池化层、3个全连接层。
在这里插入图片描述LeNet分为卷积层块和全连接层块两个部分。下面我们分别介绍这两个模块。卷积层块里的基本单位是卷积层后接最大池化层:卷积层用来识别图像里的空间模式,如线条和物体局部,之后的最大池化层则用来降低卷积层对位置的敏感性。卷积层块由两个这样的基本单位重复堆叠构成。在卷积层块中,每个卷积层都使用5x5的窗口,并在输出上使用sigmoid激活函数。第一个卷积层输出通道数为6,第二个卷积层输出通道数则增加到16。这是因为第二个卷积层比第一个卷积层的输入的高和宽要小,所以增加输出通道使两个卷积层的参数尺寸类似。卷积层块的两个最大池化层的窗口形状均为2*2,且步幅为2。由于池化窗口与步幅形状相同,池化窗口在输入上每次滑动所覆盖的区域互不重叠。

卷积层块的输出形状为(批量大小, 通道, 高, 宽)。当卷积层块的输出传入全连接层块时,全连接层块会将小批量中每个样本变平(flatten)。也就是说,全连接层的输入形状将变成二维,其中第一维是小批量中的样本,第二维是每个样本变平后的向量表示,且向量长度为通道、高和宽的乘积。全连接层块含3个全连接层。它们的输出个数分别是120、84和10,其中10为输出的类别个数。

在卷积层块中输入的高和宽在逐层减小。卷积层由于使用高和宽均为5的卷积核,从而将高和宽分别减小4,而池化层则将高和宽减半,但通道数则从1增加到16。全连接层则逐层减少输出个数,直到变成图像的类别数10。通过多次卷积和池化,CNN的最后一层将输入的图像像素映射为具体的输出。如在分类任务中会转换为不同类别的概率输出,然后计算真实标签与CNN模型的预测结果的差异,并通过反向传播更新每层的参数,并在更新完成后再次前向传播,如此反复直到训练完成 。

三、Pytorch构建模型

import torch
torch.manual_seed(0)
torch.backends.cudnn.deterministic= False
torch.backends.cudnn.benchmark = True

import torchvision.models as models
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
from torch.utils.data.dataset import Dataset

1.pytorch常用网络
1.1 Linear介绍 [全连接层]

nn.Linear(input_feature,out_feature,bias=True)

1.2 卷积介绍 [2D卷积层]

nn.Conv2d(in_channels,out_channels,kernel_size,stride=1,padding=0,
dilation=1,groups,bias=True,padding_mode='zeros')

##kernel_size,stride,padding 都可以是元组
## dilation 为在卷积核中插入的数量

1.3 转置卷积介绍 [2D反卷积层]

nn.ConvTranspose2d(in_channels,out_channels,kernel_size,stride=1,
padding=0,out_padding=0,groups=1,bias=True,dilation=1,padding_mode='zeros')

##padding是输入填充,out_padding填充到输出

1.4 最大值池化层 [2D池化层]

nn.MaxPool2d(kernel_size,stride=None,padding=0,dilation=1)

1.5 批量归一化层 [2D归一化层]

nn.BatchNorm2d(num_features,eps,momentum,affine=True,
track_running_stats=True)

affine=True 表示批量归一化的α,β是被学到的
track_running_stats=True 表示对数据的统计特征进行关注

2. pytorch 创建模型的四种方法
假设创建卷积层 → \rightarrow Relu层 → \rightarrow 池化层 → \rightarrow 全连接层 → \rightarrow Relu层 → \rightarrow 全连接层


# 导入包
import torch
import torch.nn.functional as F
from collections import OrderedDict

2.1.自定义型[定义在init,前向过程在forward]

class Net1(torch.nn.Module):
    def __init__(self):
      super(Net1, self).__init__()
      self.conv1 = torch.nn.Conv2d(3, 32, 3, 1, 1)
      self.dense1 = torch.nn.Linear(32 * 3 * 3, 128)
      self.dense2 = torch.nn.Linear(128, 10)

    def forward(self, x):
      x = F.max_pool2d(F.relu(self.conv(x)), 2)
      x = x.view(x.size(0), -1)
      x = F.relu(self.dense1(x))
      x = self.dense2(x)
    return x

2.2 序列集成型[利用nn.Squential(顺序执行的层函数)]访问各层只能通过数字索引

class Net2(torch.nn.Module):
    def __init__(self):
        super(Net2, self).__init__()
        self.conv = torch.nn.Sequential(
        torch.nn.Conv2d(3, 32, 3, 1, 1),
        torch.nn.ReLU(),
        torch.nn.MaxPool2d(2))
        self.dense = torch.nn.Sequential(
        torch.nn.Linear(32 * 3 * 3, 128),
        torch.nn.ReLU(),
        torch.nn.Linear(128, 10)
        )

    def forward(self, x):
        conv_out = self.conv(x)
        res = conv_out.view(conv_out.size(0), -1)
        out = self.dense(res)
        return out

2.3 序列添加型[利用Squential类add_module顺序逐层添加]给予各层的name属性

class Net3(torch.nn.Module):
    def __init__(self):
        super(Net3, self).__init__()
        self.conv=torch.nn.Sequential()
        self.conv.add_module("conv1",torch.nn.Conv2d(3, 32, 3, 1, 1))
        self.conv.add_module("relu1",torch.nn.ReLU())
        self.conv.add_module("pool1",torch.nn.MaxPool2d(2))
        self.dense = torch.nn.Sequential()
        self.dense.add_module("dense1",torch.nn.Linear(32 * 3 * 3, 128))
        self.dense.add_module("relu2",torch.nn.ReLU())
        self.dense.add_module("dense2",torch.nn.Linear(128, 10))

    def forward(self, x):
        conv_out = self.conv(x)
        res = conv_out.view(conv_out.size(0), -1)
        out = self.dense(res)
        return out

2.4 序列集成字典型[OrderDict集成模型字典【‘name’:层函数】]name为key

class Net4(torch.nn.Module):
    def __init__(self):
        super(Net4, self).__init__()
        self.conv = torch.nn.Sequential(
        OrderedDict(
        [
        ("conv1", torch.nn.Conv2d(3, 32, 3, 1, 1)),
        ("relu1", torch.nn.ReLU()),
        ("pool", torch.nn.MaxPool2d(2))
        ]
        ))

        self.dense = torch.nn.Sequential(
        OrderedDict([
        ("dense1", torch.nn.Linear(32 * 3 * 3, 128)),
        ("relu2", torch.nn.ReLU()),
        ("dense2", torch.nn.Linear(128, 10))
        ])
        )

    def forward(self, x):
        conv_out = self.conv1(x)
        res = conv_out.view(conv_out.size(0), -1)
        out = self.dense(res)
        return out

总结

待到秋来九月八,我花开后百花杀。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值