卷积神经网络概念及使用 PyTorch 简单实现

卷积神经网络

  卷积神经网络(CNN)是深度学习的表明算法之一  。具备表征学习能力,可以按其阶层结构对输入信息进行平移不变分类,所以也被称为“平移不变人工神经网络”随着深度学习理论的提出和数值计算设备的改进,卷积神经网络获得了快速发展,并被应用于 计算机视觉天然语言处理等领域

  卷积是经过两个函数 f,g 生成第三个函数的一种数学算子,表征函数 f 与 g 通过翻转和平移的重叠部分的面积。数学定义公式:html

  事实上,在卷积网络上使用的离散卷积,也就是不连续的,它是一种运算方式,也就是按照卷积核,将输入对应位置的数据进行加权和运算,接下来结合卷积核的概念,就会很好理解了。算法

  卷积神经网络利用卷积结构减小须要学习的参数量,从而提升反向传播算法的训练效率。在卷积神经网络中,第一个卷积层会直接接受图像像素级的输入,每个卷积操做只处理一小块图像,进行卷积操做后传递到后面的网络,每一层卷积都会提取数据中最有效的特征。这种方法能够提取到图像中最基础的特征,好比不一样方向的拐角或者边,然后进行组合和抽象成更高阶的特征,所以卷积神经网络对图像缩放、平移和旋转具备不变性。数组

卷积神经网络的要点就是卷积层中的局部链接、权值共享、和池化层中降采样。局部链接、权值共享和降采样下降了参数量,使得训练复杂度大大下降,并减轻了过拟合的风险。同时还赋予了卷积神经网络对平移、形变、尺度的某种程度的不变性,提升了模型的泛化能力网络

 

 卷积神经网络最重要的两个知识点就是卷积核卷积神经网络的结构框架

  • 卷积核
    • 卷积核定义
    • 卷积操做
    • 深度
    • 步幅
    • 零填充
  • 卷积神经网络的结构
    • 输入层 INPUT
    • 卷积层 CONV
    • 激活函数层 RELU
    • 池化层 POOL
    • 全链接层 FC

 

卷积核

​  卷积核是整个网络的核心,训练 CNN 的过程就是不断更新卷积核参数直到最优的过程。
less

  卷积核的定义:对于输入图像中的一部分区域,进行加权平均的处理,其中这个过程的权重,由一个函数定义,这个函数就是卷积核。ide

以下图彩色图像有RGB三个色值通道,分别表示红、绿、蓝,每一个通道内的像素能够用一个像下图右边的二维数组表示,数值表明0-255之间的像素值。假设一张900*600的彩色的图片,计算机里面能够用 (900*600*3)的数组表示。函数

卷积过程学习

  卷积过程是基于一个小矩阵,也就是卷积核,在上面所说的每层像素矩阵上不断按步长扫过去的,扫到数与卷积核对应位置的数相乘,而后求总和,每扫一次,获得一个值,所有扫完则生成一个新的矩阵。以下图测试

  卷积核如何设置能够参考卷积神经网络的卷积核大小、个数。卷积层数通常取(3,3)的小矩阵,卷积核里面每一个值就是咱们须要寻找(训练)的神经元参数(权重),开始会随机有个初始值,当训练网络时,网络会经过后向传播不断更新这些参数值,直到寻找到最佳的参数值。“最佳”、须要经过损失函数去评估。

  卷积操做至关于特征提取,卷积核至关于一个过滤器,提取咱们须要的特征。

以下图卷积操做,从左上角扫到右下角,最终获得右边的特征图谱。

 

  图解:一个过滤器(红色边框)在输入图像上移动(卷积操做)以生成特征映射。在同一张图像上,另外一个过滤器(绿色边框)的卷积生成了不一样的特征图。须要注意到,卷积操做捕获原始图像中的局部依赖关系很重要。还要注意这两个不一样的过滤器如何从同一张原始图像获得不一样的特征图。请记住,以上图像和两个过滤器只是数值矩阵。
  实际上,卷积神经网络在训练过程当中会本身学习这些过滤器的值(尽管在训练过程以前咱们仍须要指定诸如过滤器数目、大小,网络框架等参数)。过滤器数目越多,提取的图像特征就越多,识别新图像时效果就会越好。
 
