问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

CNN卷积神经网络的原理与实践

创作时间:
作者:
@小白创作中心

CNN卷积神经网络的原理与实践

引用
CSDN
1.
https://blog.csdn.net/qq_61600833/article/details/142444432

卷积神经网络(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神经网络对手写数字模型进行改进,可以将正确率又提高一个层次!

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号