卷积神经网络CNN

目录

一、图像基础知识

图像基本概念

图像的加载

二、CNN概述

CNN概述

三、卷积层

卷积计算

Padding

Stride

多通道卷积计算

PyTorch卷积层API

四、池化层

池化层计算

Stride

Padding

多通道池化层计算

[PyTorch 池化 API](#PyTorch 池化 API)

五、图像分类案例

[CIFAR10 数据集](#CIFAR10 数据集)

搭建图像分类网络

编写训练函数

编写预测函数

总体代码


一、图像基础知识

图像基本概念

图像是由像素点组成的,每个像素点的取值范围为: [0, 255] 。像素值越接近于0,颜色越暗,接近于黑色;像素值越接近于255,颜色越亮,接近于白色。

在深度学习中,我们使用的图像大多是彩色图,彩色图由RGB3个通道组成,如下图所示

图像的加载

使用 matplotlib 库来实际理解下上面讲解的图像知识。

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
# 像素值的理解
def test01():
 # 全0数组是黑色的图像
 img = np.zeros([200, 200, 3])
 # 展示图像
 plt.imshow(img)
 plt.show()
 # 全255数组是白色的图像
 img = np.full([200, 200, 3], 255)
 # 展示图像
 plt.imshow(img)
 plt.show()

test01()
python 复制代码
# 图像的加载
def test02():
 # 读取图像
 img = plt.imread("data/yy.jpg")
 # 图像形状 高,宽,通道
 print("图像的形状(H, W, C):\n", img.shape)
 # 展示图像
 plt.imshow(img)
 plt.axis("off")
 plt.show()
test02()

二、CNN概述

CNN概述

卷积神经网络(Convolutional Neural Network)是含有卷积层的神经网络. 卷积层的作用就是用来自动学习、提取图像的特征。
CNN网络主要由三部分构成:卷积层、池化层和全连接层构成:

  1. 卷积层负责提取图像中的局部特征;
  2. 池化层用来大幅降低参数量级(降维);
  3. 全连接层用来输出想要的结果。

三、卷积层

卷积计算

  1. input 表示输入的图像
  2. filter 表示卷积核, 也叫做卷积核(滤波矩阵)
  3. input 经过 filter 得到输出为最右侧的图像,该图叫做特征图

卷积运算本质上就是在卷积核和输入数据的局部区域间做点积。

最终的特征图结果为 :

Padding

通过上面的卷积计算过程,最终的特征图比原始图像小很多,如果想要保持经过卷积后的图像大小不变, 可以在原图周围添加 padding 来实现.

Stride

按照步长为 1 来移动卷积核,计算特征图如下所示:

如果把 Stride 增大为 2 ,也是可以提取特征图的,如下图所示:

多通道卷积计算

实际中的图像都是多个通道组成的,我们怎么计算卷积呢?

如下图所示:

当使用多个卷积核时 , 应该怎么进行特征提取呢 ?


特征图大小
输出特征图的大小与以下参数息息相关 :

  1. size: 卷积核/过滤器大小,一般会选择为奇数,比如有 1*1 、3*3、5*5
  2. Padding: 零填充的方式
  3. Stride: 步长

那计算方法如下图所示 :

  1. 输入图像大小: W x W
  2. 卷积核大小: F x F
  3. Stride: S
  4. Padding: P
  5. 输出图像大小: N x N

特征图大小计算方法

以下图为例 :

  1. 图像大小: 5 x 5
  2. 卷积核大小: 3 x 3
  3. Stride: 1
  4. Padding: 1
  5. (5 - 3 + 2) / 1 + 1 = 5, 即得到的特征图大小为: 5 x 5

PyTorch卷积层API

conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
"""
参数说明:
in_channels: 输入通道数,
out_channels: 输出通道,也可以理解为卷积核 kernel 的数量
kernel_size :卷积核的高和宽设置,一般为 3,5,7...
stride :卷积核移动的步长
padding :在四周加入 padding 的数量,默认补 0
"""

python 复制代码
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
def test():
    # 读取图像, 形状: (640, 640, 3)
    img = plt.imread('data/yy.jpg')
    plt.imshow(img)
    plt.axis('off')
    plt.show()
    # 构建卷积层
    # out_channels表示卷积核个数
    # 修改out_channels,stride,padding观察特征图的变化情况
    conv = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, stride=2, padding=0)
    # 输入形状: (BatchSize, Channel, Height, Width)
    # mg形状: torch.Size([3, 640, 640])
    img = torch.tensor(img).permute(2, 0, 1)
    # img 形状: torch.Size([1, 3, 640, 640])
    img = img.unsqueeze(0)
    # 将图像送入卷积层中
    feature_map_img = conv(img.to(torch.float32))
    # 打印特征图的形状
    print(feature_map_img.shape)

if __name__ == '__main__':
    test()

四、池化层

池化层计算

池化层 (Pooling) 降低维度 , 缩减模型大小,提高计算速度。

Stride

Padding

多通道池化层计算

PyTorch池化API

最大池化

nn.MaxPool2d(kernel_size=2, stride=2, padding=1)

平均池化

nn.AvgPool2d(kernel_size=2, stride=1, padding=0)

python 复制代码
import torch
import torch.nn as nn
"""
1. 单通道池化
"""
def test01():
    # 定义输入输数据 【1,3,3 】
    inputs = torch.tensor([[[0, 1, 2], [3, 4, 5], [6, 7, 8]]]).float()
    # 修改stride,padding观察效果
    # 1. 最大池化
    polling = nn.MaxPool2d(kernel_size=2, stride=1, padding=0)
    output = polling(inputs)
    print("最大池化:\n", output)
    # 2. 平均池化
    polling = nn.AvgPool2d(kernel_size=2, stride=1, padding=0)
    output = polling(inputs)
    print("平均池化:\n", output)

"""
2. 多通道池化
"""
def test02():
    # 定义输入输数据 【3,3,3 】
    inputs = torch.tensor([[[0, 1, 2], [3, 4, 5], [6, 7, 8]],
    [[10, 20, 30], [40, 50, 60], [70, 80, 90]],
    [[11, 22, 33], [44, 55, 66], [77, 88, 99]]]).float()
    # 最大池化
    polling = nn.MaxPool2d(kernel_size=2, stride=1, padding=0)
    output = polling(inputs)
    print("多通道池化:\n", output)
if __name__ == '__main__':
    test01()
    test02()

输出结果

最大池化:

tensor([[[4., 5.],

7., 8.\]\]\]) 平均池化: tensor(\[\[\[2., 3.\], \[5., 6.\]\]\]) 多通道池化: tensor(\[\[\[ 4., 5.\], \[ 7., 8.\]\], \[\[50., 60.\], \[80., 90.\]\], \[\[55., 66.\], \[88., 99.\]\]\])

