Pytorch | 从零构建AlexNet对CIFAR10进行分类

Pytorch | 从零构建AlexNet对CIFAR10进行分类

  • CIFAR10数据集
  • AlexNet
    • 网络结构
    • 技术创新点
    • 性能表现
    • 影响和意义
  • AlexNet结构代码详解
    • 结构代码
    • 代码详解
      • 特征提取层 self.features
      • 分类部分self.classifier
      • 前向传播forward
  • 训练过程和测试结果
  • 代码汇总
    • alexnet.py
    • train.py
    • test.py

CIFAR10数据集

CIFAR-10数据集是由加拿大高级研究所(CIFAR)收集整理的用于图像识别研究的常用数据集,基本信息如下:

  • 数据规模:该数据集包含60,000张彩色图像,分为10个不同的类别,每个类别有6,000张图像。通常将其中50,000张作为训练集,用于模型的训练;10,000张作为测试集,用于评估模型的性能。
  • 图像尺寸:所有图像的尺寸均为32×32像素,这相对较小的尺寸使得模型在处理该数据集时能够相对快速地进行训练和推理,但也增加了图像分类的难度。
  • 类别内容:涵盖了飞机(plane)、汽车(car)、鸟(bird)、猫(cat)、鹿(deer)、狗(dog)、青蛙(frog)、马(horse)、船(ship)、卡车(truck)这10个不同的类别,这些类别都是现实世界中常见的物体,具有一定的代表性。

下面是一些示例样本:
在这里插入图片描述

AlexNet

AlexNet是由Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton在2012年提出的一种深度卷积神经网络,在ImageNet图像识别挑战赛中取得了巨大成功,推动了深度学习在计算机视觉领域的快速发展。以下是对它的详细介绍:

网络结构

  • 卷积层:包含5个卷积层,这些卷积层通过不同的卷积核大小、步长和填充方式,逐步提取图像的特征。
  • 池化层:有3个最大池化层,用于减小特征图的尺寸,同时保留关键特征,减少计算量和过拟合风险。
  • 全连接层:包括3个全连接层,用于对提取的特征进行分类,最后一层输出分类结果。
    在这里插入图片描述
    上图为AlexNet原文中的网络结构(针对ImageNet,图片尺寸为224×224),本文是针对CIFAR10,其尺寸为32×32,因此结构不太相同,比如卷积核的大小,具体可以参考下面的代码。

技术创新点

  • ReLU激活函数:使用ReLU(Rectified Linear Unit)作为激活函数,解决了传统激活函数在深度网络中梯度消失的问题,加快了训练速度。
  • Dropout正则化:在全连接层中使用了Dropout技术,随机丢弃部分神经元,防止过拟合,提高模型的泛化能力。
  • 重叠池化:采用重叠池化(Overlapping Pooling),即池化窗口之间有重叠,有助于提取更多的特征信息,提升模型的性能。
  • 多GPU训练:首次利用多GPU进行并行训练,大大提高了训练速度,使得在大规模数据集上训练深度网络成为可能。

性能表现

  • 在ImageNet数据集上,AlexNet的top-5错误率大幅降低至15.3%,相比之前的方法有了显著提升,展示了其强大的图像识别能力。
  • 能够学习到丰富的图像特征,对不同类别的物体具有很好的区分能力,在实际应用中取得了很好的效果。

影响和意义

  • 推动深度学习发展:AlexNet的成功引起了学术界和工业界对深度学习的广泛关注,激发了更多研究人员对深度神经网络的研究兴趣,推动了深度学习技术的快速发展。
  • 开启卷积神经网络新时代:为后续的卷积神经网络研究提供了重要的参考和借鉴,许多新的网络结构和技术都是在AlexNet的基础上发展而来的。
  • 拓展应用领域:由于其在图像识别任务上的出色表现,AlexNet及其改进模型被广泛应用于计算机视觉的各个领域,如目标检测、图像分割、人脸识别等。

AlexNet结构代码详解

结构代码

