CIFAR-10数据集分类实验报告

本篇文章只是一篇CIFAR-10数据集分类简介,是我的一门课程的大作业,从BP网络进行分类到CNN进行分类。
程序贴在最后!!!

1 开发环境

目前主流的神经网络框架有Tensorflow,Pytorch,MXNET,Keras等。本次实验使用Pytroch深度学习框架。PyTorch看作加入了GPU支持的numpy,并且它是一个拥有自动求导功能的强大的深度神经网络。

2 BP神经网络相关知识

2.1 激活函数

  • 激活函数的作用是加入非线性因素,从而提高神经网络对模型的表达能力,解决线性模型所不能解决的问题。
    在这里插入图片描述
    在这里插入图片描述

  • 上图是sigmoid激活函数。

  • 计算量大,反向传播求误差梯度时,求导涉及除法。

  • 反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。
    在这里插入图片描述
    在这里插入图片描述

  • 上图是ReLU激活函数。

  • ReLU 的收敛速度会比 sigmoid 快很多。

2.2 损失函数

在这里插入图片描述

  • MSE损失更适合用于回归问题,比如房价预测,对于分类问题,MSE Loss不能很好的逼近目标。
    在这里插入图片描述
  • 交叉熵损失,以二分类为例。该损失更适合用于分类问题。

3 CIFAR-10数据集

在这里插入图片描述

  • 该数据集分为训练集和测试集,一共有10种动物。其中,训练集有50000张图片,测试集有10000张图片,训练集和测试集不重叠,图片大小为32 * 32 * 3。

4 BP网络实验

  • SGD(随机梯度下降)优化策略。SGD就是每一次随机抽取一个mini-batch,迭代计算mini-batch的梯度,然后对参数进行更新。weight decay(权重衰减):设置为5e-4,防止过拟合,是放在正则项(regularization)前面的一个系数,正则项一般指示模型的复杂度。momentum(动量):设置为0.9,提高收敛速度。学习率设置为0.01,ReLU实验的学习率为0.005。
  • 引入Batchsize,设置为128,通过并行化提高内存的利用率。就是尽量让你的GPU满载运行,提高训练速度;适当Batch Size使得梯度下降方向更加准确。
  • 输入是将32 * 32 * 3的图像展平为3072维的图像特征,输出是10维。
  • 总共学习100个epochs。
  • 从隐藏层神经元个数、激活函数和隐藏层层数三个角度实验,证明对BP网络的影响。
  • 实验并没有对学习率有一个很好的调整,只是大范围的确定了学习率,所以训练的精度不代表模型最好精度。

4.1 三层BP网络隐藏层神经元个数

在这里插入图片描述

  • 如图,是三层BP网络,使用sigmoid激活函数,将损失函数改为交叉熵损失,而不是使用均方差MSE损失。
    在这里插入图片描述

  • 上图是确定三层BP网络确定隐藏层神经元个数的经验。
    在这里插入图片描述
    在这里插入图片描述

  • 如图,对三层BP网络的隐藏层分别设置为8,2048和10240个神经元。

  • 对于隐藏层设置为8个神经元,神经元个数太少会导致网络表达能力太弱,无法对特征进行正确的表达。

  • 对于隐藏层设置为10240个神经元,神经元个数太多不一定就能够对特征有正确的表达,而且神经元个数过大容易导致模型泛化能力较差。

  • 由上述经验确定的隐藏层设置为2048个神经元,效果最好。
    在这里插入图片描述
    在这里插入图片描述

  • 分别选择512,2048,4096个神经元,可以看到512个神经元相对于2048和4096个神经元稍微差一点,而2048和4096个神经元效果相似。

4.2 激活函数Sigmoid和ReLU

在这里插入图片描述
在这里插入图片描述

  • 使用三层BP网络实验,隐藏层神经元个数为2048,该实验只改变了两个模型的激活函数。
  • 可以看到ReLU激活函数明显优于Sigmoid激活函数。
  • ReLU收敛速度更快,且能使损失收敛到一个更小的值。
    在这里插入图片描述
    在这里插入图片描述
  • 注意这里有一个尖峰,损失突然增大,我个人的理解是我只固定了一个学习率,当损失很小的适合应该使用小学习率。

