GoogLeNet网络详解并使用pytorch搭建模型

1.GoogLeNet网络详解

网络中的创新点:
(1)引入了Inception结构(融合不同尺度的特征信息)
(2)使用1x1的卷积核进行降维以及映射处理 (虽然VGG网络中也有,但该论文介绍的更详细)
(3)添加两个辅助分类器帮助训练
(4)丢弃全连接层,使用平均池化层(大大减少模型参数,除去两个辅助分类器,网络大小只有vgg的1/20)

(1) Inception结构

inception的作用:增加网络深度和宽度的同时减少参数。
在这里插入图片描述
左图为原始结构,右图加上了降维功能。

在左图中,将特征矩阵同时输入到四个分支中进行处理(并行),将这四个分支处理后的特征矩阵按深度(channel维度)进行拼接,最后得到一个输出特征矩阵。

在右图中,通过增加三个1x1的卷积层达到降维的作用,目的是为了降维(减小深度),减少模型训练参数,减少计算量。

:每个分支所得特征矩阵的高和宽必须相同(通过调整stride和padding),以保证输出特征能在深度上进行拼接。

(2) 1x1卷积核降维

在这里插入图片描述
如果不使用1x1卷积核,使用64个5x5的卷积核进行卷积,就需要819200个参数;如果使用24个1x1的卷积核进行卷积,只需要50688个参数。

CNN参数个数 = 卷积核尺寸 × 卷积核深度 × 卷积核组数 = 卷积核尺寸 × 输入特征矩阵深度 × 输出特征矩阵深度

(3) 辅助分类器

在这里插入图片描述
在GoogLeNet网络中有两个辅助分类器,结构是完全一样的。这两个辅助分类器的输入分别来自Inception(4a)和Inception(4d)。

辅助分类器的第一层是一个平均池化下采样层,池化核大小为5x5,stride=3

第二层是卷积层,卷积核大小为1x1,stride=1,卷积核个数是128

第三层是全连接层,节点个数是1024

第四层是全连接层,节点个数是1000(对应分类的类别个数)

辅助分类器的两个分支的作用
(1)可以把它看做inception网络中的一个小细节,它确保了即便是隐藏单元和中间层也参与了特征计算,它们也能预测图片的类别,它在inception网络中起到一种调整的效果,并且能防止网络发生过拟合。

(2)给定深度相对较大的网络,有效传播梯度反向通过所有层的能力是一个问题。通过将辅助分类器添加到这些中间层,可以期望较低阶段分类器的判别力。在训练期间,它们的损失以折扣权重(辅助分类器损失的权重是0.3)加到网络的整个损失上。

GoogLeNet 网络参数

在这里插入图片描述参数#1x1,#3x3reduce,#3x3,#5x5reduce,#5x5,#pool proj主要对应Inception结构的配置
在这里插入图片描述
#1x1对应着分支1上1x1的卷积核个数
#3x3reduce对应着分支2上1x1的卷积核个数
#3x3对应着分支2上3x3的卷积核个数
#5x5reduce对应着分支3上1x1的卷积核个数
#5x5对应着分支3上5x5的卷积核个数
pool proj对应着分支4上1x1的卷积核个数。

GoogLeNet 网络模型

在这里插入图片描述

2.使用Pytorch搭建GoogLeNet网络

model.py

定义卷积模板

在搭建网络之前先进行模板文件的创建,可以通过此方法定义卷积模板避免重复搭建conv和relu,卷积模板的定义:

class BasicConv2d(nn.Module):        #在搭建卷积层过程中通常将卷积和ReLU激活函数共同使用
    def __init__(self, in_channels, out_channels, **kwargs):
        super(BasicConv2d, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, **kwargs)
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):            #定义正向传播过程
        x = self.conv(x)
        x = self.relu(x)
        return x

定义Inception模板

class Inception(nn.Module):                 #定义Inception结构模板
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj):  #结合googlenet网络参数和Inception结构
        super(Inception, self).__init__()

        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)    #分支1,使用定义的卷积模板,输入的特征矩阵深度为in_channels,卷积核个数为传入的ch1x1

        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),           #分支2
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)       #将padding设置为1,使输出特征矩阵和输入特征矩阵的高和宽保持一致,保证输出大小等于输入大小
        )

        self.branch3 = nn.Sequential(                                    #分支3
            BasicConv2d(in_channels, ch5x5red, kernel_size=1),
            BasicConv2d(ch5x5red, ch5x5, kernel_size=5, padding=2)       #保证输出大小等于输入大小:output_size=(input_size-5+2*2)/1+1=input_size
        )

        self.branch4 = nn.Sequential(                                    #分支4
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )

    def forward(self, x):                         #定义正向传播过程
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        branch4 = self.branch4(x)

