找回密码
 立即注册
首页 业界区 业界 pytorch入门 - AlexNet神经网络

pytorch入门 - AlexNet神经网络

坏级尹 2025-9-24 17:45:28
AlexNet背景

AlexNet是2012年由Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton提出的深度卷积神经网络架构。
它在ImageNet大规模视觉识别挑战赛(ILSVRC)中取得了突破性成绩,将top-5错误率从26%降低到了15.3%,这一成就标志着深度学习在计算机视觉领域的崛起。
AlexNet的成功主要归功于以下几个创新点:

  • 使用ReLU(Rectified Linear Unit)作为激活函数,解决了传统Sigmoid/Tanh激活函数在深层网络中的梯度消失问题
  • 采用Dropout技术减少全连接层的过拟合
  • 使用重叠的最大池化(max pooling)代替传统平均池化,提升了特征不变性
  • 首次在CNN中成功应用GPU加速训练,使得训练大规模深层网络成为可能
AlexNet的出现开启了深度学习在计算机视觉领域的新纪元,为后续各种CNN架构(如VGG、ResNet等)的发展奠定了基础。
AlexNet架构

1.png

 
AlexNet原始架构包含8个学习层 - 5个卷积层和3个全连接层。下面是详细的架构描述:

  • ​输入层​​:接受224×224×3的RGB图像(在FashionMNIST中调整为227×227×1的灰度图像)
  • ​卷积层1​​:96个11×11的卷积核,步长4,使用ReLU激活
  • ​最大池化层1​​:3×3池化窗口,步长2
  • ​卷积层2​​:256个5×5的卷积核,padding=2,使用ReLU激活
  • ​最大池化层2​​:3×3池化窗口,步长2
  • ​卷积层3​​:384个3×3的卷积核,padding=1,使用ReLU激活
  • ​卷积层4​​:384个3×3的卷积核,padding=1,使用ReLU激活
  • ​卷积层5​​:256个3×3的卷积核,padding=1,使用ReLU激活
  • ​最大池化层3​​:3×3池化窗口,步长2
  • ​全连接层1​​:4096个神经元,使用ReLU激活,Dropout=0.5
  • ​全连接层2​​:4096个神经元,使用ReLU激活,Dropout=0.5
  • ​全连接层3(输出层)​​:1000个神经元(在FashionMNIST中调整为10个)
参数计算详解

让我们详细计算AlexNet每一层的参数数量:

  • ​卷积层1​​:

    • 输入:227×227×1
    • 96个11×11卷积核
    • 参数数量 = (11×11×1 + 1偏置)×96 = 11,712

  • ​卷积层2​​:

    • 输入:27×27×96 (经过池化后尺寸)
    • 256个5×5卷积核
    • 参数数量 = (5×5×96 + 1)×256 = 614,656

  • ​卷积层3​​:

    • 输入:13×13×256
    • 384个3×3卷积核
    • 参数数量 = (3×3×256 + 1)×384 = 885,120

  • ​卷积层4​​:

    • 输入:13×13×384
    • 384个3×3卷积核
    • 参数数量 = (3×3×384 + 1)×384 = 1,327,488

  • ​卷积层5​​:

    • 输入:13×13×384
    • 256个3×3卷积核
    • 参数数量 = (3×3×384 + 1)×256 = 884,992

  • ​全连接层1​​:

    • 输入:6×6×256 = 9216
    • 输出:4096
    • 参数数量 = (9216 + 1)×4096 = 37,752,832

  • ​全连接层2​​:

    • 输入:4096
    • 输出:4096
    • 参数数量 = (4096 + 1)×4096 = 16,781,312

  • ​全连接层3​​:

    • 输入:4096
    • 输出:10(FashionMNIST)
    • 参数数量 = (4096 + 1)×10 = 40,970

总参数数量约为6000万(原始AlexNet),在FashionMNIST上约为5800万。
代码实现解析

