AlexNet文献阅读与代码实现

news/2025/1/12 22:26:51/文章来源:https://www.cnblogs.com/-yuanyuan/p/18667489

目录
  • AlexNet文献阅读与代码实现
    • 文献内容介绍
    • 代码实现
    • 内容总结

AlexNet文献阅读与代码实现

前言:笔者目前研一,刚开始入门深度学习,所以想记录一下自己学习的过程,接下来的时间里,我会定期阅读深度学习领域的经典文献,并尝试用代码实现它们,也欢迎大家积极评论。

:博客本身更侧重于代码实现,关于一些深度学习的概念,如卷积核之类的内容,大家有疑惑的话可以自行搜索。

文献地址:

ImageNet Classification with Deep Convolutional Neural Networks

文献内容讲解:

9年后重读深度学习奠基作之一:AlexNet【论文精读·2】_哔哩哔哩_bilibili

一些术语:

术语 含义
trick 一些技巧,比如alexnet就提到它采用了dropout、relu这样的技巧来加速训练并避免过拟合

一些超参

超参名 含义
weight decay weight decay是一种正则化技术,防止模型在训练数据上过拟合
momentum momentum是一种加速梯度下降的优化方法,它通过考虑前一个步骤的更新方向,来帮助当前的梯度更新更快地收敛
batchsize 将几个数据放到一起作为一个批次

可以在优化器里很轻松的设置它们:

optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=1e-4)

文献内容介绍

​ 这篇文章是2012年发表的,作者为Alex Krizhevsky,Ilya Sutskever,Geoffrey Hinton(前段时间拿到诺贝尔物理学家的那位,因为这事我才了解到这位大佬)。AlexNet在2012年的ImageNet比赛中取得了显著成功。(AlexNet是一个大型的用于图片分类的数据集),这篇文章最终发表在2012年的NIPS(NeurIPS)会议上。自此之后,深度学习在计算机视觉领域得到广泛应用。

​ AlexNet由五层卷积,和三层全连接组成,网络结构如下所示:

pEPyjgK.png

​ 网络是上下两层的原因是GPU容量不够,所以作者把图片切开,分成两层来训练,最后拼接起来。他们用的GPU是GTX 580,只有3G的内存,而我们现在实现的时候不需要再切开了。所以我们可以把上下拼起来来看这个网络结构。(当然,作者在第二层的卷积后,做了一次上下两层连接的工作,我们现在忽略这一步)

文章第三节采用了一些trick来加速训练。

  1. Relu

​ 之前的文章主要采用sigmod来作为激活函数,而本文采用了relu,relu训练起来会快很多。(不过我也不知道为什么)

  1. 用多个GPU训练

​ 文章中介绍他如何采用多个GPU来训练,也就是把数据切开来分开训练

  1. 正则化(local response normalization)

​ 就是把数据归一化(现在来看这个东西不太重要)

  1. overlapping pooling

​ 对传统的pooling技术做了改进(这个我也没懂)

文章第四节介绍了一些方法来避免过拟合

  1. 数据增强

​ 原图像是256 * 256的,它随机裁剪出224 * 224的部分出来,这样数据量就变为原来2048倍(现在来看不能这么算,因为裁剪出来的很多是一样的)

  1. dropout

​ 作者把前两层全连接层中随机的50%的权重设为0,以此来避免过拟合

代码实现

​ 为了减小内存的开销,我们这里采用的数据集是minist数据集(也就是手写数字识别的数据集),也就是把输入图像变为1 * 28 * 28大小的图像,输出类别变为了10(也就是0-9)。代码目录如下所示:

pEPyy7j.png

​ 在data目录下,存放我们的minist数据集,model.py存放我们设计好的网络模型,train.py实现对模型的训练,并在训练完成后保存模型权重,test.py加载保存好的权重并进行测试。

