Week2-P2: CIFAR10彩色图片识别

🍨 本文为[🔗365天深度学习训练营] 中的学习记录博客
🍖 原作者:[K同学啊 | 接辅导、项目定制]

我的环境:

语言环境:Anaconda3 (Python3)

编译器:jupyter notebook

深度学习环境:Pytorch

torch == 1.12.1+cu116

torchvision == 0.13.1+cu116

1. 准备

1.1 设置GPU

import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

device

输出

device(type='cuda')

1.2 导入数据

使用dataset下载CIFAR10数据集,并划分好训练集与测试集

train_ds = torchvision.datasets.CIFAR10('data', 
                                      train=True, 
                                      transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensor
                                      download=True)

test_ds  = torchvision.datasets.CIFAR10('data', 
                                      train=False, 
                                      transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensor
                                      download=True)

输出

Files already downloaded and verified
Files already downloaded and verified

使用dataloader加载数据,并设置好基本的batch_size

batch_size = 10 # 根据电脑内存调整大小

train_dl = torch.utils.data.DataLoader(train_ds, 
                                       batch_size=batch_size, 
                                       shuffle=True)

test_dl  = torch.utils.data.DataLoader(test_ds, 
                                       batch_size=batch_size)
# 取一个批次查看数据格式
# 数据的shape为:[batch_size, channel, height, weight]
# 其中batch_size为自己设定,channel,height和weight分别是图片的通道数,高度和宽度。
imgs, labels = next(iter(train_dl))
imgs.shape

输出

torch.Size([10, 3, 32, 32])

1.3 数据可视化

squeeze()函数的功能是从矩阵shape中,去掉维度为1的。例如一个矩阵是的shape是(5, 1),使用过这个函数后,结果为(5, )。

import numpy as np

 # 指定图片大小,图像大小为20宽、5高的绘图(单位为英寸inch)
plt.figure(figsize=(20, 5)) 
for i, imgs in enumerate(imgs[:20]):
    # 维度缩减
    npimg = imgs.numpy().transpose((1, 2, 0))
    # 将整个figure分成2行10列,绘制第i+1个子图。
    plt.subplot(2, 10, i+1)
    plt.imshow(npimg, cmap=plt.cm.binary)
    plt.axis('off')
    
#plt.show()  如果你使用的是Pycharm编译器,请加上这行代码

 输出

 2. 构建简单的CNN网络

这段代码定义了一个简单的卷积神经网络(Convolutional Neural Network, CNN)模型,用于图像分类任务。下面是对代码的详细解释:

import torch.nn.functional as F: 导入PyTorch中的神经网络函数模块,并将其命名为F,以便后续使用。

num_classes = 10: 设置图像的类别数,这是一个分类问题,共有10个类别。

class Model(nn.Module)::定义了一个名为Model的类,该类继承自nn.Module,是PyTorch中神经网络模块的基类。

def __init__(self):Model类的构造函数,初始化神经网络的各个层。特征提取网络:self.pool3 = nn.MaxPool2d(kernel_size=2): 第三层池化层,使用最大池化,池化核大小为2*2。

self.pool2 = nn.MaxPool2d(kernel_size=2): 第二层池化层,使用最大池化,池化核大小为2*2。

self.conv2 = nn.Conv2d(64, 64, kernel_size=3): 第二层卷积层,输入通道数为64,输出通道数为64,卷积核大小为3*3。

self.pool1 = nn.MaxPool2d(kernel_size=2): 第一层池化层,使用最大池化,池化核大小为2*2。

self.conv1 = nn.Conv2d(3, 64, kernel_size=3): 第一层卷积层,输入通道数为3(RGB图像),输出通道数为64,卷积核大小为3*3。

分类网络:

self.fc2 = nn.Linear(256, num_classes): 全连接层2,输入特征数为256,输出特征数为类别数num_classes

self.fc1 = nn.Linear(512, 256): 全连接层1,输入特征数为512,输出特征数为256。

self.conv3 = nn.Conv2d(64, 128, kernel_size=3): 第三层卷积层,输入通道数为64,输出通道数为128,卷积核大小为3*3。

def forward(self, x)::定义了模型的前向传播过程。

通过卷积层、激活函数ReLU和池化层进行特征提取。

使用torch.flatten将特征张量展平,以便输入全连接层。

通过全连接层和ReLU进行分类。

返回模型的输出。

总体而言,这个模型包含了三个卷积层用于特征提取,紧接着是两个全连接层用于分类。前向传播通过这些层以生成最终的输出,适用于输入图像的分类任务。

import torch.nn.functional as F

num_classes = 10  # 图片的类别数