定义辅助分类器模板

class InceptionAux(nn.Module):                                    #定义辅助分类器
    def __init__(self, in_channels, num_classes):
        super(InceptionAux, self).__init__()
        self.averagePool = nn.AvgPool2d(kernel_size=5, stride=3)  #平均池化
        self.conv = BasicConv2d(in_channels, 128, kernel_size=1)  # output[batch, 128, 4, 4]

        self.fc1 = nn.Linear(2048, 1024)                          #输入节点个数128x4x4
        self.fc2 = nn.Linear(1024, num_classes)

    def forward(self, x):                                         #定义正向传播过程
        # aux1: N x 512 x 14 x 14, aux2: N x 528 x 14 x 14
        x = self.averagePool(x)
        # aux1: N x 512 x 4 x 4, aux2: N x 528 x 4 x 4
        x = self.conv(x)
        # N x 128 x 4 x 4
        x = torch.flatten(x, 1)
        x = F.dropout(x, 0.5, training=self.training)            #原论文采用0.7
        # N x 2048                                               #self.training会随着训练或测试的不同而变化
        x = F.relu(self.fc1(x), inplace=True)                    #当实例化一个模型model后,可以通过model.train()和model.eval()来控制模型的状态
        x = F.dropout(x, 0.5, training=self.training)            #在model.train()模式下self.training=True,在model.eval()模式下self.training=False
        # N x 1024
        x = self.fc2(x)
        # N x num_classes
        return x

定义GoogLeNet网络