特征映射(卷积特征)的大小由执行卷积步骤以前须要决定的三个参数控制:
  • 深度:深度对应于咱们用于卷积运算的过滤器数量。在图7所示的网络中,咱们使用三个不一样的过滤器对初始的船图像进行卷积,从而生成三个不一样的特征图。能够将这三个特征地图视为堆叠的二维矩阵,所以,特征映射的“深度”为3。
  • 步幅:步幅是在输入矩阵上移动一次过滤器矩阵的像素数量。当步幅为1时,咱们一次将过滤器移动1个像素。当步幅为2时,过滤器每次移动2个像素。步幅越大,生成的特征映射越小。有横行和纵向两个方向
  • 零填充:有时,将输入矩阵边界用零来填充会很方便,这样咱们能够将过滤器应用于输入图像矩阵的边界元素。零填充一个很好的特性是它容许咱们控制特征映射的大小。添加零填充也称为宽卷积,而不使用零填充是为窄卷积。

零填充(padding)

  卷积操做以后维度会变少,获得的矩阵会比原来矩阵小,这样很差计算,因此须要Padding,在每次卷积操做以前,在原矩阵外边补包一层0,能够只在横向补,或只在纵向补,或者四周都补0,从而使得卷积后输出的图像跟输入图像在尺寸上一致。

好比:须要作一个5*5的原始矩阵的卷积,用一个3*3卷积核来扫,扫出来结果的矩阵应该是:3*3的矩阵,变小了。

卷积前加 Padding 操做补一圈0,即300*300矩阵外面周围加一圈“0”,这样的300*300就变成了302*302的矩阵,再进行卷积出来就是300*300 ,尺寸和原图同样。

 

卷积神经网络结构

  CNN 通常来讲分为五个部分:输入层卷积层激活函数层池化层全链接层

  不过注意须要注的点是,对于大部分卷积网络,都会交替地用到中间地四层结构,也就是呈现出一种 卷积层——激活函数层——池化层——卷积层——激活函数层——池化层…地交替结构,固然,对于一些新出现地卷积网络,连池化层都省去了,以上五层结构只是通常会出现的层次。

 

输入层

  整个网络的输入,通常是一张图象地像素矩阵,在上面的图中,能够看到输入是一个立体的结构,这是由于通常的图像都会有一个深度的概念,就像RGB的彩色图像,就是 a*b*c 的形式,其中前两维指定的是图像的长和宽,第三维则是深度,彩色RGB的深度是3,而黑白图像的深度是 1

 

卷积层

  这一层的主要部分就是进行卷积操做,前面已经介绍了卷积核的概念,卷积层实际上就是实现了这个卷积核的计算过程,在这一层中,可能会见到如下的几种关键词:

  • 滤波器 Filter:实现前面定义的卷积核的神经元。
  • 步长 Stride
  • 填充 Padding
  • 深度 Depth:这里的深度不是指图像,而是指某一层中神经元(滤波器)的个数,不一样的 Filter 重点处理的特征是不一样的咱们想要获得不一样的这些特征图,因此设置了多个 Filter ,这样每一个 Filter 处理获得一张 Feature Map ,多个 Filter 就会获得多个Feature Map, 将这些Feature Map 叠在一块儿就是输出的立体,能够看到,Filter 与 Feature Map的数量是同样的,这个数量,就是 深度。

 在PyTorch中, 类nn.Conv2d()是卷积核模块。卷积核及其调用例子以下:

# 调用形式
nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0,dilation=1,groups=1, bias=True)
'''
  nn.Conv2d中参数含义:
  in_channels表示输入数据体的深度;
  out_channels表示输出数据体的深度;
  kernel_size 表示卷积核的大小;
  stride表示滑动的步长;
  padding表示边界0填充的个数;
  dilation表示输入数据体的空间间隔;
  groups 表示输入数据体和输出数据体在深度上的关联;
  bias 表示偏置。
'''

# With square kernels and equal stride
m = nn.Conv2d(16, 33, 3, stride=2)
# non-square kernels and unequal stride and with padding
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
# non-square kernels and unequal stride and with padding and dilation
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
nput = autograd.Variable(torch.randn(20, 16, 50, 100))
output = m(input)

 

激励函数层

  实际中,使用激活函数处理每每是与以前的卷积层绑定在一块儿,这个做用其实也就是是激活函数的做用,去线性化,在卷积网络中,通常使用的激励函数是 ReLu 函数,注意大多数状况下都不会使用 Sigmoid 函数处理。

 

