【深度学习】:《PyTorch入门到项目实战》(十五):三大经典卷积神经网络架构:LeNet、AlexNet、VGG

news2025/2/4 18:54:25

【深度学习】:《PyTorch入门到项目实战》(十五):三大经典卷积神经网络架构:LeNet、AlexNet、VGG(代码实现及实际案例比较)

  • ✨本文收录于【深度学习】:《PyTorch入门到项目实战》专栏,此专栏主要记录如何使用PyTorch实现深度学习及其项目实战,尽量坚持每周持续更新,欢迎大家订阅!
  • 🌸个人主页:JOJO数据科学
  • 📝个人介绍:某985统计硕士在读
  • 💌如果文章对你有帮助,欢迎✌关注、👍点赞、✌收藏、👍订阅专栏
  • 参考资料:《动手学深度学习》

在这里插入图片描述

文章目录

  • 【深度学习】:《PyTorch入门到项目实战》(十五):三大经典卷积神经网络架构:LeNet、AlexNet、VGG(代码实现及实际案例比较)
  • 三大经典卷积神经网络架构:LeNet、AlexNet、VGG(代码实现及实际案例比较)
  • 1.Lenet
    • 1.1理论介绍
    • Fashion—MNIST数据集
    • 1.2代码实现
  • 2. AlexNet
    • 2.1 理论介绍
    • 2.2 代码实现
  • 3.VGG
    • 3.1 理论介绍
    • 3.2代码实现
    • 总结

三大经典卷积神经网络架构:LeNet、AlexNet、VGG(代码实现及实际案例比较)

1.Lenet

1.1理论介绍

经过前面的介绍,我们已经了解了卷积神经网络的基本模块,接下来我们来讨论几个经典的神经网络结构,首先介绍LeNet-5。LeNet是最早的卷积神经网络之一,其被提出用于识别手写数字和机器印刷字符。1998年,Yann LeCun第一次将LeNet卷积神经网络应用到图像分类上,在手写数字识别任务中取得了巨大成功。首先看看LeNet-5的网络结构,下图是原论文放出来的架构

image-20230711160836404

假设你有一张32×32×1的图片,LeNet-5可以识别图中的手写数字。由于LeNet-5是针对灰度图片训练的,所以图片的大小只有32×32×1。

LeNet的结构如下:

  • 输入层: 接收输入图像的尺寸为32x32x1。
  • 卷积层部分:
    • 卷积层1:6×5x5的卷积核,步长为1,填充为0,使用Sigmoid激活函数。
    • 平均池化层1:2x2的池化窗口,步长为2。
    • 卷积层2:16×5x5的卷积核,步长为1,填充为0,使用Sigmoid激活函数。
    • 平均池化层2:2x2的池化窗口,步长为2。
  • 全连接层部分
    • 全连接层1:120个神经元,使用Sigmoid激活函数。
    • 全连接层2:84个神经元,使用Sigmoid激活函数。
    • 全连接层3(输出层):10个神经元,对应10个手写数字类别,现在往往用softmax。

总的来说,如果我们从左往右看,随着网络越来越深,图像的高度和宽度在缩小,从最初的32×32缩小到28×28,再到14×1410×10,最后只有5×5。与此同时,随着网络层次的加深,通道数量一直在增加,从1增加到6个,再到16个。

这个神经网络中还有一种模式至今仍然经常用到,就是一个或多个卷积层后面跟着一个池化层,然后又是若干个卷积层再接一个池化层,然后是全连接层,最后是输出,这种排列方式很常用。

Fashion—MNIST数据集

原始的LeNet是在MNIST数据集上实现的,但是MNIST数据集在今天来说实在太简单了,我们使用一个稍微复杂一点的数据集Fashion-MNIST,为了方便我们后续比较几个模型的性能。

Fashion-MNIST数据集由Zalando Research创建,并且与经典的MNIST数据集具有相似的结构。它包含了来自10个不同类别的共计70000张灰度图像,每个类别包含7000张图像。这些类别分别是:T恤、裤子、套头衫、连衣裙、外套、凉鞋、衬衫、运动鞋、包和短靴。

每张图像的尺寸为28x28像素,并以灰度形式表示,像素值范围在0到255之间。Fashion-MNIST数据集已经被标记,因此每个图像都与其对应的类别标签相关联。这使得Fashion-MNIST成为评估机器学习模型在图像分类任务上表现的理想数据集。