4.3 隐藏层层数

在这里插入图片描述

  • 四层BP网络,隐藏层增加一层。隐藏层的神经元个数为2048。
    在这里插入图片描述
    在这里插入图片描述

  • 如图所示,粉色和紫色分别表示4层BP网络和3层BP网络,他们使用Sigmoid激活函数。

  • 层数的增加反而训练收敛速度变慢,看测试准确率几乎相似,,说明层数增加并没有对特征有个更好的非线性表示,这有可能是我参数没调好,也有可能是Sigmoid激活函数两端梯度为0,梯度不均匀,训练不稳定。
    在这里插入图片描述
    在这里插入图片描述

  • 在上述实验基础上,将Sigmoid函数改为ReLU函数,可以看到,层数增加,模型的收敛速度更快,精度更高,能更好的表示非线性。

5 卷积神经网络

  • BP网络实验我将图片的像素展平为32 * 32 * 3的数组,将该数组作为这张图片的特征。实际上可以将分类问题实际上是特征提取之后加全连接层进行分类,这里提取特征就可以使用效率更高的CNN卷积神经网络。
    在这里插入图片描述

在这里插入图片描述

5.1 LeNet-5

在这里插入图片描述

  • 首先进行卷积操作,提取图片特征后,将特征展平用全连接层进行分类。
    在这里插入图片描述
  • 网络结构,假设原来图像大小32 * 32 * 3,conv1将其变为16 * 16 * 6,conv2经过第一个Conv卷积核变为12 * 12 * 16,然后经过MaxPool变为6 * 6 * 16,然后输入到全连接层。

