AI知识分享AI知识分享
✿导航
  • 人工智能
  • 神经网络
  • 机器学习
  • 深度学习
  • 强化学习
  • 自然语言处理
  • 计算机视觉
  • 大模型基础
  • 动手学深度学习
  • 理论理解
  • 工程实践
  • 应用开发
  • AI For Everyone
  • AIGC_2024大会
  • AIGC_2025大会
  • Transformer
  • Pytorch
  • HuggingFace
  • 蒸馏
  • RAG
  • 目标检测
  • MCP
  • 概念
  • 意图识别
  • 工具
✿导航
  • 人工智能
  • 神经网络
  • 机器学习
  • 深度学习
  • 强化学习
  • 自然语言处理
  • 计算机视觉
  • 大模型基础
  • 动手学深度学习
  • 理论理解
  • 工程实践
  • 应用开发
  • AI For Everyone
  • AIGC_2024大会
  • AIGC_2025大会
  • Transformer
  • Pytorch
  • HuggingFace
  • 蒸馏
  • RAG
  • 目标检测
  • MCP
  • 概念
  • 意图识别
  • 工具
  • 大模型基础

    • 语言模型基础

      • 概述
      • 基于统计方法的语言模型
      • 基于神经网络的语言模型
      • 语言模型的采样方法
      • 语言模型的评测
    • 大语言模型架构

      • 概述
      • 主流模型架构
      • Encoder-only
      • Encoder-Decoder
      • Decoder-only
      • 非Transformer 架构
    • Prompt工程

      • 工程简介
      • 上下文学习
      • 思维链
      • 技巧
    • 参数高效微调

      • 概述
      • 参数附加方法
      • 参数选择方法
      • 低秩适配方法
      • 实践与应用
    • 模型编辑

      • 简介
      • 方法
      • 附加参数法
      • 定位编辑法
    • RAG

      • 基础
      • 架构
      • 知识检索
      • 生成增强
  • 动手学深度学习

    • 深度学习基础

      • 引言
      • 数据操作
      • 数据预处理
      • 数学知识(线代、矩阵计算、求导)
      • 线性回归
      • 基础优化方法
      • Softmax回归
      • 感知机
      • 模型选择
      • 过拟合和欠拟合
      • 环境和分布偏移
      • 权重衰减
      • Dropout
      • 数值稳定性
    • 卷积神经网络

      • 模型基本操作
      • 从全连接层到卷积
      • 填充和步长
      • 多个输入和输出通道
      • 池化层
      • LeNet
      • AlexNet
      • VGG
      • NiN网络
      • GoogleNet
      • 批量归一化
      • ResNet
    • 计算机视觉

      • 图像增广
      • 微调
      • 目标检测
      • 锚框
      • 区域卷积神经网络
      • 单发多框检测
      • 一次看完
      • 语义分割
      • 转置卷积
      • 全连接卷积神经网络
      • 样式迁移
    • 循环神经网络

      • 序列模型
      • 语言模型
      • 循环神经网络
      • 序列到序列学习
      • 搜索策略
    • 注意力机制

      • 优化算法

AlexNet

参考文章

  • 手撕 CNN 经典网络之 AlexNet(理论篇)
  • 卷积神经网络经典回顾之 AlexNet

概念

LeNet-5 (1998) 和 AlexNet (2012) 是深度学习发展史上的两座里程碑。它们的关系可以概括为:LeNet 证明了卷积神经网络(CNN)的可行性,而 AlexNet 证明了 CNN 在大规模复杂任务上的统治力,并开启了现代深度学习时代。

AlexNet 是深度学习发展史上的一个里程碑式卷积神经网络(CNN)模型,由 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton 在 2012 年提出,并在当年的 ImageNet 大规模视觉识别挑战赛(ILSVRC-2012) 中以显著优势夺冠,大幅超越传统计算机视觉方法,从而开启了深度学习在计算机视觉领域的爆发式发展。

基本结构

AlexNet 输入为 RGB 三通道的 224 × 224 × 3 大小的图像(也可填充为 227 × 227 × 3 )。

  • AlexNet 共包含 5 个卷积层(包含 3 个池化)和 3 个全连接层。
    • 其中,每个卷积层都包含卷积核、偏置项、ReLU 激活函数和局部响应归一化(LRN)模块。
  • 第 1、2、5 个卷积层后面都跟着一个最大池化层,后三个层为全连接层。
  • 最终输出层为 softmax,将网络输出转化为概率值,用于预测图像的类别。