class GoogLeNet(nn.Module):                                                      #定义GoogLeNet
    def __init__(self, num_classes=1000, aux_logits=True, init_weights=False):   #初始化函数
        super(GoogLeNet, self).__init__()
        self.aux_logits = aux_logits                                             #将是否使用辅助分类器的布尔变量传入类变量中
                                                                                 #根据GoogLeNet简图进行搭建
        self.conv1 = BasicConv2d(3, 64, kernel_size=7, stride=2, padding=3)      #为了将特征矩阵的高和宽缩减到原来的一半,这里将padding设置为3:(224-7+2*3)/2+1=112.5(pytorch默认向下取正)
        self.maxpool1 = nn.MaxPool2d(3, stride=2, ceil_mode=True)                #ceil_mode表示如果进行最大池化后得到的值为小数,设置为True就会向上取整,设置为False就会向下取整
                                                                                 #省略LocalRespNorm,没什么用
        self.conv2 = BasicConv2d(64, 64, kernel_size=1)
        self.conv3 = BasicConv2d(64, 192, kernel_size=3, padding=1)
        self.maxpool2 = nn.MaxPool2d(3, stride=2, ceil_mode=True)

        self.inception3a = Inception(192, 64, 96, 128, 16, 32, 32)               #使用定义的Inception模板
        self.inception3b = Inception(256, 128, 128, 192, 32, 96, 64)
        self.maxpool3 = nn.MaxPool2d(3, stride=2, ceil_mode=True)

        self.inception4a = Inception(480, 192, 96, 208, 16, 48, 64)             #这里每一个Inception的输入都可以通过将上一层Inception层的四个分支的特征矩阵深度加起来得到
        self.inception4b = Inception(512, 160, 112, 224, 24, 64, 64)
        self.inception4c = Inception(512, 128, 128, 256, 24, 64, 64)
        self.inception4d = Inception(512, 112, 144, 288, 32, 64, 64)
        self.inception4e = Inception(528, 256, 160, 320, 32, 128, 128)
        self.maxpool4 = nn.MaxPool2d(3, stride=2, ceil_mode=True)

        self.inception5a = Inception(832, 256, 160, 320, 32, 128, 128)
        self.inception5b = Inception(832, 384, 192, 384, 48, 128, 128)

        if self.aux_logits:                                                    #如果使用辅助分类器,即aux_logits = True,则创建aux1和aux2
            self.aux1 = InceptionAux(512, num_classes)                         #输入是Inception4a的输出
            self.aux2 = InceptionAux(528, num_classes)                         #输入是Inception4b的输出

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))                            #nn.AdaptiveAvgPool2d()自适应平均池化下采样,参数(1,1)代表我们所需要的输出特征矩阵的高和宽
        self.dropout = nn.Dropout(0.4)                                         #不论输入特征矩阵的高和宽的大小,都可以通过自适应平均池化下采样得到所指定的输出特征矩阵的高和宽
        self.fc = nn.Linear(1024, num_classes)
        if init_weights:                                                       #如果init_weights = True,则对模型权重进行初始化
            self._initialize_weights()

    def forward(self, x):                                                      #定义正向传播过程
        # N x 3 x 224 x 224
        x = self.conv1(x)
        # N x 64 x 112 x 112
        x = self.maxpool1(x)
        # N x 64 x 56 x 56
        x = self.conv2(x)
        # N x 64 x 56 x 56
        x = self.conv3(x)
        # N x 192 x 56 x 56
        x = self.maxpool2(x)

        # N x 192 x 28 x 28
        x = self.inception3a(x)
        # N x 256 x 28 x 28
        x = self.inception3b(x)
        # N x 480 x 28 x 28
        x = self.maxpool3(x)
        # N x 480 x 14 x 14
        x = self.inception4a(x)
        # N x 512 x 14 x 14
        if self.training and self.aux_logits:    # eval model lose this layer
            aux1 = self.aux1(x)

        x = self.inception4b(x)
        # N x 512 x 14 x 14
        x = self.inception4c(x)
        # N x 512 x 14 x 14
        x = self.inception4d(x)
        # N x 528 x 14 x 14
        if self.training and self.aux_logits:    # eval model lose this layer   是否使用辅助分类器,在训练过程使用,测试过程不用
            aux2 = self.aux2(x)

        x = self.inception4e(x)
        # N x 832 x 14 x 14
        x = self.maxpool4(x)
        # N x 832 x 7 x 7
        x = self.inception5a(x)
        # N x 832 x 7 x 7
        x = self.inception5b(x)
        # N x 1024 x 7 x 7

        x = self.avgpool(x)
        # N x 1024 x 1 x 1
        x = torch.flatten(x, 1)
        # N x 1024
        x = self.dropout(x)
        x = self.fc(x)
        # N x 1000 (num_classes)
        if self.training and self.aux_logits:   # eval model lose this layer    是否使用辅助分类器,在训练过程使用,测试过程不用
            return x, aux2, aux1
        return x

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)

train.py

和VGG的train脚本基本一样,只有两点不同:

    net = GoogLeNet(num_classes=5, aux_logits=True, init_weights=True)    #5个类别,使用辅助分类器、权重初始化

GoogLeNet的网络输出 loss 有三个部分,分别是主干输出loss、两个辅助分类器输出loss(权重0.3)

logits, aux_logits2, aux_logits1 = net(images.to(device))   #三个输出:主输出、辅助分类器1的输出、辅助分类器2的输出
loss0 = loss_function(logits, labels.to(device))            #计算主分类器与真实标签之间的损失
loss1 = loss_function(aux_logits1, labels.to(device))       #计算辅助分类器1与真实标签之间的损失
loss2 = loss_function(aux_logits2, labels.to(device))       #计算辅助分类器2与真实标签之间的损失
loss = loss0 + loss1 * 0.3 + loss2 * 0.3                    #将三个损失相加,0.3是因为在原论文中按0.3的权重
loss.backward()                                             #将损失进行反向传播
optimizer.step()                                            #通过优化器更新模型参数

prdict.py

在预测过程不需要辅助分类器

    # create model
    model = GoogLeNet(num_classes=5, aux_logits=False).to(device)     #将aux_logits=False就不会构建辅助分类器

    # load model weights
    weights_path = "./googleNet.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    missing_keys, unexpected_keys = model.load_state_dict(torch.load(weights_path, map_location=device),
                                                          strict=False)      #但是在保存模型时已经将辅助分类器的参数保存在权重当中,因此将strict=False
                                                                             #如果strict=True,就会精准匹配当前模型和需要载入的权重模型的结构