五、图像分类案例

首先我们导入一下工具包 :

python 复制代码
import torch
import torch.nn as nn
from torchvision.datasets import CIFAR10
from torchvision.transforms import ToTensor
from torchvision.transforms import Compose
import torch.optim as optim
from torch.utils.data import DataLoader
import time
import matplotlib.pyplot as plt
from torchsummary import summary
BATCH_SIZE = 8

CIFAR10****数据集

CIFAR-10数据集 5 万张训练图像、 1 万张测试图像、 10 个类别、每个类别有 6k 个图像,图像大小 32 × 32 × 3 。下图列举了10 个类,每一类随机展示了 10 张图片:

PyTorch 中的 torchvision.datasets 计算机视觉模块封装了 CIFAR10 数据集 , 使用方法如下 :

python 复制代码
# 1. 数据集基本信息
def create_dataset():
    # 加载数据集:训练集数据和测试数据
    train = CIFAR10(root='data', train=True, transform=Compose([ToTensor()]), download=True)
    valid = CIFAR10(root='data', train=False, transform=Compose([ToTensor()]), download=True)
    # 返回数据集结果
    return train, valid
if __name__ == '__main__':
    # 数据集加载
    train_dataset, valid_dataset = create_dataset()
    # 数据集类别
    print("数据集类别:", train_dataset.class_to_idx)
    # 数据集中的图像数据
    print("训练集数据集:", train_dataset.data.shape)
    print("测试集数据集:", valid_dataset.data.shape)
    # 图像展示
    plt.figure(figsize=(2, 2))
    plt.imshow(train_dataset.data[1])
    plt.title(train_dataset.targets[1])
    plt.show()

搭建图像分类网络

我们要搭建的网络结构如下 :

我们要搭建的网络结构如下 :

  1. 输入形状: 32x32
  2. 第一个卷积层输入 3 个 Channel, 输出 6 个 Channel, Kernel Size 为: 3x3
  3. 第一个池化层输入 30x30, 输出 15x15, Kernel Size 为: 2x2, Stride 为: 2
  4. 第二个卷积层输入 6 个 Channel, 输出 16 个 Channel, Kernel Size 为 3x3
  5. 第二个池化层输入 13x13, 输出 6x6, Kernel Size 为: 2x2, Stride 为: 2
  6. 第一个全连接层输入 576 维, 输出 120 维
  7. 第二个全连接层输入 120 维, 输出 84 维
  8. 最后的输出层输入 84 维, 输出 10 维