image-20260118073022746

核心特点

  1. 深层结构 AlexNet 包含 8 层可训练层:5 个卷积层 + 3 个全连接层。这在当时是相对较深的网络。

  2. ReLU 激活函数 首次在大型 CNN 中使用 ReLU(Rectified Linear Unit) 作为激活函数(f(x)=max⁡(0,x) f(x) = \max(0, x) f(x)=max(0,x)),相比传统的 tanh 或 sigmoid,训练速度更快且缓解了梯度消失问题。

  3. GPU 加速训练 利用 两块 NVIDIA GTX 580 GPU 并行训练,将计算负担分摊,使得训练大型 CNN 成为可能。

  4. 局部响应归一化(LRN) 在部分卷积层后引入 LRN,用于增强泛化能力(但后续研究发现效果有限,现代网络已较少使用)。

  5. 重叠池化(Overlapping Pooling) 使用 3×3 池化窗口、步长为 2,产生重叠区域,略微提升了性能并减少了过拟合。

  6. Dropout 正则化 在全连接层中使用 Dropout(丢弃率通常为 0.5),有效防止过拟合。

  7. 数据增强 通过图像平移、水平翻转和改变 RGB 通道强度等方式扩充训练数据,提升模型鲁棒性。

网络结构概览

卷积层输出尺寸计算公式:

输入图片(Input)大小为 WW,卷积核(Filter)大小为 KK,步长(stride)为 S,填充(Padding)的像素数为 P,那卷积层输出(Output)的特征图大小为 O 为

O = (W - K + 2P) / S + 1

层类型参数/配置
输入227×227×3 RGB 图像
Conv196 个 11×11 卷积核,stride=4 → 输出 55×55×96
ReLU + LRN + MaxPool (3×3, stride=2)→ 27×27×96
Conv2256 个 5×5 卷积核(分两组在两个 GPU 上)→ 27×27×256
ReLU + LRN + MaxPool→ 13×13×256
Conv3384 个 3×3 卷积核
Conv4384 个 3×3 卷积核
Conv5256 个 3×3 卷积核
MaxPool→ 6×6×256
FC64096 神经元 + Dropout
FC74096 神经元 + Dropout
FC81000 神经元(对应 ImageNet 1000 类)
Softmax输出分类概率

注:原始实现中,由于 GPU 显存限制,网络在第 1、2、4、5 卷积层和全连接层进行了“分组”(split across two GPUs)。

逐层解析

1. 输入层

AlexNet 的输入图像尺寸是 227×227×3

2. 卷积层(C1)

image-20260118073715563

该层的处理流程是:卷积-->ReLU-->局部响应归一化(LRN)-->池化。

  • 卷积:输入是 227x227x3,使用 96 个 11x11x3 的卷积核进行卷积,padding=0,stride=4,根据公式:

    • (input_size + 2 * padding - kernel_size) / stride + 1=(227+2*0-11)/4+1=55,得到输出是 55x55x96。
  • ReLU:将卷积层输出的 FeatureMap 输入到 ReLU 函数中。

  • 局部响应归一化:局部响应归一化层简称 LRN,是在深度学习中提高准确度的技术方法。

    • 一般是在激活、池化后进行。LRN 对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。
    • 局部响应归一化的输出仍然是 55x55x96。将其分成两组,每组大小是 55x55x48,分别位于单个 GPU 上。
  • 池化:使用 3x3,stride=2 的池化单元进行最大池化操作(max pooling)。

    • 注意这里使用的是重叠池化,即 stride 小于池化单元的边长。根据公式:(55+2*0-3)/2+1=27,每组得到的输出为 27x27x48。

3. 卷积层(C2)

image-20260118074251544

该层的处理流程是:卷积-->ReLU-->局部响应归一化(LRN)-->池化。

  • 卷积:两组输入均是 27x27x48,各组分别使用 128 个 5x5x48 的卷积核进行卷积,padding=2,stride=1,根据公式:

    • (input_size + 2 * padding - kernel_size) / stride + 1=(27+2*2-5)/1+1=27,得到每组输出是 27x27x128。
  • ReLU:将卷积层输出的 FeatureMap 输入到 ReLU 函数中。

  • 局部响应归一化:使用参数 k=2,n=5,α=0.0001,β=0.75 进行归一化。每组输出仍然是 27x27x128。

  • 池化:使用 3x3,stride=2 的池化单元进行最大池化操作(max pooling)。

    • 注意这里使用的是重叠池化,即 stride 小于池化单元的边长。根据公式:(27+2*0-3)/2+1=13,每组得到的输出为 13x13x128。

