感谢阅读

  • CNN概述
  • 卷积层
  • 卷积计算
  • Padding
  • stride
  • PyTorch 卷积层 API
  • 池化层
  • 经典案列图像分类
  • CIFAR10 数据集
  • 模型构建
  • 模型训练
  • 预测


CNN概述

卷积神经网络(Convolutional Neural Network)是含有卷积层的神经网络. 卷积层的作用就是用来自动学习、提取图像的特征
CNN网络主要有三部分构成:卷积层、池化层和全连接层构成,其中卷积层负责提取图像中的局部特征;池化层用来大幅降低参数量级(降维);全连接层类似人工神经网络的部分,用来输出想要的结果。

卷积层

卷积计算

input 经过 filter 的得到输出为最右侧的图像,该图叫做特征图。卷积运算本质上就是在滤波器和输入数据的局部区域间做点积。

基本的卷积神经网络结构 卷积神经网络主要包括_cnn


比如上图OutPut的左上角=input左上角33矩阵与Filter相乘以后各元素之和,也就是11+10+11+00+11+10+01+00+11=4

Padding

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

stride

stride用于设置卷积移动的步幅,卷积特征图尺寸计算公式:

基本的卷积神经网络结构 卷积神经网络主要包括_cnn_02

PyTorch 卷积层 API

conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)

参数说明:
in_channels: 输入通道数,RGB图片一般是3
out_channels: 输出通道(与kernel的数量,但不是kernel的数量,不是kernel的数量,不是kernel的数量,重要的事情说三遍)
kernel_size:卷积核的高和宽设置
stride:卷积核移动的步伐
padding:在四周加入padding的数量,默认补0

池化层

池化层 (Pooling) 降低维度, 缩减模型大小,提高计算速度. 即: 主要对卷积层学习到的特征图进行下采样

经典案列图像分类

CIFAR10 数据集

CIFAR-10数据集5万张训练图像、1万张测试图像、10个类别、每个类别有6k个图像,图像大小32×32×3。正好可以用于我们对图像分类的案列。
我们用代码试试这个数据集

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

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()

运行结果:

基本的卷积神经网络结构 卷积神经网络主要包括_卷积_03

模型构建

写下个代码之前补充个东西,以便阅读:
CNN的卷积核通道数 = 卷积输入层的通道数
CNN的卷积输出层通道数(深度)= 卷积核的个数
在卷积层的计算中,假设输入是H x W x C, C是输入的深度(即通道数),那么卷积核(滤波器)的通道数需要和输入的通道数相同,所以也为C,假设卷积核的大小为K x K,一个卷积核就为K x K x C,计算时卷积核的对应通道应用于输入的对应通道,这样一个卷积核应用于输入就得到输出的一个通道。假设有P个K x K x C的卷积核,这样每个卷积核应用于输入都会得到一个通道,所以输出有P个通道。

# 模型构建
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)

模型训练

def train(model,train_dataset):
    # 构建损失函数
    criterion = nn.CrossEntropyLoss()
    # 构建优化方法
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    # 训练轮数
    epoch = 10
    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()
        correct = 0
        # 遍历数据进行网络训练
        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(), '../model/image_classification.pth')


# 数据集加载
train_dataset, valid_dataset = create_dataset()
# 模型实例化
model = ImageClassification()
# 模型训练
train(model,train_dataset)

预测

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))