GoogLeNet(附Pytorch源码)

GoogLeNet(附Pytorch源码)

GoogleNet(Inception v1)是由Google团队在2014年提出的深度卷积神经网络架构。它是为解决图像分类任务而设计的,并在ImageNet图像分类挑战赛中取得了很好的成绩。与VGGNet、LeNet、AlexNet有较大不同。在之前我们介绍的架构中VGG大量使用了3×3卷积,AlexNet使用了5×5,而NiN使用了1×1。因此,在构建卷积层时,我们要决定过滤器的大小究竟是1×1,3×3还是5×5,或者要不要添加池化层。

GoogleNet网络的想法就是**我全都要!**GoogleNet最显著的特点是采用了一系列并行连接的Inception模块。

原文作者提到,"Inception"这个名字的想法来自于电影《盗梦空间》一个著名的网络梗:WE NEED TO GO DEEPER。

虽然网络架构因此变得更加复杂,但网络表现却非常好,下面我们来看一下GoogleNet具体组成架构:

1×1卷积

1×1 卷积由 NiN引入。1×1卷积与ReLU一起使用。因此,NiN最初使用它来引入更多的非线性,以提高网络的表示能力,因为NiN的作者认为数据是非线性形式的。在GoogLeNet中,1×1卷积被用作降维模块,以减少计算量。通过减少计算瓶颈,可以增加深度和宽度。

我举一个简单的例子来说明这一点。假设我们需要执行 5×5 卷积而不使用 1×1 卷积,如下所示:

运算次数 = (14×14×48)×(5×5×480) = 112.9M 使用 1×1 卷积:

  • 1×1的运算次数 = (14×14×16)×(1×1×480) = 1.5M
  • 5×5的运算次数 = (14×14×48)×(5×5×16) = 3.8M
  • 总操作次数 = 1.5M + 3.8M = 5.3M,这比 112.9M 小得多!!!

事实上,上面的例子是在inception时 5×5 卷积的计算。(我们可以认为,当降维时,实际上我们正在以非线性的方式进行从高维到低维的映射。相反,对于PCA,它执行线性降维。)因此,与没有 1×1 卷积的情况相比,我们可以在不增加操作数量的情况下构建 inception 模块。1×1卷积可以帮助减小模型大小,这在某种程度上也有助于减少过拟合问题

2. Inception模块

与之前介绍的lexNet VGGNet,每层的 conv 大小都是固定的相比。Inception模块将输入特征图分别进行多个不同尺寸的卷积和池化操作,并将它们的结果进行拼接。这样可以同时捕捉到不同尺度上的特征,从而提高了特征的表达能力。具体而言,Inception模块包含了1×1、3×3和5×5的卷积层,以及1×1卷积和3×3最大池化层。通过使用不同尺寸的卷积和池化操作,网络能够有效地捕捉到局部和全局的特征。

3. 全局平均池化层:

在网络的最后,GoogleNet使用全局平均池化层将特征图的大小降为1×1。这种操作可以将特征图中每个通道的特征合并为一个标量值,从而减少参数数量,并且有助于提取更加全局的特征。

之前的网络以全连接(FC)层用于网络末端,所有输入都连接到每个输出。 上面的权重(连接)数量 = 7×7×1024×1024 = 51.3M 在GoogLeNet中,全局平均池化在网络末端使用,通过对每个特征图从7×7到1×1进行平均 权重数量 = 0

4.训练辅助分类器

在模型中间的一部分有softmax分支,它们仅用于训练。这些分支是辅助分类器,包括:

  • 5×5 平均池化(步幅 3)
  • 1×1 卷积(128 个过滤器)
  • 1024 FC
  • 1000FC
  • Softmax

5.基本架构

了解了上面介绍的基本单元之后,我们就可以谈谈整体的网络架构了。

总共有22层!与之前的AlexNet、LeNet和VGGNet相比,它已经是一个非常深的模型了。 而且我们可以看到有很多inception模块连接在一起,甚至可以更深入。 以下是各层参数的详细信息。我们其实可以扩展1×1卷积的例子来自己计算运算次数。

6.Pytorch代码实现

python 复制代码
import warnings 
warnings.filterwarnings('ignore')
# 导入相关库
import torch
import torch.nn as nn
import torch.optim as optim
from torch.nn import functional as F
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

# inception块
class Inception(nn.Module):
    # c1--c4是每条路径的输出通道数
    def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):
        super(Inception, self).__init__(**kwargs)
        # 线路1,单1x1卷积层
        self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
        # 线路2,1x1卷积层后接3x3卷积层
        self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)
        self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)
        # 线路3,1x1卷积层后接5x5卷积层
        self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1)
        self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)
        # 线路4,3x3最大汇聚层后接1x1卷积层
        self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
        self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1)

    def forward(self, x):
        p1 = F.relu(self.p1_1(x))
        p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
        p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
        p4 = F.relu(self.p4_2(self.p4_1(x)))
        # 在通道维度上连结输出
        return torch.cat((p1, p2, p3, p4), dim=1)