Fashion-MNIST的目标是提供一个更具挑战性的数据集,用于测试和比较不同算法的性能。与MNIST数据集相比,Fashion-MNIST涵盖更复杂、多样化的图像内容,更能反映现实世界中的图像分类问题。

我们来简单的看一下数据集,我们可以利用torchvision来下载

import torch
import torchvision
import matplotlib.pyplot as plt

# 加载Fashion-MNIST数据集
train_set = torchvision.datasets.FashionMNIST(root='./data', train=True, download=True)
test_set = torchvision.datasets.FashionMNIST(root='./data', train=False, download=True)

# 查看数据集大小
print(f"训练集大小: {len(train_set)}")
print(f"测试集大小: {len(test_set)}")

# 获取类别标签
labels = train_set.classes
print(f"类别标签: {labels}")

# 随机显示几个样本图像
fig, axes = plt.subplots(2, 5, figsize=(10, 4))
for i, ax in enumerate(axes.flat):
    image, label = train_set[i]
    ax.imshow(image, cmap='gray')
    ax.set_title(labels[label])
    ax.axis('off')
plt.show()

image-20230716225012282

可以看到上面10张示例图,相对于手写数字识别(MNIST)数据集而言,更复杂一些,下面我们正式使用LeNet来对Fashion-MNIST数据集进行识别。

1.2代码实现

1.导入相关库

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

2.定义LeNet框架

# 定义 LeNet 模型
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, kernel_size=5,padding=2)
        self.avgpool = nn.AvgPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(6, 16, kernel_size=5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        out = self.avgpool(torch.relu(self.conv1(x)))
        out = self.avgpool(torch.relu(self.conv2(out)))
        out = out.view(out.size(0), -1)
        out = torch.sigmoid(self.fc1(out))
        out = torch.sigmoid(self.fc2(out))
        out = self.fc3(out)
        return out

注意,在整个卷积块中,与上一层相比,每一层特征的高度和宽度都减小了,因此高度和宽度都减少了4个像素。 随着层叠的上升,通道的数量从输入时的1个,增加到第一个卷积层之后的6个,再到第二个卷积层之后的16个。 同时,每个池化层层的高度和宽度都减半。最后,每个全连接层减少维数,最终输出一个维数与结果分类数相匹配的输出。

设置gpu

# 设置gpu
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

3.导入Fashion-MINIST数据集

# 加载 Fashion-MNIST 数据集
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])
trainset = torchvision.datasets.FashionMNIST(root='./data', train=True,
                                            download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64,
                                          shuffle=True, num_workers=2)
testset = torchvision.datasets.FashionMNIST(root='./data', train=False,
                                           download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64,
                                         shuffle=False, num_workers=2)

4.初始化模型

# 初始化模型、损失函数和优化器
model = LeNet().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.9)

这里我们使用交叉熵损失函数和小批量梯度下降(SGD)

5.模型训练和评估

num_epochs = 10
train_losses = []
test_losses = []

for epoch in range(num_epochs):
    train_loss = 0.0
    test_loss = 0.0
    correct = 0
    total = 0

    # 训练模型
    model.train()
    for images, labels in trainloader:
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()

    # 测试模型
    model.eval()
    with torch.no_grad():
        for images, labels in testloader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            loss = criterion(outputs, labels)
            test_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    avg_train_loss = train_loss / len(trainloader)
    avg_test_loss = test_loss / len(testloader)
    train_losses.append(avg_train_loss)
    test_losses.append(avg_test_loss)

    print(f"Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, Test Loss: {avg_test_loss:.4f}, Acc: {correct/total*100:.2f}%")