4. 卷积层(C3)

image-20260118074338540

该层的处理流程是:卷积-->ReLU

  • 卷积:输入是 13x13x256,使用 384 个 3x3x256 的卷积核进行卷积,padding=1,stride=1,根据公式:

    • (input_size + 2 * padding - kernel_size) / stride + 1=(13+2*1-3)/1+1=13,得到输出是 13x13x384。
  • ReLU:将卷积层输出的 FeatureMap 输入到 ReLU 函数中。将输出其分成两组,每组 FeatureMap 大小是 13x13x192,分别位于单个 GPU 上。

5. 卷积层(C4)

image-20260118074454470

该层的处理流程是:卷积-->ReLU

  • 卷积:两组输入均是 13x13x192,各组分别使用 192 个 3x3x192 的卷积核进行卷积,padding=1,stride=1,根据公式:

    • (input_size + 2 * padding - kernel_size) / stride + 1=(13+2*1-3)/1+1=13,得到每组 FeatureMap 输出是 13x13x192。
  • ReLU:将卷积层输出的 FeatureMap 输入到 ReLU 函数中。

6. 卷积层(C5)

image-20260118074544110

该层的处理流程是:卷积-->ReLU-->池化

  • 卷积:两组输入均是 13x13x192,各组分别使用 128 个 3x3x192 的卷积核进行卷积,padding=1,stride=1,根据公式:

    • (input_size + 2 * padding - kernel_size) / stride + 1=(13+2*1-3)/1+1=13,得到每组 FeatureMap 输出是 13x13x128。
  • ReLU:将卷积层输出的 FeatureMap 输入到 ReLU 函数中。

  • 池化:使用 3x3,stride=2 的池化单元进行最大池化操作(max pooling)。

    • 注意这里使用的是重叠池化,即 stride 小于池化单元的边长。根据公式:(13+2*0-3)/2+1=6,每组得到的输出为 6x6x128。

7. 全连接层(FC6)

image-20260118074612461

该层的流程为:(卷积)全连接 -->ReLU -->Dropout (卷积)

  • 全连接:输入为 6×6×256,使用 4096 个 6×6×256 的卷积核进行卷积,由于卷积核尺寸与输入的尺寸完全相同,即卷积核中的每个系数只与输入尺寸的一个像素值相乘一一对应,根据公式:

    • (input_size + 2 * padding - kernel_size) / stride + 1=(6+2*0-6)/1+1=1,得到输出是 1x1x4096。既有 4096 个神经元,该层被称为全连接层。
  • ReLU:这 4096 个神经元的运算结果通过 ReLU 激活函数中。

  • Dropout:随机的断开全连接层某些神经元的连接,通过不激活某些神经元的方式防止过拟合。4096 个神经元也被均分到两块 GPU 上进行运算。

8. 全连接层(FC7)

image-20260118074652227

该层的流程为:(卷积)全连接 -->ReLU -->Dropout

  • 全连接:输入为 4096 个神经元,输出也是 4096 个神经元(作者设定的)。

  • ReLU:这 4096 个神经元的运算结果通过 ReLU 激活函数中。

  • Dropout:随机的断开全连接层某些神经元的连接,通过不激活某些神经元的方式防止过拟合。

4096 个神经元也被均分到两块 GPU 上进行运算。

9. 输出层(Output layer)

image-20260118074737847

该层的流程为:(卷积)全连接 -->Softmax

  • 全连接:输入为 4096 个神经元,输出是 1000 个神经元。这 1000 个神经元即对应 1000 个检测类别。

  • Softmax:这 1000 个神经元的运算结果通过 Softmax 函数中,输出 1000 个类别对应的预测概率值。

实践代码