import torch
import torch.nn as nnclass AlexNet(nn.Module):def __init__(self, num_classes):super(AlexNet, self).__init__()self.features = nn.Sequential(# input size: (B, 3, 32, 32)   (Batch_size, Channel, Height, Width)nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1), # (B, 64, 16, 16)nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2),    # (B, 64, 8, 8)nn.Conv2d(64, 192, kernel_size=3, padding=1),   # (B, 192, 8, 8)nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2),    # (B, 192, 4, 4)nn.Conv2d(192, 384, kernel_size=3, padding=1),  # (B, 384, 4, 4)nn.ReLU(inplace=True),nn.Conv2d(384, 256, kernel_size=3, padding=1),  # (B, 256, 4, 4)nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),  # (B, 256, 4, 4)nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2),    # (B, 256, 2, 2))self.classifier = nn.Sequential(nn.Dropout(),nn.Linear(256 * 2 * 2, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Linear(4096, num_classes))def forward(self, x):x = self.features(x)x = x.view(x.size(0), 256 * 2 *2)x = self.classifier(x)return x

代码详解

以下是对上述AlexNet代码的详细解释:

特征提取层 self.features

这部分构建了AlexNet的特征提取层,是一个由多个层组成的顺序结构(通过nn.Sequential来定义)。
- 第一个卷积层
nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1)表示输入图像的通道数为3(通常对应RGB图像的红、绿、蓝三个通道),输出的通道数为64(即卷积核的数量为64,意味着会生成64个不同的特征图),卷积核大小是3×3,步长为2(在空间维度上每次移动2个像素),填充为1(在图像边缘进行1个像素的填充,这样可以保证输入输出图像尺寸在卷积操作下能按预期变化),经过这个卷积层后,输入尺寸为(B, 3, 32, 32)的图像数据会变成(B, 64, 16, 16)
- 激活函数层
nn.ReLU(inplace=True)是使用修正线性单元(Rectified Linear Unit)作为激活函数,inplace=True表示直接在输入的张量上进行修改(节省内存空间),对经过卷积后的特征图进行非线性变换,增强网络的表达能力。
- 池化层
nn.MaxPool2d(kernel_size=2)是最大池化层,池化核大小为2×2,它会在每个2×2的窗口内选取最大值作为输出,起到下采样的作用,减少数据量同时保留重要特征,比如经过第一次池化后特征图尺寸从(B, 64, 16, 16)变为(B, 64, 8, 8)

后续依次重复卷积、激活、池化等操作,不断提取图像的特征,逐步降低特征图的尺寸同时增加特征图的深度(通道数),最终经过这一系列操作后得到尺寸为(B, 256, 2, 2)的特征图。

分类部分self.classifier