池化层

  做用在 Feature Map 上,至关于对输入矩阵的尺寸进一步浓缩,也就是进一步提取特征。卷积操做后咱们提取了不少特征信息,相邻区域有类似特征信息,能够相互替代的,若是所有保留这些特征信息就会有信息冗余,增长了计算难度,这时候池化就至关于降维操做。池化是在一个小矩阵区域内,取该区域的最大值或平均值来代替该区域,该小矩阵的大小能够在搭建网络的时候本身设置。小矩阵也是从左上角扫到右下角。以下图

 

池化层有如下几个功能:

  1. 对 Feature Map 又进行一次特征提取,这也是减少数据量的操做
  2. 获取更抽象的特征,防止过拟合,提升泛化性
  3. 通过这个处理,对输入的微小变化有更大的容忍,也就是说若是数据有一些噪音,那么通过这个特征提取的过程,就必定程度上减少了噪音的影响。

最后一点要注意的是,池化层并不是是卷积网络所必需的。一些新的CNN网络设计时候并无使用池化层。

在PyTorch中,池化层是包括在类nn.MaxPool2d和nn.AvgPoo2d。下面介绍一下nn.MaxPool2d及其调用例子。其调用以下

nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1,return_indices=False,ceil_mode=False)
'''
    nn.MaxPool2d中各个参数的含义:
    kernel_size, stride,padding, dilation在nn.Conv2d中已经解释过。
    return_indices表示是否返回最大值所处的下标;
    ceil_model表示使用方格代替层结构
'''

# pool of square window of size=3, stride=2
m = nn.MaxPool2d(3, stride=2)
# pool of non-square window
m = nn.MaxPool2d((3, 2), stride=(2, 1))
input = autograd.Variable(torch.randn(20, 16, 50, 32))
output = m(input)

 

全链接层

  在一开始的结构图中能够看出, CNN网络还有一个特色 : 可能有多轮 卷积层 和池化层的处理,这时候,模型已经将图像输入处理成了信息含量更高的特征,为了实现分类的任务(通常是分类,固然也会有其余的任务),须要使用全链接层来完成分类任务。

  对n-1层和n层而言,n-1层的任意一个节点,都和第n层全部节点有链接。即第n层的每一个节点在进行计算的时候,激活函数的输入是n-1层全部节点的加权。像下面的中间层就是全链接方式。

 

 

使用PyTorch实现卷积神经网络

  使用PyTorch实现一个简单的卷积神经网络,使用的数据集是 MNIST,预期能够达到 97.05%左右的准确率。该神经网络由2个卷积层和3个全链接层构建,经过这个例子能够掌握设计卷积神经网络的特征以及参数的配置。

# coding=utf-8
# 配置库
import torch
from torch import nn, optim
import torch.nn.functional as F
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets

# 配置参数
torch.manual_seed(1)  # 设置随机数种子,确保结果可重复  
batch_size = 128  # 批处理大小
learning_rate = 1e-2  # 学习率
num_epoches = 10  # 训练次数 

# ---------------------- 加载MINSIT数据 ----------------------
# 下载训练集 MNIST 手写数字训练集
train_dataset = datasets.MNIST(
    root='./data',  # 数据保持的位置
    train=True,  # 训练集 
    transform=transforms.ToTensor(),  # 一个取值范围是[0,255]的PIL.Image
    # 转化为取值范围是[0,1.0]的torch.FloadTensor
    download=True)  # 下载数据

test_dataset = datasets.MNIST(
    root='./data',
    train=False,  # 测试集
    transform=transforms.ToTensor())


# ---------------------- 数据的批处理 ----------------------
# 数据的批处理,尺寸大小为batch_size, 
# 在训练集中,shuffle 必须设置为True, 表示次序是随机的
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)


# ---------------------- 建立CNN模型 ----------------------
# 一个类来创建CNN模型.这个CNN总体流程是:
# 卷积(Conv2d) -> 激励函数(ReLU) -> 池化, 向下采样(MaxPooling) -> 再来一遍 -> 展开多维的卷积成的特征图 -> 接入全链接层(Linear) -> 输出。