模型实现代码(model.py)
  1. import os
  2. import sys
  3. sys.path.append(os.getcwd())
  4. import torch  # 导入PyTorch主库
  5. from torch import nn  # 从torch中导入神经网络模块
  6. from torchsummary import summary  # 导入torchsummary用于模型结构总结
  7. import torch.nn.functional as F  # 导入PyTorch的函数式API,常用于激活函数、dropout等
  8. class AlexNet(nn.Module):  # 定义AlexNet模型,继承自nn.Module
  9.     def __init__(self):  # 构造函数,初始化网络结构
  10.         super(AlexNet, self).__init__()  # 调用父类的构造函数
  11.         self.ReLU = nn.ReLU()  # 定义ReLU激活函数,后续多次复用
  12.         self.conv1 = nn.Conv2d(
  13.             in_channels=1, out_channels=96, stride=4, kernel_size=11
  14.         )  # 第一层卷积,输入通道1,输出通道96,步幅4,卷积核11x11
  15.         self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2)  # 第一层池化,3x3窗口,步幅2
  16.         self.conv2 = nn.Conv2d(
  17.             in_channels=96, out_channels=256, stride=1, kernel_size=5, padding=2
  18.         )  # 第二层卷积,输入96通道,输出256通道,5x5卷积核,padding=2
  19.         self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2)  # 第二层池化,3x3窗口,步幅2
  20.         self.conv3 = nn.Conv2d(
  21.             in_channels=256, out_channels=384, stride=1, kernel_size=3, padding=1
  22.         )  # 第三层卷积,输入256通道,输出384通道,3x3卷积核,padding=1
  23.         self.conv4 = nn.Conv2d(
  24.             in_channels=384, out_channels=384, stride=1, kernel_size=3, padding=1
  25.         )  # 第四层卷积,输入384通道,输出384通道,3x3卷积核,padding=1
  26.         self.conv5 = nn.Conv2d(
  27.             in_channels=384, out_channels=256, stride=1, kernel_size=3, padding=1
  28.         )  # 第五层卷积,输入384通道,输出256通道,3x3卷积核,padding=1
  29.         self.pool3 = nn.MaxPool2d(kernel_size=3, stride=2)  # 第三层池化,3x3窗口,步幅2
  30.         self.flatten = nn.Flatten()  # 展平层,将多维输入展平成一维
  31.         self.fc1 = nn.Linear(
  32.             in_features=256 * 6 * 6, out_features=4096
  33.         )  # 第一个全连接层,输入256 * 6 * 6,输出4096
  34.         self.fc2 = nn.Linear(
  35.             in_features=4096, out_features=4096
  36.         )  # 第二个全连接层,输入4096,输出4096
  37.         self.fc3 = nn.Linear(
  38.             in_features=4096, out_features=10
  39.         )  # 第三个全连接层,输入4096,输出10(假设10分类)
  40.     def forward(self, x):  # 定义前向传播过程
  41.         x = self.conv1(x)  # 输入通过第一层卷积
  42.         x = self.ReLU(x)  # 激活
  43.         x = self.pool1(x)  # 池化
  44.         x = self.conv2(x)  # 第二层卷积
  45.         x = self.ReLU(x)  # 激活
  46.         x = self.pool2(x)  # 池化
  47.         x = self.conv3(x)  # 第三层卷积
  48.         x = self.ReLU(x)  # 激活
  49.         x = self.conv4(x)  # 第四层卷积
  50.         x = self.ReLU(x)  # 激活
  51.         x = self.conv5(x)  # 第五层卷积
  52.         x = self.ReLU(x)  # 激活
  53.         x = self.pool3(x)  # 池化
  54.         x = self.flatten(x)  # 展平为一维向量
  55.         x = self.fc1(x)  # 第一个全连接层
  56.         x = self.ReLU(x)  # 激活
  57.         x = F.dropout(x, p=0.5)  # dropout防止过拟合,丢弃概率0.5
  58.         x = self.fc2(x)  # 第二个全连接层
  59.         x = self.ReLU(x)  # 激活
  60.         x = F.dropout(x, p=0.5)  # dropout防止过拟合,丢弃概率0.5
  61.         x = self.fc3(x)  # 第三个全连接层,输出最终结果
  62.         return x  # 返回输出
  63. if __name__ == "__main__":  # 如果作为主程序运行
  64.     model = AlexNet()  # 实例化AlexNet模型
  65.     print(model)  # 打印模型结构
  66.     summary(
  67.         model, input_size=(1, 227, 227), device="cpu"
  68.     )  # 打印模型摘要,输入尺寸为(1, 227, 227),单通道
