前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >精读 AlexNet 并使用 PyTorch 实现图像分类器

精读 AlexNet 并使用 PyTorch 实现图像分类器

原创
作者头像
繁依Fanyi
修改2025-03-21 19:38:09
修改2025-03-21 19:38:09
5900
代码可运行
举报
运行总次数:0
代码可运行

在深度学习的历史上,AlexNet 可谓是一座里程碑。2012 年,这款由 Alex Krizhevsky 等人提出的卷积神经网络在 ImageNet 图像识别大赛中以绝对优势震撼了整个计算机视觉界,开启了深度学习的黄金时代。尽管 AlexNet 这篇论文今天被很多大佬拉出来“鞭尸”,但对于我这样的初学者小白来说,还是一篇值得阅读的论文。

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

一、AlexNet 的诞生与历史背景

在 2012 年之前,图像识别大多依赖传统方法和浅层网络,数据量和计算资源的限制使得这些方法在处理大规模图像数据时捉襟见肘。就在那时,AlexNet 的出现大幅提升了分类准确率,也用深层卷积网络证明了计算机完全可以像人类一样“看”懂图像。从论文来看 AlexNet 的成功归功于以下内容:

  • 深层结构:相比之前只有 2-3 层的浅层网络,AlexNet 拥有 8 层甚至更多,这让网络能够从低级特征(如边缘、纹理)逐步提取出更高级别的语义信息(如物体形状和类别特征)。
  • ReLU 激活函数:相比传统的 Sigmoid 或 Tanh,ReLU 激活函数大大加速了网络训练,解决了梯度消失问题,让深层网络的训练变得可行。
  • Dropout 正则化:在全连接层中引入 Dropout,有效防止了过拟合问题,使得模型在测试集上表现得更加稳定和强健。
  • 端到端(End2End):传统的图像分类方法通常需要先进行特征提取,再进行分类,这种分步式方法复杂且容易引入误差。AlexNet 采用了端到端的训练方式,即直接从原始图像输入,经过神经网络处理,输出分类结果。简化了训练和推理流程,后续深度学习模型的发展。
  • 局部响应归一化(LRN):这是一种模仿生物神经系统机制的归一化方法,用以提升模型的竞争性和鲁棒性,不过这一过程和正则化过程似乎存在很多争议。
  • GPU 加速:AlexNet 率先采用 GPU 并行计算,加快了模型训练的速度,充分利用了当时刚刚兴起的高性能计算资源。

二、AlexNet 论文核心内容简读

2.1 深层卷积网络

在传统的图像识别方法中,很多早期的神经网络只有 2-3 层,这种浅层网络只能捕捉到图像中最基本、最简单的特征,比如边缘或简单的纹理。想象一下,你在看一幅画时,如果只看最外层的轮廓,往往无法体会到画中细腻的情感和复杂的细节。类似地,浅层网络无法充分表达图像中的复杂模式。

AlexNet 则采用了深层结构,包含 8 层(其中 5 层为卷积层,3 层为全连接层),这使得网络可以层层递进地提取特征:

  • 第一层:捕捉最基本的低级特征,如边缘、角点和简单纹理。
  • 中间层:将这些低级特征组合,形成更复杂的形状和局部结构。
  • 最后几层:进一步抽象,将局部组合成语义信息,最终识别出图像中物体的类别。

公式上,我们可以将每一层的卷积操作描述为:

y_{i,j}^{(l)} = f\left(\sum_{m,n} x_{i+m,j+n}^{(l-1)} \cdot w_{m,n}^{(l)} + b^{(l)}\right)

$x^{(l-1)}$ 表示上一层的输入,$w^{(l)}$ 表示当前层的卷积核权重,$b^{(l)}$ 为偏置,$f$ 为激活函数(例如 ReLU)。这种局部运算保证了每个神经元只关注输入的一小部分区域(局部感受野),同时,通过多个卷积层的堆叠,整个网络能够捕捉到从局部细节到整体结构的多级特征。


2.2 ReLU 激活函数

在神经网络中,激活函数的作用是引入非线性,使得网络可以学习到复杂的映射关系。过去,大多数人使用 Sigmoid 或 Tanh 激活函数,但这些函数在深层网络中存在梯度消失的问题,即在反向传播过程中,梯度值不断变小,导致网络无法有效更新参数。

AlexNet 在其网络中采用了 ReLU(Rectified Linear Unit)激活函数,其数学表达式非常简单:

f(x) = \max(0, x)

可以将 ReLU 想象成一部“电源开关”,当电流(输入值)足够强时开关“打开”,反之则关闭。不过论文中灭有说明 ReLU 比 Sigmoid 训练速度快的原因,现在看也没有快多少,纯粹是因为它简单。