# 构建google-Net
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
                   nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1),
                   nn.ReLU(),
                   nn.Conv2d(64, 192, kernel_size=3, padding=1),
                   nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b3 = nn.Sequential(Inception(192, 64, (96, 128), (16, 32), 32),
                   Inception(256, 128, (128, 192), (32, 96), 64),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b4 = nn.Sequential(Inception(480, 192, (96, 208), (16, 48), 64),
                   Inception(512, 160, (112, 224), (24, 64), 64),
                   Inception(512, 128, (128, 256), (24, 64), 64),
                   Inception(512, 112, (144, 288), (32, 64), 64),
                   Inception(528, 256, (160, 320), (32, 128), 128),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b5 = nn.Sequential(Inception(832, 256, (160, 320), (32, 128), 128),
                   Inception(832, 384, (192, 384), (48, 128), 128),
                   nn.AdaptiveAvgPool2d((1,1)),
                   nn.Flatten())

net = nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 10))


# Xavier初始化:防止梯度爆炸,这是CNN常用的操作,特别对于GoogleNet这种已经算比较深的网络而言,特别有效,之前我们也介绍过他的具体公式。
def init_weights(m):
    if type(m) == nn.Linear or type(m) == nn.Conv2d: #对全连接层和卷积层初始化
        nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)

# 检查是否有可用的GPU
device = torch.device('cuda'if torch.cuda.is_available() else 'cpu')
model = net.to(device)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)

# 加载Fashion-MNIST数据集
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize((96,96)),
    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=128, 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=128, shuffle=False, num_workers=2)



# 训练模型
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*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*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()
yaml 复制代码
Epoch 1/10: Train Loss: 2.0245, Train Acc: 0.24, Test Loss: 1.2393, Test Acc: 0.50
Epoch 2/10: Train Loss: 0.8974, Train Acc: 0.67, Test Loss: 1.9615, Test Acc: 0.29
Epoch 3/10: Train Loss: 0.6790, Train Acc: 0.75, Test Loss: 0.4919, Test Acc: 0.81
Epoch 4/10: Train Loss: 0.4275, Train Acc: 0.84, Test Loss: 0.4063, Test Acc: 0.85
Epoch 5/10: Train Loss: 0.3615, Train Acc: 0.86, Test Loss: 0.3978, Test Acc: 0.84
Epoch 6/10: Train Loss: 0.3253, Train Acc: 0.88, Test Loss: 0.3284, Test Acc: 0.88
Epoch 7/10: Train Loss: 0.2964, Train Acc: 0.89, Test Loss: 0.3079, Test Acc: 0.89
Epoch 8/10: Train Loss: 0.2726, Train Acc: 0.90, Test Loss: 0.2953, Test Acc: 0.89
Epoch 9/10: Train Loss: 0.2540, Train Acc: 0.90, Test Loss: 0.3061, Test Acc: 0.88
Epoch 10/10: Train Loss: 0.2380, Train Acc: 0.91, Test Loss: 0.2799, Test Acc: 0.90

从结果可以看出,GoogLeNet的精确度超过了0.9,比之前的AlexNet更好。

总结

GoogleNet的是通过Inception模块的并行连接和多尺度的卷积和池化操作来提高特征的表示能力。它相对于传统的深度网络具有更高的计算效率和更强大的特征学习能力。

基本思想是Inception网络不需要人为决定使用哪个过滤器或者是否需要池化,而是由网络自行确定这些参数,你可以给网络添加这些参数的所有可能值,然后把这些输出连接起来,让网络自己学习它需要什么样的参数,采用哪些过滤器组合。简单来说,这些参数都是试出来的,Google太有钱了。

GoogleNet的成功开创了一系列基于Inception架构的后续版本(如Inception v2、v3等),为深度学习在计算机视觉任务中的广泛应用奠定了基础。

相关推荐
AI极客菌1 小时前
Controlnet作者新作IC-light V2:基于FLUX训练,支持处理风格化图像,细节远高于SD1.5。
人工智能·计算机视觉·ai作画·stable diffusion·aigc·flux·人工智能作画
阿_旭1 小时前
一文读懂| 自注意力与交叉注意力机制在计算机视觉中作用与基本原理
人工智能·深度学习·计算机视觉·cross-attention·self-attention
王哈哈^_^1 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
Power20246662 小时前
NLP论文速读|LongReward:基于AI反馈来提升长上下文大语言模型
人工智能·深度学习·机器学习·自然语言处理·nlp
数据猎手小k2 小时前
AIDOVECL数据集:包含超过15000张AI生成的车辆图像数据集,目的解决旨在解决眼水平分类和定位问题。
人工智能·分类·数据挖掘
好奇龙猫2 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
沉下心来学鲁班2 小时前
复现LLM:带你从零认识语言模型
人工智能·语言模型
数据猎手小k2 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
YRr YRr2 小时前
深度学习:循环神经网络(RNN)详解
人工智能·rnn·深度学习
sp_fyf_20243 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