# 定义卷积神经网络模型
class Cnn(nn.Module):
    def __init__(self, in_dim, n_class):  # 1x28x28
        super(Cnn, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(in_dim, 6, 3, stride=1, padding=1),  # 28 x 28 
            nn.ReLU(True),
            nn.MaxPool2d(2, 2),  # 14 x 14
            nn.Conv2d(6, 16, 5, stride=1, padding=0),  # 16 x 10 x 10
            nn.ReLU(True), nn.MaxPool2d(2, 2))  # 16x5x5

        self.fc = nn.Sequential(
            nn.Linear(400, 120),  # 400 = 16 x 5 x 5 
            nn.Linear(120, 84),
            nn.Linear(84, n_class))

    def forward(self, x):
        out = self.conv(x)
        out = out.view(out.size(0), 400)  # 400 = 16 x 5 x 5  
        out = self.fc(out)
        return out


model = Cnn(1, 10)  # 图片大小是28x28, 10是数据的种类
# 打印模型,呈现网络结构
print(model)


# ---------------------- 训练 ----------------------
# 将img, label都用Variable包起来, 而后放入model中计算out, 最后再计算less和正确率.

# 定义loss和optimizer
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)

# 开始训练
for epoch in range(num_epoches):
    running_loss = 0.0
    running_acc = 0.0
    for i, data in enumerate(train_loader, 1):  # 批处理
        img, label = data
        img = Variable(img)
        label = Variable(label)
        # 前向传播 
        out = model(img)
        loss = criterion(out, label)  # loss 
        running_loss += loss.data[0] * label.size(0)  # total loss , 因为loss 是batch 取均值的,须要把batch size 乘回去
        _, pred = torch.max(out, 1)  # 预测结果
        num_correct = (pred == label).sum()  # 正确结果的num
        # accuracy = (pred == label).float().mean() #正确率
        running_acc += num_correct.data[0]  # 正确结果的总数
        # 后向传播
        optimizer.zero_grad()  # 梯度清零,以避免影响其余batch
        loss.backward()  # 后向传播,计算梯度
        optimizer.step()  # 利用梯度更新 W ,b参数

    # 打印一个循环后,训练集合上的loss 和正确率
    print('Train {} epoch, Loss: {:.6f}, Acc: {:.6f}'.format(
        epoch + 1, running_loss / (len(train_dataset)), running_acc / (len(
            train_dataset))))


# ---------------------- 在测试集测试识别率 ----------------------
# 模型测试, 
model.eval()  # 因为训练和测试 BatchNorm, Dropout配置不一样,须要说明是否模型测试
eval_loss = 0
eval_acc = 0
for data in test_loader:  # test set 批处理
    img, label = data

    img = Variable(img, volatile=True)  # volatile 肯定你是否不调用.backward(), 测试中不须要
    label = Variable(label, volatile=True)
    out = model(img)  # 前向算法 
    loss = criterion(out, label)  # 计算 loss
    eval_loss += loss.data[0] * label.size(0)  # total loss
    _, pred = torch.max(out, 1)  # 预测结果
    num_correct = (pred == label).sum()  # 正确结果
    eval_acc += num_correct.data[0]  # 正确结果总数

print('Test Loss: {:.6f}, Acc: {:.6f}'.format(eval_loss / (len(
    test_dataset)), eval_acc * 1.0 / (len(test_dataset))))

'''
最后的训练和测试上loss和识别率分别是:
Train 1 epoch, Loss: 2.226007, Acc: 0.320517
Train 2 epoch, Loss: 0.736581, Acc: 0.803717
Train 3 epoch, Loss: 0.329458, Acc: 0.901117
Train 4 epoch, Loss: 0.252310, Acc: 0.923550
Train 5 epoch, Loss: 0.201800, Acc: 0.939000
Train 6 epoch, Loss: 0.167249, Acc: 0.949550
Train 7 epoch, Loss: 0.145517, Acc: 0.955617
Train 8 epoch, Loss: 0.128391, Acc: 0.960817
Train 9 epoch, Loss: 0.117047, Acc: 0.964567
Train 10 epoch, Loss: 0.108246, Acc: 0.966550
Test
Loss: 0.094209, Acc: 0.970500
'''

 

 

 

 

参考:http://www.sohu.com/a/241338315_787107

参考:http://www.javashuo.com/article/p-mfoladsn-mn.html

参考:https://www.cnblogs.com/wmr95/articles/7814892.html

相关文章
相关标签/搜索