2.3 Dropout 正则化

在训练神经网络时,过拟合是一个常见问题。过拟合指的是模型在训练数据上表现很好,但在新数据(测试数据)上效果很差。原因在于模型可能学到了训练数据中的噪声和偶然性,而不是普遍适用的规律。

为了防止过拟合,AlexNet 在全连接层中引入了 Dropout 技术。Dropout 的基本思想是在每次训练时,随机“丢弃”一部分神经元(即暂时将它们的输出设为 0),从而使网络不依赖于某些特定的神经元,而是学习到多个神经元之间的冗余和互补关系。

假设一个全连接层有 100 个神经元,在每一次前向传播过程中,Dropout 会以一定的概率(例如 0.5)随机关闭一半的神经元,使其在训练过程中不能过分依赖局部信息,而是必须从全局角度考虑问题。从而提高模型在测试集上的泛化能力。


2.4 局部响应归一化(LRN)

在卷积操作后,不同神经元的响应值可能相差悬殊。局部响应归一化(LRN)技术就是为了让神经元之间的响应值更加平衡,模拟人类视觉系统中神经元间的相互抑制机制。

LRN 的基本思想是在同一局部区域内,对神经元的响应值进行归一化处理。简单来说,假设某个神经元的响应值较高,那么它所在区域的其他神经元响应值就会被适当地抑制。这种方式有助于突出最显著的特征,并减少噪声干扰,使得网络输出更稳定。

公式上,局部响应归一化可以写成:

b_{x,y}^{(i)} = \frac{a_{x,y}^{(i)}}{\left(k + \alpha \sum_{j=\max(0, i - n/2)}^{\min(N-1, i + n/2)} (a_{x,y}^{(j)})^2 \right)^\beta}

其中,a_{x,y}^{(i)} 是位置 (x,y) 上第 i 个神经元的响应值, k, \alpha, \betan 是超参数。

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

2.5 GPU 加速

深层神经网络的训练涉及大量的矩阵运算和并行计算,这些任务传统 CPU 处理起来速度非常慢。GPU(图形处理单元)则专为大规模并行运算设计,能够同时处理数千个甚至上万个运算单元。

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

AlexNet 是利用多 GPU 加速训练的深层网络之一。通过使用两块 NVIDIA GTX 580 3GB GPU,AlexNet 的训练速度得到了提升(虽然在当时训练速度仍不及 CPU)。至于这个结构图,B站李沐老师讲的很易懂,大家可以去康康。


三、PyTorch 复现 AlexNet 实现图像分类器

在理解了 AlexNet 论文中的核心思想之后,接下来我们将通过代码实践,使用 PyTorch 实现一个基于 AlexNet 的图像分类器。我们选用 CIFAR-10 数据集,它包含 10 个类别的彩色图片,虽然规模和难度与 ImageNet 有差异,但对于初学者来说已经足够进行实验。

3.1 环境准备与数据加载

首先,在 Cloud Studio 上创建一个新的 Pytorch 项目,并确保环境中已安装 PyTorch 和 torchvision。接着,我们使用 torchvision 加载 CIFAR-10 数据集,并通过 transforms 对数据进行预处理,统一图片尺寸和归一化。

代码语言:python
代码运行次数:0
运行
复制
import torch
import torchvision
import torchvision.transforms as transforms

# 定义数据预处理:调整图片大小为 224x224(AlexNet 的输入尺寸),转换为 Tensor,并归一化
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# 加载 CIFAR-10 数据集
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)

# 定义 DataLoader,批量加载数据,设置 batch_size 为 32
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=32, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=32, shuffle=False)

数据集总大小 170MB 左右,下载完成后 DataLoader 就会调整图片大小、转换为张量和归一化。不过国内下载太慢,大家可以从网盘找一个下载完,上传到 Cloud Studio 上去。

3.2 构建 AlexNet 模型

接下来,我们构建 AlexNet 模型。由于原始 AlexNet 结构较为庞大,为了便于理解和实验,我们实现一个简化版。模型主要包括五个卷积层和三个全连接层,同时使用 ReLU、Dropout 和最大池化层来增强模型性能。

代码语言:python
代码运行次数:0
运行
复制
import torch.nn as nn
import torch.nn.functional as F