我们在每个卷积计算之后应用 relu 激活函数来给网络增加非线性因素。
构建网络代码实现如下:

python 复制代码
# 2.模型构建
class ImageClassification(nn.Module):
    # 定义网络结构
    def __init__(self):
        super(ImageClassification, self).__init__()
        # 定义网络层:卷积层+池化层
        self.conv1 = nn.Conv2d(3, 6, stride=1, kernel_size=3)
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(6, 16, stride=1, kernel_size=3)
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        # 全连接层
        self.linear1 = nn.Linear(576, 120)
        self.linear2 = nn.Linear(120, 84)
        self.out = nn.Linear(84, 10)

    # 定义前向传播
    def forward(self, x):
        # 卷积+relu+池化
        x = torch.relu(self.conv1(x))
        x = self.pool1(x)
        # 卷积+relu+池化
        x = torch.relu(self.conv2(x))
        x = self.pool2(x)
        # 将特征图做成以为向量的形式:相当于特征向量
        x = x.reshape(x.size(0), -1)
        # 全连接层
        x = torch.relu(self.linear1(x))
        x = torch.relu(self.linear2(x))
        # 返回输出结果
        return self.out(x)

模型结构为:

python 复制代码
if __name__ == '__main__':
    # 模型实例化
    model = ImageClassification()
    summary(model,input_size=(3,32,32),batch_size=1)

编写训练函数

在训练时,使用多分类交叉熵损失函数, Adam 优化器 . 具体实现代码如下 :

python 复制代码
# 3.训练模型
def train(model,train_dataset):
     criterion = nn.CrossEntropyLoss() # 构建损失函数
     optimizer = optim.Adam(model.parameters(), lr=1e-3) # 构建优化方法
     epoch = 100 # 训练轮数
     for epoch_idx in range(epoch):
         # 构建数据加载器
         dataloader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
         sam_num = 0 # 样本数量
         total_loss = 0.0 # 损失总和
         start = time.time() # 开始时间
            # 遍历数据进行网络训练
         for x, y in dataloader:
             output = model(x)
             loss = criterion(output, y) # 计算损失
             optimizer.zero_grad() # 梯度清零
             loss.backward() # 反向传播
             optimizer.step() # 参数更新
             total_loss += loss.item() # 统计损失和
             sam_num += 1
         print('epoch:%2s loss:%.5f time:%.2fs' %(epoch_idx + 1,total_loss / sam_num,time.time() - start))
     # 模型保存
     torch.save(model.state_dict(), 'data/image_classification.pth')

调用训练方法进行模型训练如下 :

python 复制代码
if __name__ == '__main__':
     # 数据集加载
     train_dataset, valid_dataset = create_dataset()
     # 模型实例化
     model = ImageClassification()
     # 模型训练
     train(model,train_dataset)

编写预测函数

加载训练好的模型,对测试集中的 1 万条样本进行预测,查看模型在测试集上的准确率 .

python 复制代码
def test(valid_dataset):
 # 构建数据加载器
 dataloader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=True)
 # 加载模型并加载训练好的权重
 model = ImageClassification()
 model.load_state_dict(torch.load('data/image_classification.pth'))
 model.eval()
 # 计算精度
 total_correct = 0
 total_samples = 0
 # 遍历每个batch的数据,获取预测结果,计算精度
 for x, y in dataloader:
    output = model(x)
    total_correct += (torch.argmax(output, dim=-1) == y).sum()
    total_samples += len(y)
 # 打印精度
 print('Acc: %.2f' % (total_correct / total_samples))

总体代码

python 复制代码
import torch
import torch.nn as nn
from torchvision.datasets import CIFAR10
from torchvision.transforms import ToTensor
from torchvision.transforms import Compose
import torch.optim as optim
from torch.utils.data import DataLoader
import time
import matplotlib.pyplot as plt
from torchsummary import summary
BATCH_SIZE = 8

# 1. 数据集基本信息
def create_dataset():
    # 加载数据集:训练集数据和测试数据
    train = CIFAR10(root='data', train=True, transform=Compose([ToTensor()]), download=True)
    valid = CIFAR10(root='data', train=False, transform=Compose([ToTensor()]), download=True)
    # 返回数据集结果
    return train, valid
