CNN卷积神经网络的原理与实践
CNN卷积神经网络的原理与实践
卷积神经网络(CNN)是深度学习领域中的一种重要网络结构,广泛应用于图像识别、目标检测和自然语言处理等领域。本文将详细介绍CNN的基本原理、关键组成部分,并通过一个简单的实例来展示如何构建一个卷积神经网络。
一、CNN基础原理
1.1 卷积操作
卷积是CNN的核心操作,它通过在输入图像上滑动一个小的窗口(卷积核),对窗口内的像素进行加权求和的操作,从而提取图像中的局部特征。
定义卷积层:
nn.Conv2d(
in_channels=1, # 图像通道个数,1表示灰度图
out_channels=16, # 要得到几个特征图,卷积核的个数
kernel_size=5, # 卷积核的大小,5*5
stride=1, # 步长
padding=2, # 边缘填充
)
卷积核大小、步长、边缘填充定义为5、1、2,会使得特征图与卷积操作之前的图像像素大小相同,方便计算。
卷积层的计算方法:
1.2 激活函数
激活函数用于引入非线性特性,在定义完卷积层后要在后续定义激活函数,使得神经网络可以学习更加复杂的特征。常用的激活函数有ReLU、Sigmoid和Tanh等。
1.3 池化操作
池化层的作用:
一种降采样,减小数据的空间大小,因此参数的数量和计算量也会下降,这在一定程度上也控制了过拟合。
常见的池化层:
最大池化、平均池化、全局平均池化、全局最大池化。
*平均池化(average pooling):计算图像区域的平均值作为该区域池化后的值。
*最大池化(max pooling):选图像区域的最大值作为该区域池化后的值。是最为常见的。 通常来说,CNN的卷积层之间都会周期性地插入池化层。
池化层操作方法:
与卷积层类似,池化层运算符由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动,为固定形状窗口(有时称为 池化窗口)遍历的每个位置计算一个输出。 然而,不同于卷积层中的输入与卷积核之间的互相关计算,池化层不包含参数。
1.4 全连接层
当抓取到足以用来识别图片的特征后,接下来的就是如何进行分类。 全连接层(也叫前馈层)就可以用来将最后的输出映射到线性可分的空间。 通常卷积网络的最后会将末端得到的长方体平摊(flatten)成一个长长的向量,并送入全连接层配合输出层进行分类。此过程也就相当于传统神经网络将数据传入模型。
二、构建一个简单的CNN模型
此次我们通过使用CNN网络对手写数字识别模型进行改进,传统的神经网络模型对手写数字的识别实例解析:Pytroch实战:构建手写数字识别模型_下载手写体数字数据集。 2.使用 pytorch 搭建模型。-CSDN博客
下面我们对模型进行改进
2.1 导入需要的库
import torch
from torch import nn
from torch.utils.data import DataLoader # torch中的数据管理工具
from torchvision import datasets # datasets中封装了许多与图像相关的模型以及数据集
from torchvision.transforms import ToTensor # 将其他数据类型转换为张量(tensor)
2.2 数据预处理
'''下载训练数据集(图片+标签)'''
training_data = datasets.MNIST( # MNIST数据库
root="data", # 将数据下载并存入data文件夹
train=True, # 提取训练集数据
download=True,
transform=ToTensor() # 接受PIL图片并返回转换后的版本,转换为张量类型的数据
)
test_data = datasets.MNIST(
root="data",
train=False,
download=True,
transform=ToTensor()
)
print(len(training_data))
# 64张图片为一个包
train_dataloader = DataLoader(training_data, batch_size=64) # 建议用2的指数当作一个包的数量
test_dataloader = DataLoader(test_data, batch_size=64)
for X, y in test_dataloader:
print(f'Shape of X [N,C,H,W]:{X.shape}')
print(f'Shape of y:{y.shape},{y.dtype}')
break
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(f"Using {device} device")
2.3 定义CNN神经网络
'''定义神经网络'''
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Sequential( #将多个层组合在一起
nn.Conv2d(
in_channels=1, # 图像通道个数,1表示灰度图
out_channels=16, # 要得到几个特征图,卷积核的个数
kernel_size=5, # 卷积核的大小,5*5
stride=1, # 步长
padding=2, # 边缘填充
),
nn.ReLU(), #定义激活函数
nn.MaxPool2d(kernel_size=2), #最大池化操作
)
self.conv2 = nn.Sequential( # 定义第二个卷积层
nn.Conv2d(16,32,5,1,2),
nn.ReLU(),
nn.Conv2d(32,32,5,1,2),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.conv3 = nn.Sequential( # 定义第三个卷积层
nn.Conv2d(32,64,5,1,2),
nn.ReLU(),
)
self.out = nn.Linear(64*7*7,10)
2.4定义全连接层
def forward(self,x):
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = x.view(x.size(0),-1)
output = self.out(x)
return output
2.5 训练集与测试集
model = CNN().to(device) #将模型传入GPU
print(model) #打印模型
# 训练集
def train(dataloader, model, loss_fn, optimizer):
model.train() # 开始训练,w可以改变,与测试中的model.eval()相对应
batch_size_num = 1 # batch为每个数据的编号
for X, y in dataloader: # 开始打包
X, y = X.to(device), y.to(device) # 将数据传入Gpu
pred = model.forward(X) # 前向传输,model的数据来自模型的out
loss = loss_fn(pred, y) # 通过交叉熵损失函数计算loss,pred为预测值,y为真实值
optimizer.zero_grad() # 优化,梯度值清零
loss.backward() # 反向传播计算每个参数的梯度值W
optimizer.step() # 根据梯度更新W
# 每轮的损失值可视化
loss_value = loss.item() # 从tensor数据中提取数据出来,转换成对应的整数或者浮点数
if batch_size_num % 200 == 0:
print(f"loss: {loss_value:>7f} [number:{batch_size_num}]")
batch_size_num += 1
# 测试集
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset) # 检测测试集有多少个数据
num_batches = len(dataloader) # 检测有多少个包,64个图为一个包
model.eval() # 测试,w不可更新
test_loss, correct = 0, 0 # 初始化损失值以及正确率
with torch.no_grad(): # 一个上下文管理器,关闭梯度计算
for X, y in dataloader: # 提取测试集的数据
X, y = X.to(device), y.to(device)
pred = model.forward(X) # 预测结果
test_loss += loss_fn(pred, y).item() # test_loss会自动累加每一批次的损失值
correct += (pred.argmax(1) == y).type(torch.float).sum().item() # pred.argmax(1)返回每一行中最大值对应的索引号
a = (pred.argmax(1) == y) # 比较预测值与正确标签是否相同,返回True或者False
b = (pred.argmax(1) == y).type(torch.float) # 将True-->1,False-->0,便于统计正确率
test_loss /= num_batches
correct /= size
print(f"Test result: \n Accuracy: {(100 * correct)}%,Avg loss:{test_loss}")
loss_fn = nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 优化器,SGD为随机梯度下降算法,lr为步长学习率
# optim中有很多优化算法
# train(train_dataloader,model,loss_fn,optimizer)
# test(test_dataloader,model, loss_fn)
epochs = 10
for t in range(epochs):
print(f"Epoch {t + 1}\n**********************************")
train(train_dataloader, model, loss_fn, optimizer)
print("Done!")
# 进行epochs轮train,一轮test
test(test_dataloader, model, loss_fn)
2.6 训练结果
可见,采用CNN神经网络对手写数字模型进行改进,可以将正确率又提高一个层次!