详情
<!-- #include-env-start: D:/code/klc/test/share4ai/docs/book/dive_into_on_dl/cnn/basic -->
```python
import torch
import torch.nn as nn
import torchvision
from torch.utils.data import DataLoader
import torchvision.transforms as transforms
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torch.autograd import Variable

# 定义AlexNet网络模型
class AlexNet(nn.Module):
    def __init__(self, config):
        super(AlexNet, self).__init__()
        self._config = config
        # 定义卷积层和池化层
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 192, kernel_size=5, stride=1, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(192, 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.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        # 自适应层,将上一层的数据转换成6x6大小
        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        # 全连接层
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 1024),
            nn.ReLU(inplace=True),
            nn.Linear(1024, self._config['num_classes']),
        )

    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x
# 定义模型保存与模型加载函数
def saveModel(self):
        torch.save(self.state_dict(), self._config['model_name'])

def loadModel(self, map_location):
        state_dict = torch.load(self._config['model_name'], map_location=map_location)
        self.load_state_dict(state_dict, strict=False)
# 数据集预处理
# 定义构造数据加载器的函数
def Construct_DataLoader(dataset, batchsize):
    return DataLoader(dataset=dataset, batch_size=batchsize, shuffle=True)
# 图像预处理
transform = transforms.Compose([
    transforms.Resize(96),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
# 加载CIFAR-10数据集函数
def LoadCIFAR10(download=False):
    # Load CIFAR-10 dataset
    train_dataset = torchvision.datasets.CIFAR10(root='./CIFAR10', train=True, transform=transform, download=download)
    test_dataset = torchvision.datasets.CIFAR10(root='./CIFAR10', train=False, transform=transform)
    return train_dataset, test_dataset
# 模型训练函数封装
class Trainer(object):
    # 初始化模型、配置参数、优化器和损失函数
    def __init__(self, model, config):
        self._model = model
        self._config = config
        self._optimizer = torch.optim.Adam(self._model.parameters(),\
                                           lr=config['lr'], weight_decay=config['l2_regularization'])
        self.loss_func = nn.CrossEntropyLoss()
    # 对单个小批量数据进行训练,包括前向传播、计算损失、反向传播和更新模型参数
    def _train_single_batch(self, images, labels):
        y_predict = self._model(images)

        loss = self.loss_func(y_predict, labels)
        # 先将梯度清零,如果不清零,那么这个梯度就和上一个mini-batch有关
        self._optimizer.zero_grad()
        # 反向传播计算梯度
        loss.backward()
        # 梯度下降等优化器 更新参数
        self._optimizer.step()
        # 将loss的值提取成python的float类型
        loss = loss.item()

        # 计算训练精确度
        # 这里的y_predict是一个多个分类输出,将dim指定为1,即返回每一个分类输出最大的值以及下标
        _, predicted = torch.max(y_predict.data, dim=1)
        return loss, predicted

    def _train_an_epoch(self, train_loader, epoch_id):
        """
        训练一个Epoch,即将训练集中的所有样本全部都过一遍
        """
        # 设置模型为训练模式,启用dropout以及batch normalization
        self._model.train()
        total = 0
        correct = 0

        # 从DataLoader中获取小批量的id以及数据
        for batch_id, (images, labels) in enumerate(train_loader):
            images = Variable(images)
            labels = Variable(labels)
            if self._config['use_cuda'] is True:
                images, labels = images.cuda(), labels.cuda()

            loss, predicted = self._train_single_batch(images, labels)

            # 计算训练精确度
            total += labels.size(0)
            correct += (predicted == labels.data).sum()

            # print('[Training Epoch: {}] Batch: {}, Loss: {}'.format(epoch_id, batch_id, loss))
        print('Training Epoch: {}, accuracy rate: {}%%'.format(epoch_id, correct / total * 100.0))

    def train(self, train_dataset):
        # 是否使用GPU加速
        self.use_cuda()
        for epoch in range(self._config['num_epoch']):
            print('-' * 20 + ' Epoch {} starts '.format(epoch) + '-' * 20)
            # 构造DataLoader
            data_loader = DataLoader(dataset=train_dataset, batch_size=self._config['batch_size'], shuffle=True)
            # 训练一个轮次
            self._train_an_epoch(data_loader, epoch_id=epoch)

    # 用于将模型和数据迁移到GPU上进行计算,如果CUDA不可用则会抛出异常
    def use_cuda(self):
        if self._config['use_cuda'] is True:
            assert torch.cuda.is_available(), 'CUDA is not available'
            torch.cuda.set_device(self._config['device_id'])
            self._model.cuda()

    # 保存训练好的模型
    def save(self):
        self._model.saveModel()
train_dataset, test_dataset = LoadCIFAR10(True)
最近更新: 2026/3/11 20:33
Contributors: klc407073648
Prev
LeNet
Next
VGG