计算机系统应用教程网站

网站首页 > 技术文章 正文

基于pytorch实现卷积神经网络(CNN)进行物体识别项目实战

btikc 2024-12-05 09:49:24 技术文章 41 ℃ 0 评论

预设条件

  1. 中级IDE使用经验(Jetbrain Pycharm优先)
  2. 了解Python运行环境及其命令使用
  3. 对Python编程语言有很好的理解
  4. 基本了解Pandas(处理数据框架)、Numpy、Scikit Learn和Matplot库
  5. 一些统计学知识,对分析数据是有帮助的

运行环境

以下运行软件的版本号是经过验证的可成功运行的版本号,以供参考。

编号

软件名称

版本号

1

Python

3.9.19

2

conda

22.9.0

3

pip

23.3.1

4

numpy

1.26.4

5

pandas

2.2.1

6

matplotlib

3.8.4

7

scikit-learn

1.4.2

8

scipy

1.13.0

9

seaborn

0.13.2

10

statsmodels

0.14.2

11

torch

2.4.1

12

torchaudio

2.4.1

13

torchvision

0.19.1

项目背景

上一篇文章详细介绍了全连接神经网络模型的基本概念、原理定义、案例分析等内容,但全连接神经网络由于网络连接过多,计算量较大,而且不能很好地解决图像不变性问题,所以适用范围并不大,主要作为一些大型模型的部分层次进行使用,本章在全连接模型的基础上介绍一个应用范围更加广泛的神经网络模型——卷积神经网络模型。

卷积神经网络模型是深度神经网络架构的一种网络类型,通用被用于计算机视觉领域。计算机视觉是人工智能试图理解和解释图像或视觉数据的领域。

在机器学习领域,人工神经网络的表现非常出色。神经网络被广泛应用于图像、音频和文本等各种数据集。不同的神经网络被用于不同的用途。比如预测文本序列的使用递归神经网络,图像分类的使用卷积神经网络,本篇文章主要介绍卷积神经网络的基本概念和原理,并构建卷积神经网络模型的方法和步骤。

概念原理

神经网络:层和功能

一个典型的神经网络中具有三个层:

  1. 输入层(Input Layer):输入层是向模型中输入数据的层,这一层的神经元的数量等于输入数据的特征的数量,比如图像数据就是像素的数量。
  2. 隐藏层(Hidden Layer):隐藏层接收输入层传输过来的数据,隐藏层的数量由模型大小和数据大小来决定有多少层。每个隐藏层的神经元数量可以不同,也可以大于特征的数量,
  3. 输出层(Output Layer):输出层接收从隐藏层传输过来的数据,然后再将数据传输到逻辑函数中,比如sigmoid或者softmax,这些逻辑函数可以将分类转换成概率数值,概率值最大的分类就是模型的预测。

数据向模型中传递以及每一层的输出数据来源于上一步的操作,然后使用损失函数(error function)计算损失(这些通用的损失函数是交叉熵(cross-entropy)、方差(square loss error)等),这种模型称为前馈网络模型(feedforward)。损失函数意味着模型执行效率的好坏。然后,我们通过计算导数反向传播到模型中,这一步被称为反向传播(Backpropagation),基本上是用来减少损失。

卷积神经网络

卷积神经网络(Convolutional Neural Network, CNN)是人工神经网络的扩展版本,主要用于从网格矩阵数据集中提取特征。例如,像图像或视频这样的可视化数据集,其中数据模式发挥着广泛的作用。

网络架构

卷积神经网络包含多个层,包括输入层、卷积层、池化层和全连接层。

卷积层使用卷积核提取特征,池化层使用下采样对图像消减像素来减少计算量,全连接层进行最终的预测。网络通过反向传播和梯度下降来学习如何优化卷积核。

卷积层如何工作?

卷积神经网络是一种可以共享参数的神经网络。想象你有一个图像,这个图像的参数可以用一个立方体来表达,如下图所示。