​ 一般而言深度学习的项目会有一个专门的文件夹用来处理数据,我们这里为了方便起见,直接调用了别人做好的数据,因此不用再额外写处理数据的代码。(数据集是自动下载的,权重是训练过程中保存的,也就是只要有3个.py文件,项目就可以运行起来)

model.py

AlextNet类是我们设计的网络模型,后面的main函数是我们用来对当前文件做测试的(建议为每个python文件都添加main函数,这样方便我们单独测试每一个文件)

import torch
import torch.nn as nnclass AlexNet(nn.Module):def __init__(self):super(AlexNet, self).__init__()self.features = nn.Sequential(nn.Conv2d(1, 96, kernel_size=5, stride=1),  # 输入通道为1(灰度图)nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),  # 下采样nn.Conv2d(96, 256, kernel_size=5, stride=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(256, 384, kernel_size=3, stride=1, padding=1),nn.ReLU(inplace=True),nn.Conv2d(384, 384, kernel_size=3, stride=1, padding=1),nn.ReLU(inplace=True),nn.Conv2d(384, 256, kernel_size=3, stride=1, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),  # 这里的池化)self.classifier = nn.Sequential(nn.Dropout(),nn.Linear(256 * 2 * 2, 4096),  # 修改为256 * 2 * 2nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Linear(4096, 10),  # MNIST有10个类)def forward(self, x):print(f"Input shape: {x.shape}")  # 打印输入形状# 第一层x = self.features[0](x) # 经过5 * 5的卷积 大小变为[1, 96, 24, 24]x = self.features[1](x) # 经过relu 大小不变x = self.features[2](x) # 经过池化 大小变为[1, 96, 12, 12]# 第二层x = self.features[3](x) # 经过5 * 5的卷积x = self.features[4](x)x = self.features[5](x)# 第三层x = self.features[6](x) # 经过3 * 3的卷积x = self.features[7](x)# 第四层x = self.features[8](x) # 经过3 * 3的卷积x = self.features[9](x)# 第五层x = self.features[10](x) # 经过3 * 3的卷积x = self.features[11](x)x = self.features[12](x)  # Max Poolx = x.view(x.size(0), 256 * 2 * 2)  # 展平# 后面的全连接层x = self.classifier(x)  # 分类return xif __name__ == "__main__":model = AlexNet()# 使用 torch.randn 生成随机输入,形状为 (1, 1, 28, 28)test_input = torch.randn(1, 1, 28, 28)  # 生成一个随机的28x28图像output = model(test_input)print(output.shape)  # 应该输出 (1, 10) 对应于一个样本的10个类的输出

train.py

import os
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from model import AlexNet  # 确保你的model.py文件在合适的位置# 设置随机种子以确保可重复性
torch.manual_seed(42)# 配置超参数
batch_size = 64
learning_rate = 0.001
num_epochs = 10
data_dir = './data'  # 数据存放路径# 数据变换(适合MNIST的数据)
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))  # 假设输入通道为1(灰度图)
])# 检查数据存放路径是否存在,如果不存在,则下载数据集
if not os.path.exists(data_dir):os.makedirs(data_dir)# 载入MNIST数据集,设置download=True以下载数据(如果尚未存在)
train_dataset = datasets.MNIST(root=data_dir, train=True, download=True, transform=transform)
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)# 创建模型实例
model = AlexNet()
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=1e-4)# 训练函数
def train(model, train_loader, criterion, optimizer, num_epochs):model.train()  # 设定模型为训练模式best_accuracy = 0.0  # 记录最佳准确率for epoch in range(num_epochs):running_loss = 0.0correct = 0total = 0for i, (images, labels) in enumerate(train_loader):images, labels = images.to(device), labels.to(device)# 前向传播outputs = model(images)loss = criterion(outputs, labels)# 反向传播和优化optimizer.zero_grad()  # 清空梯度loss.backward()  # 计算梯度optimizer.step()  # 更新参数# 统计running_loss += loss.item()_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()# 每十个batch打印loss和准确率if (i + 1) % 10 == 0:print(f'Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{len(train_loader)}], Loss: {loss.item():.4f}, Accuracy: {100 * correct / total:.2f}%')# 计算并打印每个epoch的平均loss和准确率avg_loss = running_loss / len(train_loader)avg_accuracy = correct / total * 100print(f'Epoch [{epoch + 1}/{num_epochs}], Average Loss: {avg_loss:.4f}, Average Accuracy: {avg_accuracy:.2f}%')# 保存最佳模型if avg_accuracy > best_accuracy:best_accuracy = avg_accuracytorch.save(model.state_dict(), 'best_model.pth')  # 保存模型权重print(f'Saved best model with accuracy: {best_accuracy:.2f}%')if __name__ == "__main__":train(model, train_loader, criterion, optimizer, num_epochs)

