卷积神经网络简介
卷积神经网络(Convolutional Neural Network,CNN或ConvNet)是一种具有局部连接、权重共享等特性的深层前馈神经网络。卷积神经网络最早是主要用来处理图像信息。如果用全连接前馈网络来处理图像时,会存在以下两个问题:(1)参数太多(2)局部不变性特征
卷积神经网络是受生物学上感受野的机制而提出。感受野(Receptive Field)主要是指听觉、视觉等神经系统中一些神经元的特性,即神经元只接受其所支配的刺激区域内的信号。在视觉神经系统中,视觉皮层中的神经细胞的输出依赖于视网膜上的光感受器。视网膜上的光感受器受刺激兴奋时,将神经冲动信号传到视觉皮层,但不是所有视觉皮层中的神经元都会接受这些信号。一个神经元的感受野是指视网膜上的特定区域,只有这个区域内的刺激才能够激活该神经元。
目前的卷积神经网络一般是由卷积层、汇聚层和全连接层交叉堆叠而成的前馈神经网络,使用反向传播算法进行训练。卷积神经网络有三个结构上的特性:局部连接,权重共享以及汇聚。这些特性使得卷积神经网络具有一定程度上的平移、缩放和旋转不变性。和前馈神经网络相比,卷积神经网络的参数更少。
2、卷积名词杂烩
卷积(Convolution):是一种数学操作。
滤波器(filter):
简单移动平均:一般情况下滤波器的长度m远小于信号序列长度n。当滤波器wk = 1/m, 1 ≤ k ≤ m时,卷积相当于信号序列的简单移动平均(窗口大小为m)。
特征映射(Feature Map):在图像处理中,卷积经常作为特征提取的有效方法。一幅图像在经过卷积操作后得到结果
互相关(Cross-Correlation):互相关(Cross-Correlation)是一个衡翻转就是从两个维度(从上到下、从左到右)颠倒次序,即旋转180 度。量两个序列相关性的函数,通常是用滑动窗口的点积计算来实现。
滤波器的步长(Stride):指滤波器在滑动时的时间间隔。
零填充(Zero Padding):是在输入向量两端进行补零。
汇聚层(Pooling Layer):汇聚层(Pooling Layer)也叫子采样层(Subsampling Layer),其作用是进行特征选择,降低特征数量,并从而减少参数数量。
汇聚(Pooling):对每个区域进行下采样(Down Sampling)得到一个值,作为这个区域的概括。
净输入:没有经过非线性激活函数的净活性值(Net Activation)。
3、卷积操作辨别:
卷积与互相关的区别仅仅在于卷积核是否进行翻转,也可以理解为图像是否进行翻转
窄卷积与宽卷积、等宽卷积:
窄卷积:步长s = 1,两端不补零p = 0,卷积后输出长度为n − m + 1。
宽卷积:步长s = 1,两端补零p = m − 1,卷积后输出长度n + m − 1。
等宽卷积:步长s = 1,两端补零p = (m −1)/2,卷积后输出长度n。
Pytorch构建CNN模型
在上一章节我们讲解了如何使用Pytorch来读取赛题数据集,本节我们使用本章学习到的知识构件一个简单的CNN模型,完成字符识别功能。
在Pytorch中构建CNN模型非常简单,只需要定义好模型的参数和正向传播即可,Pytorch会根据正向传播自动计算反向传播。
在本章我们会构建一个非常简单的CNN,然后进行训练。这个CNN模型包括两个卷积层,最后并联6个全连接层进行分类
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
# 定义模型
class SVHN_Model1(nn.Module):
def __init__(self):
super(SVHN_Model1, self).__init__()
# CNN提取特征模块
self.cnn = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=(3, 3), stride=(2, 2)),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2)),
nn.ReLU(),
nn.MaxPool2d(2),
)
#
self.fc1 = nn.Linear(32*3*7, 11)
self.fc2 = nn.Linear(32*3*7, 11)
self.fc3 = nn.Linear(32*3*7, 11)
self.fc4 = nn.Linear(32*3*7, 11)
self.fc5 = nn.Linear(32*3*7, 11)
self.fc6 = nn.Linear(32*3*7, 11)
def forward(self, img):
feat = self.cnn(img)
feat = feat.view(feat.shape[0], -1)
c1 = self.fc1(feat)
c2 = self.fc2(feat)
c3 = self.fc3(feat)
c4 = self.fc4(feat)
c5 = self.fc5(feat)
c6 = self.fc6(feat)
return c1, c2, c3, c4, c5, c6
model = SVHN_Model1()
接下来是训练代码:
# 损失函数
criterion = nn.CrossEntropyLoss()
# 优化器
optimizer = torch.optim.Adam(model.parameters(), 0.005)
loss_plot, c0_plot = [], []
# 迭代10个Epoch
for epoch in range(10):
for data in train_loader:
c0, c1, c2, c3, c4, c5 = model(data[0])
loss = criterion(c0, data[1][:, 0]) + \
criterion(c1, data[1][:, 1]) + \
criterion(c2, data[1][:, 2]) + \
criterion(c3, data[1][:, 3]) + \
criterion(c4, data[1][:, 4]) + \
criterion(c5, data[1][:, 5])
loss /= 6
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_plot.append(loss.item())
c0_plot.append((c0.argmax(1) == data[1][:, 0]).sum().item()*1.0 / c0.shape[0])
print(epoch)