self.classifier = nn.Sequential(nn.Dropout(),nn.Linear(256 * 2 * 2, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Linear(4096, num_classes)
)

这部分构建了AlexNet的分类器,同样是顺序结构。
- Dropout层
nn.Dropout()是一种正则化技术,在训练过程中以一定概率(默认0.5)随机将神经元的输出设置为0,防止过拟合,提高模型的泛化能力。这里使用了两次Dropout,分别在不同的全连接层之前。
- 全连接层
第一个nn.Linear(256 * 2 * 2, 4096)表示将经过特征提取后展平的特征向量(尺寸为256 * 2 * 2,因为前面特征提取部分最后得到的特征图尺寸是(B, 256, 2, 2),展平后维度就是256 * 2 * 2)映射到一个4096维的向量空间,后面接着激活函数nn.ReLU(inplace=True)进行非线性变换。然后又是一个Dropout层和一个同样输出维度为4096的全连接层以及相应的激活函数,最后通过nn.Linear(4096, num_classes)将4096维的向量映射到指定的类别数(num_classes)维度,得到最终的分类预测结果。

前向传播forward

def forward(self, x):x = self.features(x)x = x.view(x.size(0), 256 * 2 *2)x = self.classifier(x)return x

forward方法定义了数据在网络中的前向传播过程。

  • 特征提取
    首先x = self.features(x),将输入数据x送入到之前定义的特征提取部分(features),按照特征提取层中定义的卷积、激活、池化等操作依次对输入数据进行处理,得到提取后的特征图。
  • 特征图展平
    x = x.view(x.size(0), 256 * 2 *2)这行代码将特征图进行展平操作,使其变成一个二维张量,其中第一维对应批次大小(x.size(0)表示批次中的样本数量),第二维就是展平后的特征向量长度(由前面特征提取最后得到的特征图尺寸计算得出),这样才能输入到后面的全连接层中进行分类处理。
  • 分类预测
    最后x = self.classifier(x)将展平后的特征向量送入分类器部分(classifier),经过全连接层、激活函数、Dropout等操作逐步得到最终的分类预测结果,然后通过return x返回这个预测结果。

训练过程和测试结果

训练过程损失函数变化曲线:
在这里插入图片描述
在这里插入图片描述
训练过程准确率变化曲线:
在这里插入图片描述
测试结果:
在这里插入图片描述

代码汇总

项目github地址
项目结构:

|--data
|--models|--__init__.py|--alexnet.py
|--results
|--weights
|--train.py
|--test.py

alexnet.py

import torch
import torch.nn as nnclass AlexNet(nn.Module):def __init__(self, num_classes):super(AlexNet, self).__init__()self.features = nn.Sequential(# input size: (B, 3, 32, 32)   (Batch_size, Channel, Height, Width)nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1), # (B, 64, 16, 16)nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2),    # (B, 64, 8, 8)nn.Conv2d(64, 192, kernel_size=3, padding=1),   # (B, 192, 8, 8)nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2),    # (B, 192, 4, 4)nn.Conv2d(192, 384, kernel_size=3, padding=1),  # (B, 384, 4, 4)nn.ReLU(inplace=True),nn.Conv2d(384, 256, kernel_size=3, padding=1),  # (B, 256, 4, 4)nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),  # (B, 256, 4, 4)nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2),    # (B, 256, 2, 2))self.classifier = nn.Sequential(nn.Dropout(),nn.Linear(256 * 2 * 2, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Linear(4096, num_classes))def forward(self, x):x = self.features(x)x = x.view(x.size(0), 256 * 2 *2)x = self.classifier(x)return x

train.py

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from models import AlexNet
import matplotlib.pyplot as pltimport ssl
ssl._create_default_https_context = ssl._create_unverified_context# 定义数据预处理操作
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.491, 0.482, 0.446), (0.247, 0.243, 0.261))])# 加载CIFAR10训练集
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=2)# 定义设备(GPU优先,若可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 实例化模型
model = AlexNet(num_classes=10).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)# 训练轮次
epochs = 15def train(model, trainloader, criterion, optimizer, device):model.train()running_loss = 0.0correct = 0total = 0for i, data in enumerate(trainloader, 0):inputs, labels = data[0].to(device), data[1].to(device)optimizer.zero_grad()outputs = model(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item()_, predicted = outputs.max(1)total += labels.size(0)correct += predicted.eq(labels).sum().item()epoch_loss = running_loss / len(trainloader)epoch_acc = 100. * correct / totalreturn epoch_loss, epoch_accif __name__ == "__main__":loss_history, acc_history = [], []for epoch in range(epochs):train_loss, train_acc = train(model, trainloader, criterion, optimizer, device)print(f'Epoch {epoch + 1}: Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%')loss_history.append(train_loss)acc_history.append(train_acc)# 保存模型权重,每5轮次保存到weights文件夹下if (epoch + 1) % 5 == 0:torch.save(model.state_dict(), f'weights/alexnet_epoch_{epoch + 1}.pth')# 绘制损失曲线plt.plot(range(1, epochs+1), loss_history, label='Loss', marker='o')plt.xlabel('Epoch')plt.ylabel('Loss')plt.title('Training Loss Curve')plt.legend()plt.savefig('results\\train_loss_curve.png')plt.close()# 绘制准确率曲线plt.plot(range(1, epochs+1), acc_history, label='Accuracy', marker='o')plt.xlabel('Epoch')plt.ylabel('Accuracy (%)')plt.title('Training Accuracy Curve')plt.legend()plt.savefig('results\\train_acc_curve.png')plt.close()

test.py

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from models import AlexNetimport ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 定义数据预处理操作
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.491, 0.482, 0.446), (0.247, 0.243, 0.261))])# 加载CIFAR10测试集
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=2)# 定义设备(GPU优先,若可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 实例化模型
model = AlexNet(num_classes=10).to(device)
criterion = nn.CrossEntropyLoss()# 加载模型权重
weights_path = "weights/alexnet_epoch_15.pth"  
model.load_state_dict(torch.load(weights_path, map_location=device))def test(model, testloader, criterion, device):model.eval()running_loss = 0.0correct = 0total = 0with torch.no_grad():for data in testloader:inputs, labels = data[0].to(device), data[1].to(device)outputs = model(inputs)loss = criterion(outputs, labels)running_loss += loss.item()_, predicted = outputs.max(1)total += labels.size(0)correct += predicted.eq(labels).sum().item()epoch_loss = running_loss / len(testloader)epoch_acc = 100. * correct / totalreturn epoch_loss, epoch_accif __name__ == "__main__":test_loss, test_acc = test(model, testloader, criterion, device)print("================AlexNet Test================")print(f"Load Model Weights From: {weights_path}")print(f'Test Loss: {test_loss:.4f}, Test Acc: {test_acc:.2f}%')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/494194.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 杨辉三角 - 力扣(LeetCode)