# if __name__ == '__main__':
#     # 数据集加载
#     train_dataset, valid_dataset = create_dataset()
#     # 数据集类别
#     print("数据集类别:", train_dataset.class_to_idx)
#     # 数据集中的图像数据
#     print("训练集数据集:", train_dataset.data.shape)
#     print("测试集数据集:", valid_dataset.data.shape)
#     # 图像展示
#     plt.figure(figsize=(2, 2))
#     plt.imshow(train_dataset.data[1])
#     plt.title(train_dataset.targets[1])
#     plt.show()

# 2.模型构建
class ImageClassification(nn.Module):
    # 定义网络结构
    def __init__(self):
        super(ImageClassification, self).__init__()
        # 定义网络层:卷积层+池化层
        self.conv1 = nn.Conv2d(3, 6, stride=1, kernel_size=3)
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(6, 16, stride=1, kernel_size=3)
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        # 全连接层
        self.linear1 = nn.Linear(576, 120)
        self.linear2 = nn.Linear(120, 84)
        self.out = nn.Linear(84, 10)

    # 定义前向传播
    def forward(self, x):
        # 卷积+relu+池化
        x = torch.relu(self.conv1(x))
        x = self.pool1(x)
        # 卷积+relu+池化
        x = torch.relu(self.conv2(x))
        x = self.pool2(x)
        # 将特征图做成以为向量的形式:相当于特征向量
        x = x.reshape(x.size(0), -1)
        # 全连接层
        x = torch.relu(self.linear1(x))
        x = torch.relu(self.linear2(x))
        # 返回输出结果
        return self.out(x)



# 3.训练模型
def train(model,train_dataset):
     criterion = nn.CrossEntropyLoss() # 构建损失函数
     optimizer = optim.Adam(model.parameters(), lr=1e-3) # 构建优化方法
     epoch = 100 # 训练轮数
     for epoch_idx in range(epoch):
         # 构建数据加载器
         dataloader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
         sam_num = 0 # 样本数量
         total_loss = 0.0 # 损失总和
         start = time.time() # 开始时间
            # 遍历数据进行网络训练
         for x, y in dataloader:
             output = model(x)
             loss = criterion(output, y) # 计算损失
             optimizer.zero_grad() # 梯度清零
             loss.backward() # 反向传播
             optimizer.step() # 参数更新
             total_loss += loss.item() # 统计损失和
             sam_num += 1
         print('epoch:%2s loss:%.5f time:%.2fs' %(epoch_idx + 1,total_loss / sam_num,time.time() - start))
     # 模型保存
     torch.save(model.state_dict(), 'data/image_classification.pth')



# 4.预测模型
def test(valid_dataset):
     # 构建数据加载器
     dataloader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=True)
     # 加载模型并加载训练好的权重
     model = ImageClassification()
     model.load_state_dict(torch.load('data/image_classification.pth'))
     model.eval()
     # 计算精度
     total_correct = 0
     total_samples = 0
     # 遍历每个batch的数据,获取预测结果,计算精度
     for x, y in dataloader:
        output = model(x)
        total_correct += (torch.argmax(output, dim=-1) == y).sum()
        total_samples += len(y)
     # 打印精度
     print('Acc: %.2f' % (total_correct / total_samples))

if __name__ == '__main__':
    #1.数据集加载
    train_dataset, valid_dataset = create_dataset()
    #2.模型实例化
    model = ImageClassification()
    #3.模型训练
    train(model, train_dataset)
    #4.预测模型
    test(valid_dataset)
相关推荐
工藤学编程29 分钟前
零基础学AI大模型之LangChain智能体执行引擎AgentExecutor
人工智能·langchain
图生生33 分钟前
基于AI的商品场景图批量生成方案,助力电商大促效率翻倍
人工智能·ai
说私域34 分钟前
短视频私域流量池的变现路径创新:基于AI智能名片链动2+1模式S2B2C商城小程序的实践研究
大数据·人工智能·小程序
yugi98783837 分钟前
用于图像分类的EMAP:概念、实现与工具支持
人工智能·计算机视觉·分类
aigcapi41 分钟前
AI搜索排名提升:GEO优化如何成为企业增长新引擎
人工智能
彼岸花开了吗1 小时前
构建AI智能体:八十、SVD知识整理与降维:从数据混沌到语义秩序的智能转换
人工智能·python·llm
MM_MS1 小时前
Halcon图像锐化和图像增强、窗口的相关算子
大数据·图像处理·人工智能·opencv·算法·计算机视觉·视觉检测
韩师傅1 小时前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端
AI大佬的小弟1 小时前
【小白第一课】大模型基础知识(1)---大模型到底是啥?
人工智能·自然语言处理·开源·大模型基础·大模型分类·什么是大模型·国内外主流大模型
lambo mercy1 小时前
无监督学习
人工智能·深度学习