# 绘制测试误差和训练误差曲线
plt.plot(train_losses, label='Training Loss')
plt.plot(test_losses, label='Testing Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
Epoch [1/10], Train Loss: 2.2963, Test Loss: 2.2134, Acc: 30.00%
Epoch [2/10], Train Loss: 0.9418, Test Loss: 0.6950, Acc: 75.43%
Epoch [3/10], Train Loss: 0.5754, Test Loss: 0.5239, Acc: 80.05%
Epoch [4/10], Train Loss: 0.4852, Test Loss: 0.4512, Acc: 83.23%
Epoch [5/10], Train Loss: 0.4302, Test Loss: 0.4255, Acc: 84.22%
Epoch [6/10], Train Loss: 0.3905, Test Loss: 0.3730, Acc: 85.98%
Epoch [7/10], Train Loss: 0.3644, Test Loss: 0.3640, Acc: 86.68%
Epoch [8/10], Train Loss: 0.3424, Test Loss: 0.3370, Acc: 87.41%
Epoch [9/10], Train Loss: 0.3253, Test Loss: 0.3261, Acc: 87.83%
Epoch [10/10], Train Loss: 0.3107, Test Loss: 0.3042, Acc: 88.74%

image-20230716225525277
可以看出经过10个epoch的训练后,测试精度达到88.74%,后续我们比较其与其他两个模型的差距

2. AlexNet

2.1 理论介绍

AlexNet,是以论文的第一作者Alex Krizhevsky的名字命名的,另外两位合著者是ilya SutskeverGeoffery Hinton。AlexNet在2012年在ImageNet图像分类挑战赛上取得了突破性的成果,其本质上和LeNet没有区别,可以看做是一个更深的LeNet,拥有更多的参数。AlexNet首先用一张227×227×3图像,论文中实际用的是224×224×3,实践中往往227×227×3更方便,我们来看一下ALexNet的基本框架

image-20230717002828572

  • 输入层: 接收输入图像的尺寸为227x227x3。
  • 卷积层部分:
    • 卷积层1: 96个11x11的卷积核,步长为4,填充为0,ReLU激活函数。
    • 最大池化层1: 3x3的池化窗口,步长为2。
    • 卷积层2: 256个5x5的卷积核,步长为1,填充为2,ReLU激活函数。
    • 最大池化层2: 3x3的池化窗口,步长为2。
    • 卷积层3: 384个3x3的卷积核,步长为1,填充为1,ReLU激活函数。
    • 卷积层4: 384个3x3的卷积核,步长为1,填充为1,ReLU激活函数。
    • 卷积层5: 256个3x3的卷积核,步长为1,填充为1,ReLU激活函数。
    • 最大池化层3:3x3的池化窗口,步长为2。
  • 全连接层部分:
    • 全连接层1: 4096个神经元,ReLU激活函数。
    • Dropout层1: 以0.5的概率随机将输入置为0。
    • 全连接层2: 4096个神经元,ReLU激活函数。
    • Dropout层2: 以0.5的概率随机将输入置为0。
    • 全连接层3(输出层): 1000个神经元,对应ImageNet的1000个类别。

实际上,AlexNet神经网络与LeNet有很多相似之处,不过AlexNet要大得多。正如前面讲到的LeNet,其大约有6万个参数,而AlexNet模型总共有5个卷积层,3个池化层和3个全连接层,参数量较大,约6000万个参数。同时,通过大输入图像尺寸和大尺寸的卷积核,使得网络能够更好地捕捉图像中的细节信息。此外,AlexNetLeNet表现更为出色的另一个原因是它使用了ReLu激活函数,以及在池化层中使用了maxpooling。于此同时,AlexNet引入了深度学习中的一些重要概念和技术,如使用ReLU激活函数、局部响应归一化(LRN)和Dropout正则化等。

2.2 代码实现

原文中AlexNet是在ImageNet上进行训练的,但是这里为了方便比较,以及节约训练时间,我们依旧在Fashion—MNIST数据集上进行训练。

1.定义AlexNet模型

#定义AlexNet
class AlexNet(nn.Module):
    def __init__(self, num_classes=10):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 96, kernel_size=11, stride=4),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(96, 256, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(256, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2)
        )
        self.classifier = nn.Sequential(
            nn.Dropout(p=0.5),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5),
            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 * 6 * 6)
        x = self.classifier(x)
        return x

2.加载数据集