test.py

import torch  
import torch.nn as nn  
from torch.utils.data import DataLoader  
from torchvision import datasets, transforms  
from model import AlexNet  # 确保你的model.py文件在合适的位置  # 设置设备  
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')  # 数据变换(适合MNIST的数据)  
transform = transforms.Compose([  transforms.ToTensor(),  transforms.Normalize((0.5,), (0.5,))  # 假设输入通道为1(灰度图)  
])  # 载入MNIST测试集  
test_dataset = datasets.MNIST(root='./data', train=False, download=False, transform=transform)  
test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)  # 创建模型实例并加载最佳模型权重  
model = AlexNet()  
model.load_state_dict(torch.load('best_model.pth'))  
model.to(device)  
model.eval()  # 设置为评估模式  # 测试函数  
def test(model, test_loader):  correct = 0  total = 0  with torch.no_grad():  # 禁用梯度计算  for images, labels in test_loader:  images, labels = images.to(device), labels.to(device)  # 前向传播  outputs = model(images)  _, predicted = torch.max(outputs.data, 1)  total += labels.size(0)  # 总样本数量  correct += (predicted == labels).sum().item()  # 统计正确分类的样本数量  accuracy = 100 * correct / total  print(f'Test Accuracy: {accuracy:.2f}%')  if __name__ == "__main__":  test(model, test_loader)

最终输出的准确率为99.2%,可以通过调参,将结果调到99.5%,手写数字识别数据集还是比较简单的,后续我会尝试用其他更大的数据集进行训练。

内容总结

我对卷积的认知就是提取特征并降低维度。因为图像往往都比较大,比如说224 * 224,如果直接把它展平,作为一个向量输入到全连接网络中,参数量就太大了,这样很难去训练。所以我们在使用全连接网络之前会通过卷积来提取图像特征,然后再它图像展平作为全连接网络的输入,最终得到我们想要的结果。大家也可以尝试多加几层卷积,来尝试对准确率的影响。

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

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

相关文章

c语言的基本类型及输入输出占位符

基本类型 c语言的基本类型:int 【long、short、unsigned、signed(c90)】、char、float、double、_Bool【布尔值】、_complex、_Imaginary【复数和虚数】 输入输出 printf标志字符标志 含义- 转换的结果在字段内左对齐+ 有符号的转换结果总是以+或者-开始空格 如果有符号转换的…

LeetCode:65.有效数字