图像有长、宽(图像的维度)和高(表示图像的通道,一般表示RGB通道)。现在想象一下,选取图像的一小部分,然后在上面运行一个称为“滤波器”或“核”的小型神经网络,该网络有K个输出,并以垂直方式表示它们。现在将神经网络滑动整个图像,结果我们会得到另一张具有不同宽度、高度和深度的图像。现在不再只有R、G和B通道,而是有更多的通道,但宽度和高度较小。这个操作称为卷积。如果选取图像的一小部分的大小与原图像大小相同,那就是常规神经网络。相比较全连接层,卷积层由于存在这个小部分,会造成在计算过程中,卷积层有更少的权重。

卷积的数学概述

前面介绍了卷积的概念,现在描述一下卷积所涉及到数学。

  1. 卷积层包含过滤器或核的集合,这些过滤器具有较小的长度和宽度,其中深度跟输入层的图像输入的通道数相同。
  2. 如果我们在图像维度为34x34x3的尺度上运行一个卷积层,那么过滤器的尺寸为AxAx3,这里的A可以是3,5,7或者比图像维度更小的值。
  3. 在前向传播中,我们一步一步地滑动过滤器经过整个图像区域,这里的每一步叫作步幅,每一步的计算是对卷积核的权重和移动的区域进行点积计算。
  4. 当我们滑动过滤器时,每个过滤器将产生一个2维的输出,然后将它们堆叠在一起,最终得到一个深度等于过滤器数量的输出矩阵。

数据获取

数据集简介

通常,当你需要处理图像、文本、音频或视频时,你需要使用标准python包将数据转换成numpy数组,然后再将这类数组转成torch的张量。

  • 对图像数据,使用Pillow, OpenCV进行操作
  • 对音频,使用scipy and librosa进行操作
  • 对于文本,使用原生Python或Cython或者NLTK和SpaCy进行操作

使用torchvision包可以载入数据集,比如ImageNet、CIFAR10、MNIST等,使用torchvision.datasets和torch.utils.data.DataLoader进行图像变换。

本文使用的CIFAR10数据集是一种常用的图像分类数据集,包含10个类别的60000张彩色图像,包含的类别分别是飞机、汽车、鸟、猫、鹿、青蛙、马、轮船、卡车等。CIFAR10中的图像尺寸是 3x32x32,说明这是一个32x32像素大小的3通道彩色图像。

模型开发

构建模型按照以下步骤:

  1. 载入和归一化CIFAR10训练和测试集
  2. 定义卷积神经网络
  3. 定义损失函数
  4. 使用训练数据训练模型
  5. 使用测试数据测试模型

载入和归一化数据

调用torchavision包可以非常方便地载入数据。

核心代码:

现在对载入的训练集数据随机提取几个看看数据内容,因为这些数据都是图像,也可以通过可视化代码显示出图像。

核心代码:

首次运行会提示下载数据:

非首次运行的执行结果:

可视化显示效果:

从执行结果中可以看出,随机挑选的四个图像,分别是truck ship cat dog,分属不同的四个类别。

定义模型

按照上一章描述的模型网络层次,用torch.nn包的Conv2D来定义卷积层。

方法定义:

CLASS torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1,

padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros',

device=None, dtype=None)

函数作用:定义一个卷积核,方便后面进行二维卷积操作。

我们会发现一个现象:pytorch的conv2d没有要求输入卷积核的信息,设置卷积核的权重可以在后面进行。

参数说明:

参数

数据类型

描述

in_channels

int

输入图像通道数

out_channels

int

卷积产生的通道数,即过滤器(filter)或卷积核(kernel)的个数,每个卷积核会产生一个输出通道;

kernel_size

(int or tuple)

卷积核尺寸,可以设为1个int型数或者一个(int, int)型的元组。例如输入5表示高5宽5,表示5*5卷积核,(2,3)是高2宽3卷积核

stride

(int or tuple, optional)

卷积步长,默认为1。可以设为1个int型数或者一个(int, int)型的元组

padding

(int or tuple, optional)

填充操作,控制padding_mode的数目。简言之,就是决定图像边沿填充的方式