5.2 自制CNN

    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 8, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(8, 16, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(16, 32, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.conv4 = nn.Sequential(
            nn.Conv2d(32, 64, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.fc1 = nn.Sequential(
            nn.Linear(64 * 2 * 2, 120),
            nn.ReLU()
        )
        self.fc2 = nn.Sequential(
            nn.Linear(120, 84),
            nn.ReLU()
        )
        self.fc3 = nn.Linear(84, 10)
  • 使用大小为3的卷积核

5.3 实验

在这里插入图片描述
在这里插入图片描述

  • 如图,使用卷积神经网络效果要远远好于BP网络。

6 代码

import torch
from torch.utils import data
import torch.nn as nn
from tqdm import tqdm
import wandb
import torchvision.transforms as transforms
import torchvision

class BP_model(nn.Module):
    def __init__(self):
        super(BP_model, self).__init__()
        self.fc1 = nn.Linear(32 * 32 * 3, 2048) # 线性层
        self.fc2 = nn.Linear(2048, 2048)
        self.fc3 = nn.Linear(2048, 10)

    def forward(self, x):
        out = x.view(-1, 32 * 32 * 3) # 展平处理
        out = nn.ReLU()(self.fc1(out))
        out = nn.ReLU()(self.fc2(out))
        out = self.fc3(out)
        return out

class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 6, 5, 1, 2), # 卷积核 原来通道数 转换后通道数 卷积核大小 步长 填充
            nn.ReLU(), # 激活函数
            nn.MaxPool2d(2) # 最大池化层
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(6, 16, 5, 1, 0),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.fc1 = nn.Sequential(
            nn.Linear(16 * 6 * 6, 120),
            nn.ReLU()
        )
        self.fc2 = nn.Sequential(
            nn.Linear(120, 84),
            nn.ReLU()
        )
        self.fc3 = nn.Linear(84, 10)
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(-1, 16 * 6 * 6)
        x = self.fc1(x)
        x = self.fc2(x)
        x = self.fc3(x)
        return x

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 8, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(8, 16, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(16, 32, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.conv4 = nn.Sequential(
            nn.Conv2d(32, 64, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.fc1 = nn.Sequential(
            nn.Linear(64 * 2 * 2, 120),
            nn.ReLU()
        )
        self.fc2 = nn.Sequential(
            nn.Linear(120, 84),
            nn.ReLU()
        )
        self.fc3 = nn.Linear(84, 10)
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = x.view(-1, 64 * 2 * 2)
        x = self.fc1(x)
        x = self.fc2(x)
        x = self.fc3(x)
        return x

if __name__ == '__main__':

    wandb.init(project="BP")
    wandb.config.dropout = 0.2
    wandb.config.hidden_layer_size = 128
    # 读取数据,数据预处理
    transform = transforms.Compose([
                transforms.ToTensor(),
                transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]) # totensor是将0-255转到0-1,normalize是将0-1转换到-1-1
    trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                            download=False, transform=transform) # 读取数据集,数据预处理
    trainloader = torch.utils.data.DataLoader(trainset, batch_size=128,
                                              shuffle=True, num_workers=1, pin_memory=True) # 打乱,包装成batchsize
    testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                           download=False, transform=transform)
    testloader = torch.utils.data.DataLoader(testset, batch_size=128,
                                             shuffle=False, num_workers=1, pin_memory=True)

    device = torch.device("cuda:0") # 选择cpu或者GPU
    # model = BP_model()
    # model = LeNet5() # 用哪个模型,打开哪个,屏蔽另外两个
    model = CNN()
    model.to(device) # 选择cpu或者gpu
    criterion = nn.CrossEntropyLoss() # 交叉熵损失
    optimizer = torch.optim.SGD([{'params': model.parameters()}],
                                lr=0.005, weight_decay=5e-4, momentum=0.9) # 随机梯度优化策略
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=200) # CNN训练使用该学习率优化策略,bp网络也可以使用试试

    for epoch in range(80):
        # 训练
        model.train()
        for i, (img, label) in tqdm(enumerate(trainloader)):
            img, label = img.to(device, non_blocking=True), label.to(device, non_blocking=True).long() # 加载数据
            output = model(img) # 计算结果
            loss = criterion(output, label) # 计算损失
            optimizer.zero_grad()
            loss.backward() # 反向传播
            optimizer.step() # 优化器更新

            iters = epoch * len(trainloader) + i
            if iters % 10 == 0:
                wandb.log({'loss': loss}) # 可视化
        # 测试
        accuracy = 0
        model.eval()
        for i, (img, label) in enumerate(testloader):
            img, label = img.to(device), label.to(device).long()
            output = model(img)
            output = output.max(dim=1)[1] # 预测是哪一类
            accuracy += (output==label).sum().item() # 准确率计算
        accuracy = accuracy / 10000 # 准确率计算
        print(accuracy)
        wandb.log({'accuracy': accuracy}) # 可视化

        scheduler.step() # 学习率更新






  • 8
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CIFAR-10数据集是一个常用的用于图像分类数据集,包含10个类别(飞机、汽车、鸟类、猫、鹿、狗、青蛙、马、船和卡车),每个类别有6000张32x32的彩色图像。在这里,我将简要介绍一下CIFAR-10数据集分类代码的分析。 该代码通常由以下几个部分组成: 1. 数据预处理 在导入CIFAR-10数据集之前,需要对数据进行预处理。这包括归一化、数据增强等操作。对于归一化,一般将每个像素的值除以255,将像素值范围缩放到0-1之间。对于数据增强,一般采用随机裁剪、随机翻转等方式增加数据集的多样性。 2. 模型定义 在该代码中,一般采用卷积神经网络(CNN)作为模型。CNN是一种常用的用于图像分类神经网络,其主要结构包括卷积层、池化层、全连接层等。在CNN中,卷积层可以提取图像的特征,池化层可以对特征图进行降维,全连接层可以对特征进行分类。 3. 模型训练 在模型训练阶段,通常采用交叉熵损失函数作为损失函数,采用随机梯度下降(SGD)或Adam等优化器进行优化。同时,还需要设置一些超参数,如学习率、批大小、训练轮数等。 4. 模型评估 在模型训练完成后,需要对模型进行评估。常用的评估指标包括准确率、精确率、召回率、F1-score等。在该代码中,一般采用测试集对模型进行评估。 总之,CIFAR-10数据集分类代码的分析涉及到数据预处理、模型定义、模型训练和模型评估等方面。同时需要注意调整超参数,防止过拟合等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ma lidong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值