LeetCode:65.有效数字解题步骤构建一个表示状态的图。遍历字符串,并沿着图走,如果到了某个节点无路可走就返false。遍历结束,如走到3/5/6,就返回true,否则返回false。 extend 2 8 10 16进制 /*** 检查一个字符串是否可以表示为一个有效的数字* @param {string} s - 待检查的…

vs2019项目报错:文件中的类都不能进行设计,解决办法_无法设计基类system.void

文件中的类都不能进行设计,因此未能为该文件显示设计器。设计器检查出文件中有以下类: Form1 --- 无法设计基类“System.Void”。 出现上述错误,解决办法如下: 关闭所有设计窗口(例如form1.cs(设计)),菜单栏生成,清理解决方案,关闭vs2022,重新启动vs2022打开你的项目…

关于VSCode的c/c++环境配置

适用于Windows的VSCode的c/c++环境配置c/c++环境配置写给 初学C/C++的 保姆级 VSCode环境配置第一步 下载VScode 找到官网点下载就好啦下载安装完成之后,启动!点击左侧“拓展”图标下载中文拓展安装好后重启 别关 留着备用 第二步 下载c/c++编译器 这里我使用MinGw实际上 Mi…

G75 拉插 CF622F The Sum of the k-th Powers

视频链接: The Sum of the k-th Powers - 洛谷 | 计算机科学教育新生态// 拉插 O(klogn) #include <iostream> #include <cstring> #include <algorithm> using namespace std;#define LL long long const int N=1e6+10,mod=1e9+7; int n,k; LL ml[N],mr…

Cline 免费插件 + Qwen2.5 大模型,零经验也能开发“对联王”微信小程序

本文详细介绍了如何利用免费的 Cline 插件辅助开发一款名为“对联王”的微信小程序,特别适合希望在春节期间创作春联的用户。文章不仅比较了 Cline 与 Cursor 两款工具的优缺点,还分享了作者实际操作中的经验和建议。通过具体步骤演示,即使是编程新手也能跟随指导,结合微信…

实现无感刷新Token技术:.Net Web API与axios的完美结合

备忘:https://mp.weixin.qq.com/s?__biz=MjM5MDE5MDM5NA==&mid=2449944319&idx=1&sn=71e84d8ee24769e77b19ca8367333b8f&chksm=b1bb10aa86cc99bc2f20686354e8184023278de74dba857a42d720dc47fabb654c12ecb83524&scene=21#wechat_redirect 我们都知道Toke…

方法引用与lambda底层原理Java方法引用、lambda能被序列化么?

系列文章目录和关于我 0.引入 最近笔者使用flink实现一些实时数据清洗(从kafka清洗数据写入到clickhouse)的功能,在编写flink作业后进行上传,发现运行的时候抛出:java.io.NotSerializableException,错误消息可能类似于 “org.apache.flink.streaming.api.functions.MapFu…

Java方法引用、lambda如何序列化方法引用与lambda底层原理

系列文章目录和关于我 0.引入 最近笔者使用flink实现一些实时数据清洗(从kafka清洗数据写入到clickhouse)的功能,在编写flink作业后进行上传,发现运行的时候抛出:java.io.NotSerializableException,错误消息可能类似于 “org.apache.flink.streaming.api.functions.MapFu…

2024年秋学期 分析力学(理论物理基础Ⅰ)笔记

内容说明舍去了哈密顿雅可比方程等内容 删去了振动相关的一些模型,如参数共振等 授课难度疑似过大了一点(毕竟才半个学期),协变相关内容疑似太tm多了 有心力场模块笔记有所省略,部分笔记不排除记录有误的可能性 部分章节间未换页笔记正文拉格朗日力学及其协变形式 哈密顿力…

find_package()使用指南

关于find_package() 在使用cmake引用第三方库(比如OpenCV)时,我们总是使用find_package()这个指令来实现对包的查找(比如find_package(OpenCV))。调用完后就可以使用一些似乎凭空出现的变量如${OpenCV_INCLUDE_DIRS}以及${OpenCV_LIBS},分别指示了OpenCV库的头文件路径以及各个…

音视频:JavaCV 采集桌面画面并显示的方法

需要进行简单的音视频编程,如果不是特别熟悉C/C++,那么JavaCV应该是比较好的选择,下面记录一下使用JavaCV采集桌面数据的方法。 使用FFmpegFrameGrabber采集桌面数据,指定输入源为desktop,format设置为gdigrab。参考雷神的博客:https://blog.csdn.net/leixiaohua1020/art…