本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com
卷积神经网络CNN是深度学习中用于图像识别的主要模型,是深度学习中的核心成员之一
本文讲解为什么需要卷积神经网络,卷积神经网络的思想是什么,以及卷积神经网络的基本结构
通过本文可以对卷积神经网络有个初步的认识,知道卷积神经网络是个什么东西,用于解决什么问题
本节讲解卷积神经网络诞生的背景,即为什么会有卷积神经网络
CNN卷积神经网络是什么
卷积神经网络CNN(Convolutional Neural Networks)一般是指带有卷积层的一类型神经网络
CNN是一种由MLP神经网络发展而来的神经网络模型,常应用于图像识别、图像分割等图像相关问题
比较经典的CNN模型有:LeNet,AlexNet,VGG,GoogLeNet/InceptionNet,ResNet,Unet等
如果只提到CNN模型时,则往往是指由上述经典CNN模型中提炼出来的一个通用的基础CNN模型
一个基础CNN卷积神经网络的结构如下:
如图所示,一个基础CNN往往有五大层,或者更多层其中前三层是由卷积层、池化层组成,
它的思想是,先用卷积层、池化层来对输入图像提取特征,然后再使用全连接层来拟合目标输出
CNN卷积神经网络的思想
CNN是由多层感知机MLP神经网络发展而来的一种专用于处理图像的神经网络模型
理解了MLP是如何发展出CNN的,就自然很容易理解CNN的思想与意义了
MLP神经网络为何处理不了图像
在CNN之前,比较火的是MLP,因为MLP只要隐神经元足够多,就能拟合任意曲线
但是,MLP在应用于图象识别时,却难以进行,效果也并不太好
因为图象识别的输入比较多,就导致隐神经元也必须足够多,这样模型的参数比较多
如图所示,224*224的图像,就有5W个输入,如果用1W个隐节点,也有5亿多个权重
参数过多,训练就非常困难,而且模型的拟合能力也过强,导致模型预测效果往往不佳
卷积神经网络的解决图像问题的思路
可以注意到,图像的每个像素并非是孤立的,它与周边的像素紧密相连
CNN卷积神经网络引入了卷积层与池化层,先对输入进行信息融合与降维
在使得输入个数压缩得更加少之后,再用传统的MLP全连接神经网络来拟合输出
例如,224*224的图像经过压缩后就成为了30*30,此时再使用全连接神经网络进行拟合时就不会有太大的压力
所以,CNN总的来说,就是用卷积层与池化层不断融合图像的信息,使得输入减少,然后再用MLP来拟合输出
本节介绍CNN的卷积运算与池化运算,两者都是CNN的核心内容
CNN的卷积运算
CNN引入的卷积运算就是使用一个卷积核窗口(卷积核就是一个矩阵)对输入进行逐步扫描,
并将窗口覆盖的元素与卷积核进行加权求和作为输出,具体如下:
经过卷积核的运算后,输入的每个像素都与周围的像素加权为一个新像素,
卷积就相当于把输入的信息进行加权融合后作为输出(一般把输出称为FeatureMap)
卷积层的意义如下:
卷积层的生物意义:接收野
从生物角度来看,卷积层就是模仿人的眼睛看东西,一块一块的看
卷积层的数学意义:合并周围元素的信息,构建像素间的关联性
从数学角度上来看,卷积层的作用是合并周围元素的信息,使得图像每个元素与周围元素紧密相关
CNN的池化运算
池化层将输入的每一小块的信息压缩为一个信息,常用的池化层有均值池化与最大值池化
池化层主要用于减小FeatureMap的大小,经过池化之后,传给下一层的输入将减少
以一个2x2,步幅也为2的均值池化为例,它的运算例子如下图所示:
可以看到,2x2,步幅也为2的均值池化就相当于将图像按2x2的窗口大小进行均值压缩,
2x2,步幅也为2的池化窗口是最常用的,此时输出的高和宽都将是输入的一半
本节展示一个CNN卷积神经网络的代码实现
卷积神经网络-代码实现
手写数字数据集MNIST包含了10个手写数字(0-9)的7W个样本,每个样本是28*28的单通道灰度图片
MNIST样本示例如下:
下面我们构建一个CNN模型来实现对手写数字样本的预测
整体设计思路如下:
如图所示,先用两个带池化的卷积层先将FeatureMap压缩到7×7,此时FeatureMap相对较小,
再用一个卷积将FeatureMap一次性压缩为1×1即可,同时,在整个过程中,将通道逐步提升为80,
最后通过两个全连接层来拟合输出,最终输出时经过softmax,就可以得到各个类别的概率
具体代码实现如下:
import torch
from torch import nn
from torch.utils.data import DataLoader
import torchvision
import numpy as np
#--------------------模型结构--------------------------------------------
# 卷积神经网络的结构
class ConvNet(nn.Module):
def __init__(self,in_channel,num_classes):
super(ConvNet, self).__init__()
self.nn_stack=nn.Sequential(
#--------------C1层-------------------
nn.Conv2d(in_channel,6, kernel_size=5,stride=1,padding=2),
nn.ReLU(inplace=True),
nn.AvgPool2d(kernel_size=2,stride=2),
# 输出14*14
#--------------C2层-------------------
nn.Conv2d(6,16, kernel_size=5,stride=1,padding=2),
nn.ReLU(inplace=True),
nn.AvgPool2d(kernel_size=2,stride=2),
# 输出7*7
#--------------C3层-------------------
nn.Conv2d(16,80,kernel_size=7,stride=1,padding=0),
# 输出1*1*80
#--------------全连接层F4----------
nn.Flatten(), # 对C3的结果进行展平
nn.Linear(80, 120),
nn.ReLU(inplace=True),
#--------------全连接层F5----------
nn.Linear(120, num_classes)
)
def forward(self, x):
p = self.nn_stack(x)
return p
#-----------------------模型训练---------------------------------------
# 参数初始化函数
def init_param(model):
# 初始化权重阈值
param_list = list(model.named_parameters()) # 将模型的参数提取为列表
for i in range(len(param_list)): # 逐个初始化权重、阈值
is_weight = i%2==0 # 如果i是偶数,就是权重参数,i是奇数就是阈值参数
if is_weight:
torch.nn.init.normal_(param_list[i][1],mean=0,std=0.01) # 对于权重,以N(0,0.01)进行随机初始化
else:
torch.nn.init.constant_(param_list[i][1],val=0) # 阈值初始化为0
# 训练函数
def train(dataloader,valLoader,model,epochs,goal,device):
for epoch in range(epochs):
err_num = 0 # 本次epoch评估错误的样本
eval_num = 0 # 本次epoch已评估的样本
print('-----------当前epoch:',str(epoch),'----------------')
for batch, (imgs, labels) in enumerate(dataloader):
# -----训练模型-----
x, y = imgs.to(device), labels.to(device) # 将数据发送到设备
optimizer.zero_grad() # 将优化器里的参数梯度清空
py = model(x) # 计算模型的预测值
loss = lossFun(py, y) # 计算损失函数值
loss.backward() # 更新参数的梯度
optimizer.step() # 更新参数
# ----计算错误率----
idx = torch.argmax(py,axis=1) # 模型的预测类别
eval_num = eval_num + len(idx) # 更新本次epoch已评估的样本
err_num = err_num +sum(y != idx) # 更新本次epoch评估错误的样本
if(batch%10==0): # 每10批打印一次结果
print('err_rate:',err_num/eval_num) # 打印错误率
# -----------验证数据误差---------------------------
model.eval() # 将模型调整为评估状态
val_acc_rate = calAcc(model,valLoader,device) # 计算验证数据集的准确率
model.train() # 将模型调整回训练状态
print("验证数据的准确率:",val_acc_rate) # 打印准确率
if((err_num/eval_num)<=goal): # 检查退出条件
break
print('训练步数',str(epoch),',最终训练误差',str(err_num/eval_num))
# 计算数据集的准确率
def calAcc(model,dataLoader,device):
py = np.empty(0) # 初始化预测结果
y = np.empty(0) # 初始化真实结果
for batch, (imgs, labels) in enumerate(dataLoader): # 逐批预测
cur_py = model(imgs.to(device)) # 计算网络的输出
cur_py = torch.argmax(cur_py,axis=1) # 将最大者作为预测结果
py = np.hstack((py,cur_py.detach().cpu().numpy())) # 记录本批预测的y
y = np.hstack((y,labels)) # 记录本批真实的y
acc_rate = sum(y==py)/len(y) # 计算测试样本的准确率
return acc_rate
#--------------------------主流程脚本----------------------------------------------
#-------------------加载数据--------------------------------
train_data = torchvision.datasets.MNIST(
root = 'D:\pytorch\data' # 路径,如果路径有,就直接从路径中加载,如果没有,就联网获取
,train = True # 获取训练数据
,transform = torchvision.transforms.ToTensor() # 转换为tensor数据
,download = True # 是否下载,选为True,就下载到root下面
,target_transform= None)
val_data = torchvision.datasets.MNIST(
root = 'D:\pytorch\data' # 路径,如果路径有,就直接从路径中加载,如果没有,就联网获取
,train = False # 获取测试数据
,transform = torchvision.transforms.ToTensor() # 转换为tensor数据
,download = True # 是否下载,选为True,就下载到root下面
,target_transform= None)
#-------------------模型训练--------------------------------
trainLoader = DataLoader(train_data, batch_size=1000, shuffle=True) # 将数据装载到DataLoader
valLoader = DataLoader(val_data , batch_size=100) # 将验证数据装载到DataLoader
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 设置训练设备
model = ConvNet(in_channel =1,num_classes=10).to(device) # 初始化模型,并发送到设备
lossFun = torch.nn.CrossEntropyLoss() # 定义损失函数为交叉熵损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.01,momentum =0.9,dampening=0.0005) # 初始化优化器
train(trainLoader,valLoader,model,1000,0.01,device) # 训练模型,训练100步,错误低于1%时停止训练
# -----------模型效果评估---------------------------
model.eval() # 将模型切换到评估状态(屏蔽Dropout)
train_acc_rate = calAcc(model,trainLoader,device) # 计算训练数据集的准确率
print("训练数据的准确率:",train_acc_rate) # 打印准确率
val_acc_rate = calAcc(model,valLoader,device) # 计算验证数据集的准确率
print("验证数据的准确率:",val_acc_rate) # 打印准确率
运行结果如下:
-----------当前epoch: 0 ----------------
err_rate: tensor(0.7000)
验证数据的准确率: 0.3350877192982456
-----------当前epoch: 1 ----------------
err_rate: tensor(0.6400)
验证数据的准确率: 0.3350877192982456
-----------当前epoch: 2 ----------------
.......
.......
-----------当前epoch: 80 ----------------
err_rate: tensor(0.0100)
验证数据的准确率: 0.9982456140350877
-----------------------------------------
训练步数 80 ,最终训练误差 tensor(0.0088)
训练数据的准确率: 0.9982456140350877
验证数据的准确率: 0.9982456140350877
可以看到,模型的训练准确率与验证数据的准确率都达到了极高的水平,说明模型是有效的
好了,对于卷积神经网络CNN的讲解就到这里了~
End