class Model(nn.Module):
     def __init__(self):
        super().__init__()
         # 特征提取网络
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3)   # 第一层卷积,卷积核大小为3*3
        self.pool1 = nn.MaxPool2d(kernel_size=2)       # 设置池化层,池化核大小为2*2
        self.conv2 = nn.Conv2d(64, 64, kernel_size=3)  # 第二层卷积,卷积核大小为3*3   
        self.pool2 = nn.MaxPool2d(kernel_size=2) 
        self.conv3 = nn.Conv2d(64, 128, kernel_size=3) # 第二层卷积,卷积核大小为3*3   
        self.pool3 = nn.MaxPool2d(kernel_size=2) 
                                      
        # 分类网络
        self.fc1 = nn.Linear(512, 256)          
        self.fc2 = nn.Linear(256, num_classes)
     # 前向传播
     def forward(self, x):
        x = self.pool1(F.relu(self.conv1(x)))     
        x = self.pool2(F.relu(self.conv2(x)))
        x = self.pool3(F.relu(self.conv3(x)))
        
        x = torch.flatten(x, start_dim=1)

        x = F.relu(self.fc1(x))
        x = self.fc2(x)
       
        return x

加载并打印模型

from torchinfo import summary
# 将模型转移到GPU中(我们模型运行均在GPU中进行)
model = Model().to(device)

summary(model)

输出

=================================================================
Layer (type:depth-idx)                   Param #
=================================================================
Model                                    --
├─Conv2d: 1-1                            1,792
├─MaxPool2d: 1-2                         --
├─Conv2d: 1-3                            36,928
├─MaxPool2d: 1-4                         --
├─Conv2d: 1-5                            73,856
├─MaxPool2d: 1-6                         --
├─Linear: 1-7                            131,328
├─Linear: 1-8                            2,570
=================================================================
Total params: 246,474
Trainable params: 246,474
Non-trainable params: 0
=================================================================

Layer (type:depth-idx): 显示每一层的类型和深度索引。例如,Conv2d: 1-1 表示深度为1的第一个卷积层。

Param #: 显示每一层的参数数量。

Model: --: 表示整个模型的起始。

├─Conv2d: 1-1 1,792: 表示第一个卷积层,具有1,792个参数。

├─MaxPool2d: 1-2 --: 表示第一个池化层,没有可训练参数。

├─Conv2d: 1-3 36,928: 表示第二个卷积层,具有36,928个参数。

├─MaxPool2d: 1-4 --: 表示第二个池化层,没有可训练参数。

├─Conv2d: 1-5 73,856: 表示第三个卷积层,具有73,856个参数。

├─MaxPool2d: 1-6 --: 表示第三个池化层,没有可训练参数。

├─Linear: 1-7 131,328: 表示第一个全连接层,具有131,328个参数。

├─Linear: 1-8 2,570: 表示第二个全连接层,具有2,570个参数。

Total params: 246,474: 显示总共的参数数量。

Trainable params: 246,474: 显示可训练的参数数量。

Non-trainable params: 0: 显示不可训练的参数数量,即没有需要更新的参数

3. 训练模型

3.1 设置超参数

loss_fn    = nn.CrossEntropyLoss() # 创建损失函数
learn_rate = 1e-2 # 学习率
opt        = torch.optim.SGD(model.parameters(),lr=learn_rate)

3.2 编写训练函数

# 训练循环
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  # 训练集的大小,一共60000张图片
    num_batches = len(dataloader)   # 批次数目,1875(60000/32)

    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率
    
    for X, y in dataloader:  # 获取图片及其标签
        X, y = X.to(device), y.to(device)
        
        # 计算预测误差
        pred = model(X)          # 网络输出
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
        
        # 反向传播
        optimizer.zero_grad()  # grad属性归零
        loss.backward()        # 反向传播
        optimizer.step()       # 每一步自动更新
        
        # 记录acc与loss
        train_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()
        train_loss += loss.item()
            
    train_acc  /= size
    train_loss /= num_batches

    return train_acc, train_loss

3.3 编写测试函数

def test (dataloader, model, loss_fn):
    size        = len(dataloader.dataset)  # 测试集的大小,一共10000张图片
    num_batches = len(dataloader)          # 批次数目,313(10000/32=312.5,向上取整)
    test_loss, test_acc = 0, 0
    
    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad():
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            
            # 计算loss
            target_pred = model(imgs)
            loss        = loss_fn(target_pred, target)
            
            test_loss += loss.item()
            test_acc  += (target_pred.argmax(1) == target).type(torch.float).sum().item()

    test_acc  /= size
    test_loss /= num_batches

    return test_acc, test_loss