复制代码
训练代码(train.py)
  1. import os  # 导入os模块,用于与操作系统交互
  2. import sys  # 导入sys模块,用于操作Python运行时环境
  3. sys.path.append(os.getcwd())  # 将当前工作目录添加到sys.path,方便模块导入
  4. import time  # 导入time模块,用于计时
  5. from torchvision.datasets import FashionMNIST  # 导入FashionMNIST数据集
  6. from torchvision import transforms  # 导入transforms用于数据预处理
  7. from torch.utils.data import (
  8.     DataLoader,  # 导入DataLoader用于批量加载数据
  9.     random_split,  # 导入random_split用于划分数据集
  10. )
  11. import numpy as np  # 导入numpy用于数值计算
  12. import matplotlib.pyplot as plt  # 导入matplotlib用于绘图
  13. import torch  # 导入PyTorch主库
  14. from torch import nn, optim  # 导入神经网络模块和优化器
  15. import copy  # 导入copy模块用于深拷贝
  16. import pandas as pd  # 导入pandas用于数据处理
  17. from AlexNet_model.model import AlexNet  # 从自定义模块导入AlexNet模型
  18. def train_val_date_load():  # 定义函数用于加载训练集和验证集
  19.     train_dataset = FashionMNIST(
  20.         root="./data",  # 数据存储路径
  21.         train=True,  # 加载训练集
  22.         download=True,  # 如果数据不存在则下载
  23.         transform=transforms.Compose(
  24.             [
  25.                 transforms.Resize(size=227),  # 将图片缩放到227x227
  26.                 transforms.ToTensor(),  # 转换为Tensor
  27.             ]
  28.         ),
  29.     )
  30.     train_date, val_data = random_split(
  31.         train_dataset,
  32.         [
  33.             int(len(train_dataset) * 0.8),  # 80%作为训练集
  34.             len(train_dataset) - int(len(train_dataset) * 0.8),  # 剩余20%作为验证集
  35.         ],
  36.     )
  37.     train_loader = DataLoader(
  38.         dataset=train_date,
  39.         batch_size=32,
  40.         shuffle=True,
  41.         num_workers=1,  # 训练集加载器,批量32,打乱顺序
  42.     )
  43.     val_loader = DataLoader(
  44.         dataset=val_data,
  45.         batch_size=32,
  46.         shuffle=True,
  47.         num_workers=1,  # 验证集加载器,批量32,打乱顺序
  48.     )
  49.     return train_loader, val_loader  # 返回训练集和验证集加载器
  50. def train_model_process(model, train_loader, val_loader, epochs=10):  # 定义训练过程函数
  51.     device = "cuda" if torch.cuda.is_available() else "cpu"  # 判断是否有GPU可用
  52.     optimizer = optim.Adam(model.parameters(), lr=0.001)  # 使用Adam优化器,学习率0.001
  53.     criterion = nn.CrossEntropyLoss()  # 定义交叉熵损失函数
  54.     model.to(device)  # 将模型移动到指定设备
  55.     best_model_wts = copy.deepcopy(model.state_dict())  # 保存最佳模型参数
  56.     best_acc = 0.0  # 初始化最佳准确率
  57.     train_loss_all = []  # 记录每轮训练损失
  58.     val_loss_all = []  # 记录每轮验证损失
  59.     train_acc_all = []  # 记录每轮训练准确率
  60.     val_acc_all = []  # 记录每轮验证准确率
  61.     since = time.time()  # 记录训练开始时间
  62.     for epoch in range(epochs):  # 遍历每个训练轮次
  63.         print(f"Epoch {epoch + 1}/{epochs}")  # 打印当前轮次信息
  64.         train_loss = 0.0  # 当前轮训练损失
  65.         train_correct = 0  # 当前轮训练正确样本数
  66.         val_loss = 0.0  # 当前轮验证损失
  67.         val_correct = 0  # 当前轮验证正确样本数
  68.         train_num = 0  # 当前轮训练样本总数
  69.         val_num = 0  # 当前轮验证样本总数
  70.         for step, (images, labels) in enumerate(train_loader):  # 遍历训练集
  71.             images = images.to(device)  # 将图片移动到设备
  72.             labels = labels.to(device)  # 将标签移动到设备
  73.             model.train()  # 设置模型为训练模式
  74.             outputs = model(images)  # 前向传播,获取输出
  75.             pre_lab = torch.argmax(outputs, dim=1)  # 获取预测标签
  76.             loss = criterion(outputs, labels)  # 计算损失
  77.             optimizer.zero_grad()  # 梯度清零
  78.             loss.backward()  # 反向传播
  79.             optimizer.step()  # 更新参数
  80.             train_loss += loss.item() * images.size(0)  # 累加损失
  81.             train_correct += torch.sum(pre_lab == labels.data)  # 累加正确预测数
  82.             train_num += labels.size(0)  # 累加样本数
  83.         for step, (images, labels) in enumerate(val_loader):  # 遍历验证集
  84.             images = images.to(device)  # 将图片移动到设备
  85.             labels = labels.to(device)  # 将标签移动到设备
  86.             model.eval()  # 设置模型为评估模式
  87.             with torch.no_grad():  # 关闭梯度计算
  88.                 outputs = model(images)  # 前向传播
  89.                 pre_lab = torch.argmax(outputs, dim=1)  # 获取预测标签
  90.                 loss = criterion(outputs, labels)  # 计算损失
  91.                 val_loss += loss.item() * images.size(0)  # 累加损失
  92.                 val_correct += torch.sum(pre_lab == labels.data)  # 累加正确预测数
  93.                 val_num += labels.size(0)  # 累加样本数
  94.         train_loss_all.append(train_loss / train_num)  # 记录本轮平均训练损失
  95.         val_loss_all.append(val_loss / val_num)  # 记录本轮平均验证损失
  96.         train_acc = train_correct.double() / train_num  # 计算本轮训练准确率
  97.         val_acc = val_correct.double() / val_num  # 计算本轮验证准确率
  98.         train_acc_all.append(train_acc.item())  # 记录训练准确率
  99.         val_acc_all.append(val_acc.item())  # 记录验证准确率
  100.         print(
  101.             f"Train Loss: {train_loss / train_num:.4f}, Train Acc: {train_acc:.4f}, "
  102.             f"Val Loss: {val_loss / val_num:.4f}, Val Acc: {val_acc:.4f}"
  103.         )  # 打印本轮损失和准确率
  104.         if val_acc_all[-1] > best_acc:  # 如果本轮验证准确率更高
  105.             best_acc = val_acc_all[-1]  # 更新最佳准确率
  106.             best_model_wts = copy.deepcopy(model.state_dict())  # 保存最佳模型参数
  107.         # model.load_state_dict(best_model_wts)  # 可选:恢复最佳模型参数
  108.     time_elapsed = time.time() - since  # 计算训练总耗时
  109.     print(
  110.         f"Training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s\n"
  111.         f"Best val Acc: {best_acc:.4f}"
  112.     )  # 打印训练耗时和最佳准确率
  113.     torch.save(model.state_dict(), "./models/alex_net_best_model.pth")  # 保存模型参数
  114.     train_process = pd.DataFrame(
  115.         data={
  116.             "epoch": range(1, epochs + 1),  # 轮次
  117.             "train_loss_all": train_loss_all,  # 训练损失
  118.             "val_loss_all": val_loss_all,  # 验证损失
  119.             "train_acc_all": train_acc_all,  # 训练准确率
  120.             "val_acc_all": val_acc_all,  # 验证准确率
  121.         }
  122.     )  # 构建训练过程数据表
  123.     return train_process  # 返回训练过程数据
  124. def matplot_acc_loss(train_process):  # 定义绘图函数
  125.     plt.figure(figsize=(12, 5))  # 创建画布,设置大小
  126.     plt.subplot(1, 2, 1)  # 激活第1个子图
  127.     plt.plot(
  128.         train_process["epoch"], train_process["train_loss_all"], label="Train Loss"
  129.     )  # 绘制训练损失曲线
  130.     plt.plot(
  131.         train_process["epoch"], train_process["val_loss_all"], label="Val Loss"
  132.     )  # 绘制验证损失曲线
  133.     plt.xlabel("Epoch")  # 设置x轴标签
  134.     plt.ylabel("Loss")  # 设置y轴标签
  135.     plt.title("Loss vs Epoch")  # 设置子图标题
  136.     plt.legend()  # 显示图例
  137.     plt.subplot(1, 2, 2)  # 激活第2个子图
  138.     plt.plot(
  139.         train_process["epoch"], train_process["train_acc_all"], label="Train Acc"
  140.     )  # 绘制训练准确率曲线
  141.     plt.plot(
  142.         train_process["epoch"], train_process["val_acc_all"], label="Val Acc"
  143.     )  # 绘制验证准确率曲线
  144.     plt.xlabel("Epoch")  # 设置x轴标签为Epoch
  145.     plt.ylabel("Accuracy")  # 设置y轴标签为Accuracy
  146.     plt.title("Accuracy vs Epoch")  # 设置子图标题
  147.     plt.legend()  # 显示图例
  148.     plt.tight_layout()  # 自动调整子图间距
  149.     plt.ion()  # 关闭交互模式,防止图像自动关闭
  150.     plt.show()  # 显示所有图像
  151.     plt.savefig("./models/alex_net_output.png")  # 保存图片到指定路径
  152. if __name__ == "__main__":  # 如果当前脚本作为主程序运行
  153.     traindatam, valdata = train_val_date_load()  # 加载训练集和验证集
  154.     result = train_model_process(
  155.         AlexNet(), traindatam, valdata, 10
  156.     )  # 训练模型并获取训练过程数据
  157.     matplot_acc_loss(result)  # 绘制训练和验证的损失及准确率曲线