# 加载 Fashion-MNIST 数据集
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize((227,227)),#将原始图像扩宽到227×227
    transforms.Normalize((0.5,), (0.5,))
])
trainset = torchvision.datasets.FashionMNIST(root='./data', train=True,
                                            download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(trainset, batch_size=64,
                                          shuffle=True, num_workers=2)
testset = torchvision.datasets.FashionMNIST(root='./data', train=False,
                                           download=True, transform=transform)
test_loader = torch.utils.data.DataLoader(testset, batch_size=64,
                                         shuffle=False, num_workers=2)

3.初始化AlexNet模型

# 初始化AlexNet模型
model = AlexNet().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

由于Fashion-MNINST图像默认是28×28的,我们需要使用transforms.Resize将其增加到227×227,在实际中我们一般不会这样做。

4.模型训练和评估

# 训练AlexNet模型
num_epochs = 10
train_losses = []
test_losses = []

for epoch in range(num_epochs):
    train_loss = 0.0
    test_loss = 0.0
    correct = 0
    total = 0

    # 训练模型
    model.train()
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()

    # 测试模型
    model.eval()
    with torch.no_grad():
        for images, labels in test_loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            loss = criterion(outputs, labels)
            test_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    avg_train_loss = train_loss / len(train_loader)
    avg_test_loss = test_loss / len(test_loader)
    train_losses.append(avg_train_loss)
    test_losses.append(avg_test_loss)

    print(f"Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, Test Loss: {avg_test_loss:.4f}, Acc: {correct/total*100:.2f}%")

# 绘制测试误差和训练误差曲线
plt.plot(train_losses, label='Training Loss')
plt.plot(test_losses, label='Testing Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()

Epoch [1/10], Train Loss: 0.8024, Test Loss: 1.3865, Acc: 55.48%
Epoch [2/10], Train Loss: 0.4890, Test Loss: 0.4372, Acc: 83.66%
Epoch [3/10], Train Loss: 0.3692, Test Loss: 0.3832, Acc: 85.42%
Epoch [4/10], Train Loss: 0.3307, Test Loss: 0.3728, Acc: 86.03%
Epoch [5/10], Train Loss: 0.3030, Test Loss: 0.3281, Acc: 87.72%
Epoch [6/10], Train Loss: 0.2829, Test Loss: 0.3285, Acc: 87.78%
Epoch [7/10], Train Loss: 0.2697, Test Loss: 0.3515, Acc: 87.47%
Epoch [8/10], Train Loss: 0.2560, Test Loss: 0.3193, Acc: 88.24%
Epoch [9/10], Train Loss: 0.2466, Test Loss: 0.3005, Acc: 89.02%
Epoch [10/10], Train Loss: 0.2373, Test Loss: 0.3068, Acc: 89.00%

image-20230716225921965

可以看出AlexNet在Fashion-MNIST数据集上测试精度有所提升,突破了89%,这是因为AlexNet使用了更深更大的网络。

3.VGG

3.1 理论介绍

经过Lenet和AlexNet的介绍,我们可以发现使用更深更大的神经网络,能够带来更好的效果,这也是目前深度学习领域一直在做的事情,包括现在热门gpt4。但是AlexNet有一个问题是框架的设置太不规则,因此如何更好的设计更深更大的神经网络值得我们去思考。

为了实现这一目的,VGG模型(Visual Geometry Group)产生了,VGG由牛津大学的研究团队开发的深度卷积神经网络模型。VGG模型在2014年的ImageNet图像分类挑战赛中取得了很大的成功,并且在计算机视觉领域被广泛应用。

VGG模型的主要特点是它采用了非常小的卷积核(3x3)最大池化层(2x2),以及多个卷积和池化层的叠加。模型的深度可变,通过调整卷积和全连接层的数量来改变模型的深度。最常用的VGG模型有VGG16VGG19

VGG模型的主要优势是它具有非常好的表达能力和一致性,以及相对简单的结构。它通过多层卷积和池化层来逐渐提取图像的特征,并通过全连接层进行分类。这种结构使得模型能够捕获不同尺度下的图像特征,从而提高了模型的准确性,下面我们具体讲讲这种网络结构,如下图所示(VGG16

image-20230717002743938

VGG模型主要由VGG块和全连接层组成,通过多次叠加这些层来逐渐提取图像特征并进行分类。其中每一个VGG块都是由3×3的卷积层和2×2的池化层组成

下面是VGG16模型的详细结构:

  1. 输入层:接收输入图像的尺寸为224x224x3。

  2. VGG块部分:

    • 卷积层1-1:64个3x3的卷积核,填充为1,ReLU激活函数。

    • 卷积层1-2:64个3x3的卷积核,填充为1,ReLU激活函数。

    • 最大池化层1:2x2的池化窗口,步长为2

    • 卷积层2-1:128个3x3的卷积核,填充为1,ReLU激活函数。

    • 卷积层2-2:128个3x3的卷积核,填充为1,ReLU激活函数。

    • 最大池化层2:2x2的池化窗口,步长为2。

    • 卷积层3-1:256个3x3的卷积核,填充为1,ReLU激活函数。

    • 卷积层3-2:256个3x3的卷积核,填充为1,ReLU激活函数。

    • 卷积层3-3:256个3x3的卷积核,填充为1,ReLU激活函数。

    • 最大池化层3:2x2的池化窗口,步长为2。

    • 卷积层4-1:512个3x3的卷积核,填充为1,ReLU激活函数。

    • 卷积层4-2:512个3x3的卷积核,填充为1,ReLU激活函数。

    • 卷积层4-3:512个3x3的卷积核,填充为1,ReLU激活函数。

    • 最大池化层4:2x2的池化窗口,步长为2。

    • 卷积层5-1:512个3x3的卷积核,填充为1,ReLU激活函数。

    • 卷积层5-2:512个3x3的卷积核,填充为1,ReLU激活函数。

    • 卷积层5-3:512个3x3的卷积核,填充为1,ReLU激活函数。

    • 最大池化层5:2x2的池化窗口,步长为2。

  3. 全连接层部分:

    • 全连接层1:4096个神经元,ReLU激活函数。
    • Dropout层1:以0.5的概率随机将输入置为0。
    • 全连接层2:4096个神经元,ReLU激活函数。
    • Dropout层2:以0.5的概率随机将输入置为0。
    • 全连接层3(输出层):1000个神经元,对应ImageNet的1000个类别。

VGG16模型总共有13个卷积层和3个全连接层,参数量较大。该模型的设计思想是通过多层的小卷积核和池化层来逐渐缩小宽度,并提取出更高级别的图像特征。同时,使用ReLU激活函数来增强网络的非线性表达能力。最后通过全连接层进行分类。

随着网络的加深,图像的高度和宽度都在以一定的规律不断缩小,每次池化后刚好缩小一半,而通道数量在不断增加,而且刚好也是在每组卷积操作后增加一倍。也就是说,图像缩小的比例和通道数增加的比例是有规律的。

  • VGG使用可重复使用的卷积块构建深度卷积神经网络

  • 不同的卷积块个数和超参数可以得到不同系列的VGG(如:VGG16、VGG19)

3.2代码实现

1.VGG模型定义

原时VGG16模型定义如下

# 定义VGG16模型
class VGG16(nn.Module):
    def __init__(self, num_classes=1000):
        super(VGG16, self).__init__()

        self.features = nn.Sequential(
            nn.Conv2d(1, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),

            nn.Conv2d(64, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.Conv2d(128, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),

            nn.Conv2d(128, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),

            nn.Conv2d(256, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),

            nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )

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

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


这样看上去有点冗余,为了方便更改架构,我们可以设置VGG块,然后根据VGG块来生成网络,后续的很多网络都用类似的想法。 VGG块定义如下

import torch
import torch.nn as nn
def vgg_block(num_convs, in_channels, out_channels):
    layers = []
    for _ in range(num_convs):
        layers.append(nn.Conv2d(in_channels, out_channels,
                                kernel_size=3, padding=1))
        layers.append(nn.ReLU())
        in_channels = out_channels
    layers.append(nn.MaxPool2d(kernel_size=2,stride=2))
    return nn.Sequential(*layers)

由于这里使用的数据集数量较小,考虑到性能问题,这里我们使用VGG-11,共有8个卷积层和3个全连接层。

conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
def vgg(conv_arch):
    conv_blks = []
    in_channels = 1
    # 卷积层部分
    for (num_convs, out_channels) in conv_arch:
        conv_blks.append(vgg_block(num_convs, in_channels, out_channels))
        in_channels = out_channels

    return nn.Sequential(
        *conv_blks, nn.Flatten(),
        # 全连接层部分
        nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5),
        nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5),
        nn.Linear(4096, 10))

net = vgg(conv_arch)

2.加载Fashion-MNIST数据集

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

# 设置使用的设备为GPU,如果没有GPU则使用CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 加载Fashion-MNIST数据集
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize((224,224)),#
    transforms.Normalize((0.5,), (0.5,))
])