dilation

(int or tuple, optional)

扩张操作:控制卷积核点的间距,默认值:1。

groups

(int, optional)

控制分组卷积,默认不分组,为1组。输入图像通道数

bias

(bool, optional)

为真,则在输出中添加一个可学习的偏差。默认:True。

padding_mode

(string, optional)

padding模式,默认为Zero填充。

池化层用torch.nn.MaxPool2d()来定义。

定义方法:

CLASS torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

函数作用:对邻域内特征点取最大值,减小卷积层参数误差造成估计均值的偏移的误差,更多的保留纹理信息。

池化与卷积的共同点:池化操作也是原图像矩阵(或特征图矩阵)与一个固定形状的窗口(核、或者叫算子)进行计算,并输出特征图的一种计算方式;

池化与卷积的不同点:卷积操作的卷积核是有数据(权重)的,而池化直接计算池化窗口内的原始数据,这个计算过程可以是选择最大值、选择最小值或计算平均值,分别对应:最大池化、最小池化和平均池化。由于在实际使用中最大池化是应用最广泛的池化方法,以下讲述均针对于最大池化进行说明,平均池化和最小池化也是同样的作用原理。

本文同样采用的是最大池化。

参数说明:

参数

数据类型

描述

kernel_size

(int or tuple)

最大池化的窗口大小,可以是单个值,也可以是tuple元组

stride

(int or tuple)

步长,可以是单个值,也可以是tuple元组

padding

(int or tuple)

填充,可以是单个值,也可以是tuple元组

dilation

int

控制窗口中元素步幅

return_indices

bool

布尔类型,返回最大值位置索引

ceil_mode

Bool

布尔类型,为True,用向上取整的方法,计算输出形状;默认是向下取整。

全连接层用torch.nn.linear来定义。

定义方法:

CLASS torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

函数作用:提供一个线性变换模块,通常用于神经网络中的全连接层。在一个全连接层中,输入向量通过权重矩阵和偏置项进行线性变换,从而得到输出向量。

参数说明:

参数

数据类型

描述

in_features

int

输入的二维张量的大小,也即输入的神经元个数

out_features

Int

输出的二维张量的大小,也即输出神经元个数

bias

bool

是否包含偏置

下面我们来看创建模型的代码:

定义的模型中,首先定义一个卷积层,其中输入的数据是彩色图像,包含RGB三个通道,所以输入通道in_channels是3,输出通道是卷积核的个数,这里采用输出通道out_channels=6,另外卷积核尺寸采用5*5维度,因为是正方形,所以可以简写为0,即kernel_size=5。

接着定义一个最大池化层,其中最大池化的窗口大小是2*2,即kernel_size=2,步长stride设置为2。

再定义一个卷积层,其中in_channels=6,因为从上一层传递过来的通道数就是6,所以这里也要设置为6,out_channels=16表示卷积核的个数有所增加,kernel_size=5延续不变。

然后接着定义三个全连接层。先来看第一个全连接层,因为从上一层传递过来的输出是16通道(也可以理解成卷积核数目),且每个卷积核的维度是5*5所以,全连接层的输入神经元的个数是16*5*5,即in_features=16*5*5,定义输出的神经元的个数是120,即out_features=120。接着第二个全连接层的in_features是上一层的out_features,所以第二个全连接层in_features=120,定义输出神经元个数out_features=84。同理第三个全连接层的in_features=84,它的输出神经元个数需要根据输出数据的类别进行设置,因为这个模型的输出数据一共有10个类别,所以第三个全连接层的out_features=10。

向前传播方法forward定义了数据的流向,我们可以看到输入数据首先进入第一个卷积层,出来的数据再进入Relu激活函数,然后流入池化层,这完成了第一层的操作。接着数据进入第二个卷积层,然后进入Relu激活函数,最后流入池化层,这是第二层。然后对数据进行展平操作,由二维数据变成一维数据。随后数据通过全连接层和Relu激活函数后,从第三个全连接层输出结果。