导师博客:https://blog.csdn.net/qq_37541097/article/details/103482003
导师github:https://github.com/WZMIAOMIAO/deep-learning-for-image-processing
模型参考:https://www.cnblogs.com/itmorn/p/11230388.html
代码用的导师的,自己又加了些备注,就放在自己的github里了:
https://github.com/Petrichor223/Deep_Learning/tree/master

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以给你一些关于使用PyTorch搭建HR-Net的建议:1. 先了解HR-Net的基本架构,然后熟悉PyTorch提供的相关API;2. 使用PyTorch搭建HR-Net的基本结构,并设置相应的参数;3. 调整参数,以获得最佳模型;4. 在测试集上进行验证,以确保模型具有良好的性能。 ### 回答2: HR-Net是一种用于人体关键点检测的神经网络模型,我们可以使用PyTorch搭建HR-Net。在搭建HR-Net之前,我们需要进行一些准备工作。 首先,我们需要安装PyTorch库。可以通过以下命令安装PyTorch: ``` pip install torch torchvision ``` 然后,我们需要下载HR-Net的代码和预训练的权重。可以在GitHub上找到HR-Net的代码库,并下载。下载完成后,解压缩代码包。 接下来,我们可以在PyTorch中定义HR-Net的网络结构。HR-Net基于两个主要的网络模块:骨干网络和多分支特征融合模块。 在骨干网络中,我们可以选择使用一些常见的神经网络模型,如ResNet、AlexNet等。我们可以在PyTorch中创建这些骨干网络,并将其作为HR-Net的输入。 在多分支特征融合模块中,我们通过将不同尺度的特征图进行融合,来提高人体关键点检测的准确性。我们可以在PyTorch中实现这个多分支特征融合模块,并将其添加到HR-Net中。 最后,我们可以加载HR-Net的预训练权重,并将其用于人体关键点检测任务。我们可以使用PyTorch的数据加载器来加载训练数据,并使用预定义的损失函数和优化器来训练模型使用PyTorch搭建HR-Net可以使我们更轻松地实现人体关键点检测任务,并利用PyTorch的丰富功能来优化和扩展HR-Net模型。 ### 回答3: 使用PyTorch搭建HR-Net可以通过以下步骤完成: 1. 安装PyTorch:首先要在计算机上安装PyTorch库,可以通过在终端或命令提示符中运行适用于您的系统的安装命令来完成。 2. 导入必要的库:在Python脚本中,导入PyTorch以及其他必要的库,如numpy、matplotlib等。 3. 构建HR-Net模型:HR-Net是一种深度卷积神经网络体系结构,它具有多个分支并行处理低分辨率和高分辨率特征。可以使用PyTorch的nn.Module类构建HR-Net模型,并定义需要的卷积、池化、Batch Normalization等操作层。 4. 定义前向传播函数:在HR-Net模型类中,定义一个前向传播函数,该函数定义了输入数据通过模型时的计算流程。在这个函数中,可以将输入数据传递到HR-Net的各个分支,然后将其联合起来形成最终的输出。 5. 定义损失函数和优化器:为了训练HR-Net模型,需要定义一个损失函数来度量模型的输出和真实标签之间的差距,并选择一个优化器来更新模型的参数。PyTorch提供了各种损失函数和优化器的选项,可以根据具体问题的需求选择合适的函数和优化器。 6. 训练模型使用已定义的损失函数和优化器,在训练数据上进行模型的训练。通过将训练数据输入到HR-Net模型中,并计算其输出与真实标签之间的损失,根据这个损失来更新模型的参数。 7. 测试模型:在训练完成后,可以使用测试数据来评估模型的性能。将测试数据输入到HR-Net模型中,获取模型的预测输出,并与真实标签进行比较,可以计算一些评价指标,例如准确率、精确率、召回率等。 8. 调整模型和超参数:根据测试结果,可以对模型和超参数进行调整,以优化模型的性能。可以更改模型的结构、增加或减少训练数据,调整学习率等。 9. 保存和加载模型:在训练完成后,可以将模型保存到磁盘上,以便后续使用。同时,也可以从保存的模型文件中加载已经训练好的模型,并在新的数据上进行预测。 以上是使用PyTorch搭建HR-Net的一般步骤,具体实现过程中可以根据需要进行进一步的细化和改进。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值