trainset = torchvision.datasets.FashionMNIST(root='./data', train=True,download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64,shuffle=True, num_workers=2)

testset = torchvision.datasets.FashionMNIST(root='./data', train=False,download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64,shuffle=False, num_workers=2)

由于VGG输入图像要求为224×224,这里需要将Fashion—MNIST的图像大小更改,使用transforms.Resize函数。

3.初始化模型

model = net.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.05)

4.模型训练和评估

# 训练模型
num_epochs = 10
train_losses = []
test_losses = []
train_accs = []
test_accs = []

for epoch in range(num_epochs):
    train_loss = 0.0
    train_total = 0
    train_correct = 0

    model.train()
    for images, labels in trainloader:
        images, labels = images.to(device), labels.to(device)

        optimizer.zero_grad()

        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        train_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        train_total += labels.size(0)
        train_correct += (predicted == labels).sum().item()

    train_loss /= len(trainloader)
    train_accuracy = 100.0 * train_correct / train_total
    train_losses.append(train_loss)
    train_accs.append(train_accuracy)

    test_loss = 0.0
    test_total = 0
    test_correct = 0

    model.eval()
    with torch.no_grad():
        for images, labels in testloader:
            images, labels = images.to(device), labels.to(device)

            outputs = model(images)
            loss = criterion(outputs, labels)

            test_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            test_total += labels.size(0)
            test_correct += (predicted == labels).sum().item()

    test_loss /= len(testloader)
    test_accuracy = 100.0 * test_correct / test_total
    test_losses.append(test_loss)
    test_accs.append(test_accuracy)

    print(f"Epoch {epoch+1}/{num_epochs}: Train Loss: {train_loss:.4f}, Train Acc: {train_accuracy:.2f}%, Test Loss: {test_loss:.4f}, Test Acc: {test_accuracy:.2f}%")