3.4 正式训练

这段代码是一个简单的训练循环,用于训练和评估神经网络模型。

epochs = 5: 定义了训练循环的轮数,即要遍历整个训练数据集的次数。注意:这里原文是10。

train_loss = [], train_acc = [], test_loss = [], test_acc = []: 初始化四个列表,分别用于存储每个训练周期(epoch)的训练损失、训练准确度、测试损失和测试准确度。

for epoch in range(epochs):: 开始循环训练,遍历指定轮数的训练周期。

model.train(): 将模型设置为训练模式,这是因为在训练循环中,需要启用训练模式以便进行梯度更新。

epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt): 调用自定义的train函数,该函数执行一个训练周期,返回该周期的训练准确度和训练损失。

model.eval(): 将模型设置为评估模式,这是因为在测试阶段,不需要进行梯度更新。

epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn): 调用自定义的test函数,该函数执行一个测试周期,返回该周期的测试准确度和测试损失。

train_acc.append(epoch_train_acc), train_loss.append(epoch_train_loss), test_acc.append(epoch_test_acc), test_loss.append(epoch_test_loss): 将训练和测试阶段的准确度和损失记录到相应的列表中。

template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}'): 定义一个字符串模板,用于格式化输出训练和测试结果。

print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss)): 打印每个训练周期的训练和测试结果,包括训练准确度、训练损失、测试准确度和测试损失。

print('Done'): 在所有训练周期完成后,打印 'Done' 表示训练循环结束。

整个代码段描述了一个简单的神经网络模型的训练和测试过程,记录并输出每个训练周期的性能指标。这样的训练循环是深度学习中常见的一种模式。

epochs     = 5
train_loss = []
train_acc  = []
test_loss  = []
test_acc   = []

for epoch in range(epochs):
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
    
    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')
    print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
print('Done')

输出

Epoch: 1, Train_acc:22.5%, Train_loss:2.072, Test_acc:36.0%,Test_loss:1.741
Epoch: 2, Train_acc:41.5%, Train_loss:1.600, Test_acc:46.0%,Test_loss:1.494
Epoch: 3, Train_acc:50.8%, Train_loss:1.360, Test_acc:54.6%,Test_loss:1.275
Epoch: 4, Train_acc:57.5%, Train_loss:1.196, Test_acc:56.9%,Test_loss:1.223
Epoch: 5, Train_acc:62.2%, Train_loss:1.070, Test_acc:61.4%,Test_loss:1.104
Done

4. 结果可视化

import matplotlib.pyplot as plt: 导入Matplotlib库并将其命名为plt,用于绘制图表。

import warnings: 导入Python的warnings模块,用于处理警告信息。

warnings.filterwarnings("ignore"): 忽略警告信息,防止警告信息在输出中显示。

plt.rcParams['font.sans-serif'] = ['SimHei']: 设置Matplotlib绘图时使用的字体为中文宋体(SimHei)。

plt.rcParams['axes.unicode_minus'] = False: 防止在绘图中出现负号显示问题。

plt.rcParams['figure.dpi'] = 100: 设置绘图的分辨率。

epochs_range = range(epochs): 创建一个包含训练周期数量的范围对象,用于横坐标。

plt.figure(figsize=(12, 3)): 创建一个图形对象,设置图形的大小为宽12单位、高3单位。

plt.subplot(1, 2, 1): 在图形中创建一个子图,1行2列的图形中的第1个子图。

plt.plot(epochs_range, train_acc, label='Training Accuracy'): 绘制训练准确度曲线,横坐标为训练周期范围,纵坐标为训练准确度,并添加标签。

plt.plot(epochs_range, test_acc, label='Test Accuracy'): 绘制测试准确度曲线,横坐标为训练周期范围,纵坐标为测试准确度,并添加标签。

plt.legend(loc='lower right'): 在图中添加图例,位置在右下角。

plt.title('Training and Validation Accuracy'): 添加图的标题。

plt.subplot(1, 2, 2): 在图形中创建第2个子图。

plt.plot(epochs_range, train_loss, label='Training Loss'): 绘制训练损失曲线,横坐标为训练周期范围,纵坐标为训练损失,并添加标签。

plt.plot(epochs_range, test_loss, label='Test Loss'): 绘制测试损失曲线,横坐标为训练周期范围,纵坐标为测试损失,并添加标签。

plt.legend(loc='upper right'): 在图中添加图例,位置在右上角。

plt.title('Training and Validation Loss'): 添加图的标题。

plt.show(): 显示绘制的图形。

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率

epochs_range = range(epochs)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

输出

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值