class AlexNet(nn.Module):
    def __init__(self, num_classes=10):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            # 第一层卷积:输入 3 通道,输出 64 个特征图,卷积核 11x11,步长 4,填充 2
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            # 第二层卷积:输入 64,输出 192 个特征图,卷积核 5x5,填充 2
            nn.Conv2d(64, 192, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            # 第三层卷积:输入 192,输出 384 个特征图,卷积核 3x3,填充 1
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            # 第四层卷积:输入 384,输出 256 个特征图,卷积核 3x3,填充 1
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            # 第五层卷积:输入 256,输出 256 个特征图,卷积核 3x3,填充 1
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2)
        )
        self.classifier = nn.Sequential(
            nn.Dropout(),
            # 全连接层:输入由卷积层输出的特征图展平后,大小为 256*6*6
            nn.Linear(256 * 6 * 6, 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), -1)
        # 通过分类器
        x = self.classifier(x)
        return x

# 创建模型实例并打印结构
model = AlexNet(num_classes=10)
print("模型结构:\n", model)
在这里插入图片描述
在这里插入图片描述

3.3 模型训练

有了数据和模型后,下一步就是训练模型。训练的目标是让模型在大量图片上不断调整参数,从而使得预测结果逐步接近真实标签。我们采用交叉熵损失函数和 Adam 优化器来实现这一过程。

代码语言:python
代码运行次数:0
运行
复制
import torch.optim as optim

# 定义损失函数(交叉熵损失)和优化器(Adam)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 设定训练轮数
num_epochs = 20

for epoch in range(num_epochs):
    running_loss = 0.0
    for i, (images, labels) in enumerate(train_loader):
        # 前向传播:将图片输入模型,获得预测结果
        outputs = model(images)
        loss = criterion(outputs, labels)
        
        # 反向传播前先清空梯度
        optimizer.zero_grad()
        # 反向传播:自动计算梯度
        loss.backward()
        # 更新模型参数
        optimizer.step()
        
        running_loss += loss.item()
        if (i+1) % 100 == 0:
            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
            running_loss = 0.0

print("训练完成!")

训练过程中,模型不断调整参数,损失逐步降低,说明模型在不断学习如何更好地对图片进行分类。

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

每一次的参数更新都相当于模型在朝着正确答案迈进一步。

训练了四个多小时后,训练完成。

3.4 模型评估与推理

训练完成后,我们需要评估模型在测试集上的表现,并展示部分预测结果,以直观感受模型的分类效果。

代码语言:python
代码运行次数:0
运行
复制
model.eval()  # 将模型设置为评估模式,关闭 Dropout 等训练特性
correct = 0
total = 0

with torch.no_grad():
    for images, labels in test_loader:
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f'测试集准确率:{100 * correct / total:.2f}%')

# 展示部分测试图片和预测结果
import matplotlib.pyplot as plt
import numpy as np

dataiter = iter(test_loader)
images, labels = next(dataiter)

def imshow(img):
    img = img / 2 + 0.5  # 反归一化
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

imshow(torchvision.utils.make_grid(images[:8]))

outputs = model(images[:8])
_, predicted = torch.max(outputs, 1)
print("预测结果:", predicted.numpy())

计算了模型在整个测试集上的准确率,并展示了部分测试图片及其预测结果,让你直观感受到模型的实际表现。

3.5 模型保存与加载

为了方便以后使用,我们可以将训练好的模型保存到文件中,以便随时加载,无需重新训练。

代码语言:python
代码运行次数:0
运行
复制
# 保存模型参数
torch.save(model.state_dict(), "alexnet_model.pth")
print("模型保存成功!")

# 加载模型参数
loaded_model = AlexNet(num_classes=10)
loaded_model.load_state_dict(torch.load("alexnet_model.pth"))
loaded_model.eval()
print("模型加载成功,开始推理...")

with torch.no_grad():
    outputs = loaded_model(images[:8])
    _, predicted = torch.max(outputs, 1)
    print("加载后预测结果:", predicted.numpy())

四、结语

精读 AlexNet 论文,我们简单了解了其核心设计思想——深层卷积结构、ReLU 激活、Dropout 正则化、数据增强、局部响应归一化和 GPU 加速等创新技术,这些技术共同推动了当时深度学习的发展。

AlexNet 的成功不仅证明了深层卷积神经网络在大规模图像识别任务中的巨大潜力,也为后续的网络设计提供了宝贵的经验和思路。

AlexNet 源码似乎今天也在 GitHub 上开源了,大家可以去康康!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、AlexNet 的诞生与历史背景
  • 二、AlexNet 论文核心内容简读
    • 2.1 深层卷积网络
    • 2.2 ReLU 激活函数
    • 2.3 Dropout 正则化
    • 2.4 局部响应归一化(LRN)
    • 2.5 GPU 加速
  • 三、PyTorch 复现 AlexNet 实现图像分类器
    • 3.1 环境准备与数据加载
    • 3.2 构建 AlexNet 模型
    • 3.3 模型训练
    • 3.4 模型评估与推理
    • 3.5 模型保存与加载
  • 四、结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档