# 绘制训练误差和测试误差曲线
plt.figure(figsize=(10, 5))
plt.plot(range(1, num_epochs+1), train_losses, label='Train Loss')
plt.plot(range(1, num_epochs+1), test_losses, label='Test Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training and Testing Loss')
plt.legend()
plt.show()

# 绘制训练准确率和测试准确率曲线
plt.figure(figsize=(10, 5))
plt.plot(range(1, num_epochs+1), train_accs, label='Train Acc')
plt.plot(range(1, num_epochs+1), test_accs, label='Test Acc')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Training and Testing Accuracy')
plt.legend()
plt.show()
Epoch 1/10: Train Loss: 2.2078, Train Acc: 15.18%, Test Loss: 0.9984, Test Acc: 65.97%
Epoch 2/10: Train Loss: 0.5435, Train Acc: 79.75%, Test Loss: 0.4182, Test Acc: 84.12%
Epoch 3/10: Train Loss: 0.3391, Train Acc: 87.61%, Test Loss: 0.3074, Test Acc: 88.30%
Epoch 4/10: Train Loss: 0.2872, Train Acc: 89.33%, Test Loss: 0.2830, Test Acc: 89.32%
Epoch 5/10: Train Loss: 0.2521, Train Acc: 90.65%, Test Loss: 0.2747, Test Acc: 90.11%
Epoch 6/10: Train Loss: 0.2228, Train Acc: 91.58%, Test Loss: 0.2585, Test Acc: 90.44%
Epoch 7/10: Train Loss: 0.1985, Train Acc: 92.61%, Test Loss: 0.2545, Test Acc: 91.10%
Epoch 8/10: Train Loss: 0.1767, Train Acc: 93.42%, Test Loss: 0.2654, Test Acc: 90.92%
Epoch 9/10: Train Loss: 0.1535, Train Acc: 94.28%, Test Loss: 0.2362, Test Acc: 91.81%
Epoch 10/10: Train Loss: 0.1324, Train Acc: 94.98%, Test Loss: 0.2662, Test Acc: 91.24%

image-20230717002337941

image-20230717002347934