损失函数和优化器

损失函数采用torch的交叉熵损失torch.nn.CrossEntropyLoss(),优化器采用torch的optim.SGD()。

核心代码:

交叉熵CrossEntropyLoss刻画的是两个概率分布的距离,也就是说交叉熵值越小(相对熵的值越小),两个概率分布越接近。

SGD 是随机梯度下降(Stochastic Gradient Descent,SGD)优化算法。

定义方法:

CLASS torch.optim.SGD(params, lr=0.001, momentum=0, dampening=0, weight_decay=0, nesterov=False, *, maximize=False, foreach=None, differentiable=False, fused=None)

函数作用:实现随机梯度下降算法。

参数说明:

参数

数据类型

描述

params


参数(张量)的迭代器,例如模型的参数 model.parameters()

lr

float

学习率(learning rate)。它是一个正数,控制每次参数更新的步长。较小的学习率会导致收敛较慢,较大的学习率可能导致震荡或无法收敛

momentum

int

动量(momentum)是一个用于加速 SGD 收敛的参数。它引入了上一步梯度的指数加权平均。通常设置在 0 到 1 之间。当 momentum 大于 0 时,算法在更新时会考虑之前的梯度,有助于加速收敛。默认值为 0

dampening

int

阻尼项,用于减缓动量的速度。在某些情况下,为了防止动量项引起的震荡,可以设置一个小的 dampening 值。默认值为 0

weight_decay

int

权重衰减,也称为 L2 正则化项。它用于控制参数的幅度,以防止过拟合。通常设置为一个小的正数。默认值为 0

nesterov

bool

Nesterov 动量。当设置为 True 时,采用 Nesterov 动量更新规则。Nesterov 动量在梯度更新之前先进行一次预测,然后在计算梯度更新时使用这个预测。

一般为了防止过拟合,给学习率lr设置一个较小的正数。

训练网络

设置上相关方法后,就可以开始训练网络模型了。训练网络需要实现数据迭代器的循环重复以及反向传播优化。

核心代码:

模型训练完成后,应当将模型保存到文件中,以利于后续使用。

核心代码:

测试模型

我们已经对网络进行了两次数据的训练,现在需要对模型是否学到了东西进行检验。我们可以通过预测神经网络输出的类标签,并与真实标签进行比较来验证这一点。如果预测正确,我们将该样本添加到正确预测的列表中。

测试第一步:让我们从测试集中展示一张图片,以便熟悉一下。

第二步,加载已经保存的模型。

第三步,执行预测,看看神经网络认为这些图像是什么。

按照以上步骤的核心代码如下:

执行结果如下:

从预结果来看,预测的效果还是可以的,4个结果中正确了3个,有1个预测不对。

预测的原理是通过获取预测分别的概率值来判断,如果一个类别的概率越高,模型就越认为图像是属于这个特定的类别。

下面我们来看下对整个测试数据集进行预测的效果。

核心代码:

执行结果:

Accuracy of the network on the 10000 test images: 61 %

目前来看对于整个测试集的预测正确率超过了60%。

如果将整个测试集的每个类别进行预测,那么正确率是多少呢?我们来看看结果。

核心代码:

执行结果:

从结果中可以看到,ship这一类的预测最高,达到74%,cat类最低,只有36%,因此可以结论出ship类整体清晰度最高、轮廓感更强,但cat类清晰度较模糊,而且轮廓感不高,是造成这种预测结果的原因。

结论

本文主要介绍了通过使用卷积神经网络模型进行建模和预测的实战项目案例,希望能够帮助你学习和理解卷积神经网络的核心内容。如果你还想学习更多人工智能/机器学习/深度学习相关的知识,请继续阅读我编写的其他内容的文章。

本文涉及到的机器学习实战项目文件(附带数据+代码+文档)名称如下:

cnn_train.py —— 训练模型代码

cnn_check.py —— 测试模型代码

机器学习项目实战11-基于pytorch实现卷积神经网络(CNN)进行物体识别项目实战.pdf

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表