点击链接即可产看题目:118. 杨辉三角 - 力扣(LeetCode) 一、题目 给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出…

【JetPack】WorkManager笔记

WorkManager简介: WorkManager 是 Android Jetpack 库中的一个重要组件。它用于处理那些需要在后台可靠执行的任务,这些任务可以是一次性的,也可以是周期性的,甚至是需要满足特定条件才执行的任务。例如,它可以用于在后…

GTID详解

概念和组成 1,全局事务表示:global transaction identifiers 2, GTID和事务一一对应,并且全局唯一 3,一个GTID在一个服务器上只执行一次 4,mysql 5.6.5开始支持 组成 GTID server_uuid:transaction_id 如&#xf…

常耀斌:深度学习和大模型原理与实战(深度好文)

目录 机器学习 深度学习 Transformer大模型架构 人工神经元网络 卷积神经网络 深度学习是革命性的技术成果,有利推动了计算机视觉、自然语言处理、语音识别、强化学习和统计建模的快速发展。 深度学习在计算机视觉领域上,发展突飞猛进,…

vsCode怎么使用vue指令快捷生成代码

1.下载Vetur插件 2.在文件-首选项-配置代码片段中找到vue.json文件 (注:旧版本的编辑器路径为文件-首选项-用户片段) 3.在打开的配置代码片段弹窗中搜索vue.json,找到并打开 (注:如果搜不到的话就按住鼠标…

python学opencv|读取图像(十八)使用cv2.line创造线段

【1】引言 前序已经完成了opencv基础知识的学习,我们已经掌握了处理视频和图像的基本操作。相关文章包括且不限于: python学opencv|读取图像(三)放大和缩小图像_python(1)使用opencv读取并显示图像;(2)使用opencv对图像进行缩放…

unity webgl部署到iis报错

Unable to parse Build/WebGLOut.framework.js.unityweb! The file is corrupt, or compression was misconfigured? (check Content-Encoding HTTP Response Header on web server) iis报错的 .unityweb application/octet-stream iis中添加 MIME类型 .data applicatio…

【深度学习】零基础介绍循环神经网络(RNN)

RNN介绍 零基础介绍语言处理技术基本介绍分词算法词法分析工具文本分类与聚类情感分析 自然语言处理词向量词向量学习模型1. 神经网络语言模型2. CBOW 和 skip-gram3. 层次化softmax方法4. 负采样方法 RNN介绍RNN的变种:LSTM1. Forget Gate2. Input Gate3. Update M…

Docker Compose 安装 Harbor

我使用的系统是rocky Linux 9 1. 准备环境 确保你的系统已经安装了以下工具: DockerDocker ComposeOpenSSL(用于生成证书)#如果不需要通过https连接的可以不设置 1.1 安装 Docker 如果尚未安装 Docker,可以参考以下命令安装&…

面试题整理9----谈谈对k8s的理解1

谈谈对k8s的理解 1. Kubernetes 概念 1.1 Kubernetes是什么 Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统,其服务、支持和工具的…

【JAVA】JAVA接口公共返回体ResponseData封装

一、JAVA接口公共返回体ResponseData封装&#xff0c;使用泛型的经典 例子 public class ResponseData<T> implements Serializable { /** * */ private static final long serialVersionUID 7098362967623367826L; /** * 响应状态码 */ …

Redis分片集群学习总结

Redis分片集群学习总结 为什么要使用分片集群&#xff1f;分片集群搭建Redis集群怎么写入读取数据呢&#xff1f;集群写入数据和读取数据怎么定位到对应的节点呢&#xff1f;怎么让多个数据写入同一个节点&#xff1f; 故障转移主从集群和分片集群使用场景 为什么要使用分片集群…

代理模式(JDK,CGLIB动态代理,AOP切面编程)

代理模式是一种结构型设计模式&#xff0c;它通过一个代理对象作为中间层来控制对目标对象的访问&#xff0c;从而增强或扩展目标对象的功能&#xff0c;同时保持客户端对目标对象的使用方式一致。 代理模式在Java中的应用,例如 1.统一异常处理 2.Mybatis使用代理 3.Spring…

入侵他人电脑,实现远程控制(待补充)

待补充 在获取他人无线网网络密码后&#xff0c;进一步的操作是实现入侵他人电脑&#xff0c;这一步需要获取对方的IP地址并需要制作自己的代码工具自动化的开启或者打开对方的远程访问权限。 1、获取IP地址&#xff08;通过伪造的网页、伪造的Windows窗口、hook&#xff0c;信…

windows安装Elasticsearch及增删改查操作

1.首先去官网下载Elasticsearch 下载地址 我这里选择的是7.17.18 选择windows版本 下载完成后解压是这样的 下载完成后点击elasticsearch.bat启动elasticsearch服务 输入http://localhost:9200看到如下信息说明启动成功。 还有记得修改elasticsearch.yml文件&#xff0c;…

aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发

aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发 学习内容&#xff1a; 使用本地EC2中部署docker应用使用ECS的EC2模式进行容器开发使用ECS的Fargate模式进行容器开发 1. 使用本地EC2中部署docker应用 docker整体 这里展示了docker的整体流程。 开发阶段 编写dockerfile…

电脑使用CDR时弹出错误“计算机丢失mfc140u.dll”是什么原因?“计算机丢失mfc140u.dll”要怎么解决?

电脑使用CDR时弹出“计算机丢失mfc140u.dll”错误&#xff1a;原因与解决方案 在日常电脑使用中&#xff0c;我们时常会遇到各种系统报错和文件丢失问题。特别是当我们使用某些特定软件&#xff0c;如CorelDRAW&#xff08;简称CDR&#xff09;时&#xff0c;可能会遇到“计算…

Set集合进行!contains判断IDEA提示Unnecessary ‘contains()‘ check

之前写过一个代码&#xff0c;用到了Set集合&#xff0c;判断了如果某个元素不存在就添加到集合中。今天翻看代码又看到了IDEAUnnecessary contains() check爆黄提示。 来一段测试代码&#xff1a; public class SetTest {public static void main(String[] args) {Set<Int…

以太网帧、IP数据报图解

注&#xff1a;本文为 “以太网帧、IP数据报”图解相关文章合辑。 未整理去重。 以太网帧、IP数据报的图解格式&#xff08;包含相关例题讲解&#xff09; Rebecca.Yan已于 2023-05-27 14:13:19 修改 一、基础知识 UDP 段、IP 数据包&#xff0c;以太网帧图示 通信过程中&…

Java程序打包成exe,无Java环境也能运行

Java程序开发完成后&#xff0c;通常情况下以jar包的形式发布。但有时我们需要给非软件开发人员使用程序&#xff0c;如制作好窗体应用&#xff0c;把它发给没有java开发环境的人使用&#xff0c;此时就需要制作exe安装包。本文介绍如何将java程序制作成exe安装包&#xff0c;并…