我们对比三个架构在`Fashion-MNIST数据集上的结果,发现测试集的Accuracy,VGG-11表现最好,突破了0.91,AlexNet次之,LeNet最低,这说明使用更深的网络是能够提升图像识别性能的。

总结

我们介绍了三种经典的卷积神经网络架构:LeNet,AlexNet,VGG。他们的共同思想都是使用卷积层来学习图片的空间信息,提取特征,最后使用全连接层转换到我们要的分类空间。

LeNet是首个成功应用在手写数字识别数据集上的深度卷积神经网络,只有2个卷积层、两个池化层和三个全连接层

AlexNet在LeNet基础上使用了更多更深的卷积层,在2012年的ImageNet比赛上一战成名,从此引领了深度学习的浪潮

VGG在AlexNet的基础上构建了一个非常深的卷积神经网络,通过堆叠多个小尺寸的卷积核和池化层来逐步提取图像特征。它的设计简单一致,具有较好的性能和可迁移性,成为了深度学习研究中的重要里程碑之一。

从LeNet到AlexNet再到VGG,网络在不断的变深变大,模型参数也在不断增加,包括现在很多模型都是上亿个参数,这对数据集和硬件都有很高的要求,后续我们再介绍一些能够减少模型参数的方法。

在这里插入图片描述

🔎本章的介绍到此介绍,如果文章对你有帮助,请多多点赞、收藏、评论、关注支持!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构与算法——顺序表的基本操作(C语言详解版)

顺序表插入元素 向已有顺序表中插入数据元素,根据插入位置的不同,可分为以下 3 种情况: 插入到顺序表的表头;在表的中间位置插入元素;尾随顺序表中已有元素,作为顺序表中的最后一个元素; 虽然…

GaussDB OLTP 云数据库配套工具DAS

目录 一 、前言 二、DAS的定义 1、DAS的定义 2、DAS功能特点 三、DAS应用场景 1、标准版 2、企业版 四、操作示例(标准版) 1、登录华为控制台登录,输入账号密码 2、新增数据库实例链接 3、新建对象 4、SQL操作 5、导入导出 五、…

OC多态性浅析

OC多态性浅析 小实验 假设有以下两个类classA与class B的声明与实现&#xff1a; /// classA.h#ifndef classA_h #define classA_h#import <Foundation/Foundation.h>interface classA : NSObject-(void) printVar;end#endif /* classA_h *//// classB.h#ifndef class…

微信小程序事件点击跳转页面的五种方法

第一种:标签 这是最常见的一种跳转方式,相当于html里的a标签 <navigator url"/pages/main/main"></navigator>第二种:wx.navigateTo({})方法 1.前端wxml <button bindtap"getCeshi" type"primary"> 测试按钮 </button>…

基于SpringBoot+vue的外卖点餐系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

【Android知识笔记】应用进程(二)

Service的启动原理 向AMS发送startService请求 startService时会首先拿到AMS的Binder代理对象,向AMS发起startService请求: AMS处理startService请求 接下来看AMS端处理应用的startService请求: 回忆一下应用进程启动流程: 接下来看如果Service所在应用进程没有启动的情…

GitLab 私有 Go Modules 的搭建配置

配置步骤 配置 GitLab Access Token 将 GitLab Access Token 写入到 ~/.netrc 文件里(也就是根目录下的 .netrc 文件),没有则新建,并写入内容:machine git.tenorshare.cn login ${USER_NAME} password ${GITLAB_ACCESS_TOKEN},其中 ${USER_NAME} 是GitLab 用户名,${GIT…

K8S的部署项目流程

步骤 &#xff08;1&#xff09;条件准备 &#xff08;2&#xff09;制作镜像 &#xff08;3&#xff09;docker启动程序 &#xff08;4&#xff09;启动制作好的镜像&#xff0c;看是否可以访问 &#xff08;5&#xff09;上传镜像到镜像容器中&#xff08;如&#xff1a;阿里…

Hive最全总结,学习与面试,看这一篇就行了!

废话不多说,上题: 1.Hadoop中两个⼤表实现join的操作,简单描述。 (1)Hive中可以通过分区来减少数据量;(2)还可以通过优化HQL语句,⽐如只查询需要的字段,尽量避免全表、全字段查询; 2.Hive中存放是什么? 表。存的是和hdfs的映射关系,hive是逻辑上的数据仓库,…

使用docker安装portainer

portainer是一个非常好用的docker可视化工具&#xff0c;这篇文章就介绍一下怎么使用docker来安装portainer。 第一步&#xff1a;在docker仓库查找portainer的版本 docker search portainer 我们可以看到&#xff0c;第一个portainer/portainer的描述中说了&#xff0c;这个仓…

【数据架构实践】Netflix 万亿级实时数据基础架构的四个创新阶段

Netflix 是一个很棒的地方&#xff0c;周围有许多了不起的同事。我为参与将共同信念变为现实的旅程中的每个人感到无比自豪。我想花点时间分享一下团队的主要成就&#xff1a; 我们在 Netflix 的所有组织中将流数据用例从 0 增加到 2000 多个。我们构建和发展了成功的产品&…

【Spring框架】getBean的更多用法

目录 用法1&#xff1a;根据名称获取Bean对象用法2&#xff1a;根据类型获取Bean对象用法3&#xff1a;根据名称类型来获取Bean对象 用法1&#xff1a;根据名称获取Bean对象 UserService userService (UserService)context.getBean("user");用法2&#xff1a;根据类…

如何利用量比和换手率?

1 量比 有人说&#xff0c;量比指标是盘口语言的翻译器&#xff0c;堪称股市风向标&#xff01;这一说法究竟从何而来&#xff0c;量比大小到底意味着什么呢&#xff1f; 量比是衡量相对成交量的指标。它是指股市开市后平均每分钟的成交量与过去5个交易日平均每分钟成交量之比…

【高并发网络通信架构】4.高效事件驱动模型:Reactor 模型

目录 一&#xff0c;往期文章 二&#xff0c;基本概念 1.前言 2.基本框架 3.核心特征 4.工作流程 5.用“网络通信”来理解 Reactor 模型 三&#xff0c;代码实现 1.使用 epoll 进行多路复用实现 Reactor 模式的操作流程 2.Reactor 模式实现代码&#xff08;参考&…

【SQL】Your password has expired. To log in you must change it.......

在配置SQL的编辑器的时候&#xff0c;连接过程出现了如下的错误&#xff1a; Your password has expired. To log in you must change it using a client that supports expired passwords.当连接到 SQL 时&#xff0c;如果出现 “Your password has expired. To log in you m…

【LSTM】理解LSTM

原文&#xff1a;https://colah.github.io/posts/2015-08-Understanding-LSTMs/ 递归神经网路 Humans don;t start their thinking from scratch every second. 人类都不是每一秒都从零开始思考。 from scratch 从零开始 当你读到这篇文章时&#xff0c;你理解每个单词时&a…

会议音响系统麦克风阵列波束形成算法C语言实现

+v hezkz17进数字音频系统研究开发交流答疑 一 应用麦克风阵列波束成形算法做的项目产品 二 麦克风波束形成技术应用领域? 麦克风波束形成技术是一种利用多个麦克风阵列来实现声音定向捕捉和增强的技术。通过对多个麦克风信号进行处理和合成,可以使麦克风系统在特定方向…

【Matlab】智能优化算法_蚁群优化算法ACO

【Matlab】智能优化算法_蚁群优化算法ACO 1.背景介绍2.废话不多说&#xff0c;直接上代码3.文件结构4.详细代码及注释4.1 ACO.m4.2 createColony.m4.3 createGraph.m4.4 drawBestTour.m4.5 drawGraph.m4.6 drawPhromone.m4.7 ACO.mfitnessFunction.m4.8 rouletteWheel.m4.9 upd…

ubuntu创建多用户并使用ssh链接

添加多个同时登录的用户 以下内容中的“username”根据自己需求自己定义 1.创建新用户 sudo useradd username2.给新用户添加管理权限 sudo vim /etc/sudoers打开的文件中添加如下内容 username ALL(ALL:ALL) ALL3.设置密码 输入&#xff1a; sudo passwd username打开的…

使用golang+antlr4构建一个自己的语言解析器

Goland 中Antlr4插件 在goland中安装Antlr4插件&#xff0c;用于识别输入的字符在在语法文件中生成的语法树的样子&#xff0c;大概就是如下的摸样 下载步骤&#xff1a; 1.点击文件中的设置选项 2.在插件目录下输入Antlr4搜索插件 3.点击安装即可 编写自己的语言语法文件…