复制代码
测试代码(test.py)
  1. import os  # 导入os模块,用于与操作系统交互
  2. import sys  # 导入sys模块,用于操作Python运行时环境
  3. sys.path.append(os.getcwd())  # 将当前工作目录添加到sys.path,方便模块导入
  4. import torch  # 导入PyTorch主库
  5. from torch.utils.data import (
  6.     DataLoader,  # 导入DataLoader用于批量加载数据
  7.     random_split,  # 导入random_split用于划分数据集(本文件未用到)
  8. )
  9. from torchvision import datasets, transforms  # 导入torchvision的数据集和数据变换模块
  10. from torchvision.datasets import FashionMNIST  # 导入FashionMNIST数据集
  11. from AlexNet_model.model import AlexNet  # 从自定义模块导入AlexNet模型
  12. def test_data_load():  # 定义测试数据加载函数
  13.     test_dataset = FashionMNIST(
  14.         root="./data",  # 数据存储路径
  15.         train=False,  # 加载测试集
  16.         download=True,  # 如果数据不存在则下载
  17.         transform=transforms.Compose(
  18.             [
  19.                 transforms.Resize(size=227),  # 将图片缩放到227x227
  20.                 transforms.ToTensor(),  # 转换为Tensor
  21.             ]
  22.         ),
  23.     )
  24.     test_loader = DataLoader(
  25.         dataset=test_dataset, batch_size=128, shuffle=True, num_workers=1  # 测试集加载器,批量128,打乱顺序
  26.     )
  27.     return test_loader  # 返回测试集加载器
  28. print(test_data_load())  # 打印测试集加载器(调试用)
  29. def test_model_process(model, test_loader):  # 定义模型测试过程
  30.     device = "cuda" if torch.cuda.is_available() else "cpu"  # 判断是否有GPU可用
  31.     model.to(device)  # 将模型移动到指定设备
  32.     model.eval()  # 设置模型为评估模式
  33.     correct = 0  # 正确预测样本数
  34.     total = 0  # 总样本数
  35.     with torch.no_grad():  # 关闭梯度计算,加快推理速度
  36.         for images, labels in test_loader:  # 遍历测试集
  37.             images, labels = images.to(device), labels.to(device)  # 数据移动到设备
  38.             outputs = model(images)  # 前向传播,获取输出
  39.             _, predicted = torch.max(outputs, 1)  # 获取预测标签
  40.             total += labels.size(0)  # 累加总样本数
  41.             correct += torch.sum(predicted == labels.data)  # 累加正确预测数
  42.     accuracy = correct / total * 100  # 计算准确率(百分比)
  43.     print(f"Test Accuracy: {accuracy:.2f}%")  # 打印测试准确率
  44. if __name__ == "__main__":  # 如果当前脚本作为主程序运行
  45.     test_loader = test_data_load()  # 加载测试集
  46.     model = AlexNet()  # 实例化AlexNet模型
  47.     model.load_state_dict(torch.load("./models/alex_net_best_model.pth"))  # 加载训练好的模型参数
  48.     test_model_process(model, test_loader)  # 测试模型并输出准确率
复制代码
总结

AlexNet作为深度学习在计算机视觉领域的里程碑式模型,具有以下重要特点和贡献:

  • ​架构创新​​:首次证明了深层卷积神经网络在大规模图像识别任务中的有效性
  • ​技术突破​​:引入了ReLU激活函数、Dropout、局部响应归一化等技术
  • ​硬件加速​​:开创性地使用GPU加速CNN训练,大幅缩短训练时间
  • ​开源影响​​:AlexNet的成功推动了深度学习开源框架的发展
尽管现在有更先进的CNN架构,AlexNet仍然是学习深度学习计算机视觉的经典案例。
通过本文的代码实现,我们可以在FashionMNIST数据集上复现AlexNet的基本架构,并理解其工作原理。
在实际应用中,AlexNet的一些设计已经过时,如大卷积核(11×11)被小卷积核(3×3)堆叠取代,全连接层被全局平均池化替代等。但理解AlexNet仍然是掌握现代CNN架构的重要基础。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册