深度学习(GoogLeNet)

深度学习(GoogLeNet)1GoogLeNet的介绍1.1GoogLeNet的简介GoogLeNet模型是由谷歌(Google)团队开发出来的卷积神经网络,它是2014年ImageNet挑战赛的冠军模型。相比于AlexNet模型,GoogLeNet模型的网络结构更深,共包括87层。尽管模型结构变得更复杂,但参数量更少了。GoogLeNet模型的参数量为5941552个,仅为AlexNet模型参数量的1/10。这主要归功于GoogLeNet创新性地采用了Inception模块。感兴趣的读者可以阅读原始顶会顶刊http://

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

本篇文章采用百度paddlepaddle深度学习框架,并在百度Ai Studio平台上运行。

本篇文章基于PaddlePaddle2.0-构建卷积网络GoogLeNet,不了解2.0API的读者可以参考深度学习(paddle2.0 API)_无意2121的博客-CSDN博客

目录

1 GoogLeNet的介绍

1.1 GoogLeNet的简介

1.2 GoogLeNet 原始论文

1.2.1 论文标题的来源

1.2.2 GoogLeNet 提出的原因

2 GoogLeNet 创新点

2.1 inception(原生模块)的结构

​2.2 inception(原生模块)的优势

2.3 inception(具有降维功能)的结构

2.4 1*1卷积核的作用

2.5 GAP

2.6 两个辅助分类器

3 GoogLeNet的定义

3.1 GoogLeNet的总体架构

3.2 GoogLeNet的变种

3.3 GoogLeNet的完整代码定义

4 构建GoogLeNet做CIFAR图像识别


1 GoogLeNet的介绍

1.1 GoogLeNet的简介

GoogLeNet模型是由谷歌(Google)团队开发出来的卷积神经网络,它是2014年ImageNet挑战赛的冠军模型。相比于AlexNet模型,GoogLeNet模型的网络结构更深,共包括87层。尽管模型结构变得更复杂,但参数量更少了。GoogLeNet模型的参数量为5941552个,仅为AlexNet模型参数量的1/10。这主要归功于GoogLeNet创新性地采用了Inception模块。感兴趣的读者可以阅读原始顶会顶刊http://arxiv.org/abs/1409.4842

1.2 GoogLeNet 原始论文

1.2.1 论文标题的来源

有趣的是原始论文的标题《Going deeper with convolutions》来自这个表情包

深度学习(GoogLeNet)

1.2.2 GoogLeNet 提出的原因

从 LeNet 开始,卷积神经网络通常有一个标准范式(结构) – 堆叠卷积层(后跟归一化和最大池化),然后跟一个或多个全连接层,这种模式及其优化后的模式确实在图像分类中取得较好的结果。

但为了获得更好的神经网络性能,这时候我们就需要扩大网络规模(增加网络层数及其宽度),但是更大的规模意味着更多的参数,容易出现过拟合,尤其是在训练集中标记示例的数量有限的情况下。增加网络规模的另一个缺点是计算资源的使用急剧增加

为了解决这两个问题,他们将原本密集连接的神经网络转向稀疏连接的神经网络。Aroar基于模仿生物系统的神经网络模型研究结论提出,一个基于稀疏结构的深度、宽度神经网络经过数据信息的输入后,末端神经节点中反映该数据信息特征的节点将总是处于激活状态。假如要识别一只猫,有些神经元是识别猫眼睛,有些是识别猫鼻子、猫尾巴等,如果图像真的是一只猫,这些特征会一起激活。虽然严格的数学证明需要很强的条件,但事实上这个陈述与赫布理论类似。这表明稀疏连接的神经网络具有一定科学性,但是,计算机软硬件对非均匀稀疏数据的计算效率很差

现在的问题是有没有一种方法,既能保持网络结构的稀疏性,又能利用密集矩阵的高计算性能。大量的文献表明可以将稀疏矩阵聚类为较为密集的子矩阵来提高计算性能,据此论文提出了名为inception的结构来实现此目的。

2 GoogLeNet 创新点

2.1 inception(原生模块)的结构

通过赫布理论与多尺度处理,论文提出了一种多通路 (inception) 的设计方案,即在网络同一层将不同尺寸的卷积核并联放置

深度学习(GoogLeNet)

(1) 将不同尺寸的卷积核放在同一层,意味着有不同大小的感受野,可以提取到不同特征,最后拼接特征,相当于对不同特征进行融合。

(2)之所以卷积核大小采用1、3、5,主要是为了输出维度一致。设定卷积核移动步长stride=1之后,只要分别设定padding=0、1、2,那么卷积之后便可以得到相同尺寸的特征,然后这些特征就可以直接聚合在一起了。

(3)论文表明pooling在实验中是有效的,所以Inception里面也有。
(4)网络越到后面,特征越抽象,而且每个特征所涉及的感受野也更大了,因此随着层数的增加,3×3和5×5卷积的比例也要增加。

深度学习(GoogLeNet)

2.2 inception(原生模块)的优势

多个尺寸上进行卷积再聚合的优势

  • 直观感觉上在多个尺度上同时进行卷积,能提取到不同尺度的特征。特征更为丰富也意味着最后分类判断时更加准确。
  • 利用稀疏矩阵分解成密集矩阵计算的原理可以加快收敛速度。用稀疏的分散的网络,对它们进行汇总,就可以取代一些庞大臃肿的网络。稀疏矩阵的分解示意图如下

 深度学习(GoogLeNet)

这个原理在inception中就是在特征维度上进行分解

传统的卷积层的输入数据只与一种尺度(例如3*3)的卷积核进行卷积,输出固定维度(例如192个特征)的数据。而这192个特征是平均分布在3*3的尺度范围上的,这可以理解为输出了一个稀疏均匀分布的特征集。而在inception模块中在多个尺寸的卷积核下提取特征,输出的192个特征不是均匀分布的,而是相关性强的特征聚合在一起。比如3*3的96个特征聚合在一起,5*5的96个特征聚合在一起,这也可以理解为多个密集分布的子集合。由于相关性强的特征聚合在一起,同时相关性弱的特征就被弱化,因此冗余的数据比较少,也就是数据的有效性非常强,这能够加快收敛速度。

深度学习(GoogLeNet)

换句话说,即神经元同时激活的情况下,将联合评判做出结论!意味着神经元传递信息可能不是简单的复制逐层传递,而是并行的同时传递,这也反映着inception更逼近真实生物系统的神经网络。

2.3 inception(具有降维功能)的结构

简单的多通路拼接会造成通道数迅速增长,导致计算量和复杂度增高。在3*3与5*5的卷积核之前,先经过1*1的卷积核,这样的设置能够把通道数减少。在3*3的池化层之后,用1*1卷积核降维。

深度学习(GoogLeNet)

 因此,我们可以看到具有降维功能的inception的最大创新之处是加入了1*1的卷积核。最终GooLeNet 用的inception 是改进后的具有降维功能的inception。代码如下

class Inception(paddle.nn.Layer):
    def __init__(self, in_channels, c1, c2, c3, c4):
        '''
        c1:第一条支路1x1卷积的输出通道数,数据类型是整数
        c2:第二条支路卷积的输出通道数,数据类型是tuple或list,  
            其中c2[0]是1x1卷积的输出通道数,c2[1]是3x3
        c3: 第三条支路卷积的输出通道数,数据类型是tuple或list,  
            其中c3[0]是1x1卷积的输出通道数,c3[1]是3x3
        c4:第四条支路1x1卷积的输出通道数,数据类型是整数
        '''
        super(Inception, self).__init__()
        #路线1,卷积核1x1
        self.route1x1_1 = paddle.nn.Conv2D(in_channels, c1, kernel_size=1)
        #路线2,卷积层1x1、卷积层3x3
        self.route1x1_2 = paddle.nn.Conv2D(in_channels, c2[0], kernel_size=1)
        self.route3x3_2 = paddle.nn.Conv2D(c2[0], c2[1], kernel_size=3, padding=1)
        #路线3,卷积层1x1、卷积层5x5
        self.route1x1_3 = paddle.nn.Conv2D(in_channels, c3[0], kernel_size=1)
        self.route5x5_3 = paddle.nn.Conv2D(c3[0], c3[1], kernel_size=5, padding=2)
        #路线4,池化层3x3、卷积层1x1
        self.route3x3_4 = paddle.nn.MaxPool2D(kernel_size=3, stride=1, padding=1)
        self.route1x1_4 = paddle.nn.Conv2D(in_channels, c4, kernel_size=1)

    def forward(self, x):
        route1 = F.relu(self.route1x1_1(x))
        route2 = F.relu(self.route3x3_2(F.relu(self.route1x1_2(x))))
        route3 = F.relu(self.route5x5_3(F.relu(self.route1x1_3(x))))
        route4 = F.relu(self.route1x1_4(self.route3x3_4(x)))
        out = [route1, route2, route3, route4]
        return paddle.concat(out, axis=1)  #在通道维度(axis=1)上进行连接

def BasicConv2d(in_channels, out_channels, kernel, stride=1, padding=0):
    layer = paddle.nn.Sequential(
                paddle.nn.Conv2D(in_channels, out_channels, kernel, stride, padding), 
                paddle.nn.BatchNorm2D(out_channels, epsilon=1e-3),
                paddle.nn.ReLU())
    return layer

2.4 1*1卷积核的作用

深度学习(GoogLeNet)

(1)1*1的卷积核相当于对所有特征进行一次相同权重的全连接的计算

(2)减少通道数,而不改变输出的宽度与高度,起到降维的作用,减少计算复杂度

(3)只要最后输出的特征数不变,中间的降维类似于压缩的效果,并不影响最终训练的结果

深度学习(GoogLeNet)

第一种没有1*1卷积核的参数量=256*3*3*256=589824

第二种有1*1卷积核的参数量=256*1*1*32+32*3*3*256=81920

可见大大减少参数量

2.5 GAP

无论是 AlexNet 还是 VGG,最后的卷积层和全连接层相连,参数量是巨大的(占网络参数量的 70-90% 左右)。GoogLeNet 采用了全局平均池化 Global Average Pooling 代替全连接

 深度学习(GoogLeNet)

2.6 两个辅助分类器

通过添加连接到这些中间层的辅助分类器,作用是尽快学到可分类的特征,相当于是起到了一个正则化的作用,防止梯度消失。这些分类器采用较小卷积网络的形式,放置在Inception (原生模块) 和 Inception (具有降维功能)模块的输出之上。

3 GoogLeNet的定义

3.1 GoogLeNet的总体架构

拥有上述9个inception

深度学习(GoogLeNet)

局部响应归一化(Local Response Normalization,LRN)技术是首次在 AlexNet 模型中提出这个概念

深度学习(GoogLeNet)

3.2 GoogLeNet的变种

本篇文章主要讲解inception v1,改进的版本这里不再赘述

深度学习(GoogLeNet)

3.3 GoogLeNet的完整代码定义

#构建模型
class Inception(paddle.nn.Layer):
    def __init__(self, in_channels, c1, c2, c3, c4):
        '''
        c1:第一条支路1x1卷积的输出通道数,数据类型是整数
        c2:第二条支路卷积的输出通道数,数据类型是tuple或list,  
            其中c2[0]是1x1卷积的输出通道数,c2[1]是3x3
        c3: 第三条支路卷积的输出通道数,数据类型是tuple或list,  
            其中c3[0]是1x1卷积的输出通道数,c3[1]是3x3
        c4:第四条支路1x1卷积的输出通道数,数据类型是整数
        '''
        super(Inception, self).__init__()
        #路线1,卷积核1x1
        self.route1x1_1 = paddle.nn.Conv2D(in_channels, c1, kernel_size=1)
        #路线2,卷积层1x1、卷积层3x3
        self.route1x1_2 = paddle.nn.Conv2D(in_channels, c2[0], kernel_size=1)
        self.route3x3_2 = paddle.nn.Conv2D(c2[0], c2[1], kernel_size=3, padding=1)
        #路线3,卷积层1x1、卷积层5x5
        self.route1x1_3 = paddle.nn.Conv2D(in_channels, c3[0], kernel_size=1)
        self.route5x5_3 = paddle.nn.Conv2D(c3[0], c3[1], kernel_size=5, padding=2)
        #路线4,池化层3x3、卷积层1x1
        self.route3x3_4 = paddle.nn.MaxPool2D(kernel_size=3, stride=1, padding=1)
        self.route1x1_4 = paddle.nn.Conv2D(in_channels, c4, kernel_size=1)

    def forward(self, x):
        route1 = F.relu(self.route1x1_1(x))
        route2 = F.relu(self.route3x3_2(F.relu(self.route1x1_2(x))))
        route3 = F.relu(self.route5x5_3(F.relu(self.route1x1_3(x))))
        route4 = F.relu(self.route1x1_4(self.route3x3_4(x)))
        out = [route1, route2, route3, route4]
        return paddle.concat(out, axis=1)  #在通道维度(axis=1)上进行连接

def BasicConv2d(in_channels, out_channels, kernel, stride=1, padding=0):
    layer = paddle.nn.Sequential(
                paddle.nn.Conv2D(in_channels, out_channels, kernel, stride, padding), 
                paddle.nn.BatchNorm2D(out_channels, epsilon=1e-3),
                paddle.nn.ReLU())
    return layer

class GoogLeNet(paddle.nn.Layer):
    def __init__(self, in_channel, num_classes):
        super(GoogLeNet, self).__init__()
        self.b1 = paddle.nn.Sequential(
                    BasicConv2d(in_channel, out_channels=64, kernel=7, stride=2, padding=3),
                    paddle.nn.MaxPool2D(3, 2))
        self.b2 = paddle.nn.Sequential(
                    BasicConv2d(64, 64, kernel=1),
                    BasicConv2d(64, 192, kernel=3, padding=1),
                    paddle.nn.MaxPool2D(3, 2))
        self.b3 = paddle.nn.Sequential(
                    Inception(192, 64, (96, 128), (16, 32), 32),
                    Inception(256, 128, (128, 192), (32, 96), 64),
                    paddle.nn.MaxPool2D(3, 2))
        self.b4 = paddle.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),
                    paddle.nn.MaxPool2D(3, 2))
        self.b5 = paddle.nn.Sequential(
                    Inception(832, 256, (160, 320), (32, 128), 128),
                    Inception(832, 384, (182, 384), (48, 128), 128),
                    paddle.nn.AvgPool2D(2))
        self.flatten=paddle.nn.Flatten()
        self.b6 = paddle.nn.Linear(1024, num_classes)
        
    def forward(self, x):
        x = self.b1(x)
        x = self.b2(x)
        x = self.b3(x)
        x = self.b4(x)
        x = self.b5(x)
        x = self.flatten(x)
        x = self.b6(x)
        return x

4 构建GoogLeNet做CIFAR图像识别

自行调整超参数

import paddle
import paddle.nn.functional as F
import numpy as np
from paddle.vision.transforms import Compose, Resize, Transpose, Normalize

t = Compose([Resize(size=96),Normalize(mean=[127.5, 127.5, 127.5], std=[127.5, 127.5, 127.5], data_format='HWC'),Transpose()]) #数据转换

cifar10_train = paddle.vision.datasets.cifar.Cifar10(mode='train', transform=t, backend='cv2')
cifar10_test = paddle.vision.datasets.cifar.Cifar10(mode="test", transform=t, backend='cv2')

#构建模型
class Inception(paddle.nn.Layer):
    def __init__(self, in_channels, c1, c2, c3, c4):
        '''
        c1:第一条支路1x1卷积的输出通道数,数据类型是整数
        c2:第二条支路卷积的输出通道数,数据类型是tuple或list,  
            其中c2[0]是1x1卷积的输出通道数,c2[1]是3x3
        c3: 第三条支路卷积的输出通道数,数据类型是tuple或list,  
            其中c3[0]是1x1卷积的输出通道数,c3[1]是3x3
        c4:第四条支路1x1卷积的输出通道数,数据类型是整数
        '''
        super(Inception, self).__init__()
        #路线1,卷积核1x1
        self.route1x1_1 = paddle.nn.Conv2D(in_channels, c1, kernel_size=1)
        #路线2,卷积层1x1、卷积层3x3
        self.route1x1_2 = paddle.nn.Conv2D(in_channels, c2[0], kernel_size=1)
        self.route3x3_2 = paddle.nn.Conv2D(c2[0], c2[1], kernel_size=3, padding=1)
        #路线3,卷积层1x1、卷积层5x5
        self.route1x1_3 = paddle.nn.Conv2D(in_channels, c3[0], kernel_size=1)
        self.route5x5_3 = paddle.nn.Conv2D(c3[0], c3[1], kernel_size=5, padding=2)
        #路线4,池化层3x3、卷积层1x1
        self.route3x3_4 = paddle.nn.MaxPool2D(kernel_size=3, stride=1, padding=1)
        self.route1x1_4 = paddle.nn.Conv2D(in_channels, c4, kernel_size=1)

    def forward(self, x):
        route1 = F.relu(self.route1x1_1(x))
        route2 = F.relu(self.route3x3_2(F.relu(self.route1x1_2(x))))
        route3 = F.relu(self.route5x5_3(F.relu(self.route1x1_3(x))))
        route4 = F.relu(self.route1x1_4(self.route3x3_4(x)))
        out = [route1, route2, route3, route4]
        return paddle.concat(out, axis=1)  #在通道维度(axis=1)上进行连接

def BasicConv2d(in_channels, out_channels, kernel, stride=1, padding=0):
    layer = paddle.nn.Sequential(
                paddle.nn.Conv2D(in_channels, out_channels, kernel, stride, padding), 
                paddle.nn.BatchNorm2D(out_channels, epsilon=1e-3),
                paddle.nn.ReLU())
    return layer

class GoogLeNet(paddle.nn.Layer):
    def __init__(self, in_channel, num_classes):
        super(GoogLeNet, self).__init__()
        self.b1 = paddle.nn.Sequential(
                    BasicConv2d(in_channel, out_channels=64, kernel=7, stride=2, padding=3),
                    paddle.nn.MaxPool2D(3, 2))
        self.b2 = paddle.nn.Sequential(
                    BasicConv2d(64, 64, kernel=1),
                    BasicConv2d(64, 192, kernel=3, padding=1),
                    paddle.nn.MaxPool2D(3, 2))
        self.b3 = paddle.nn.Sequential(
                    Inception(192, 64, (96, 128), (16, 32), 32),
                    Inception(256, 128, (128, 192), (32, 96), 64),
                    paddle.nn.MaxPool2D(3, 2))
        self.b4 = paddle.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),
                    paddle.nn.MaxPool2D(3, 2))
        self.b5 = paddle.nn.Sequential(
                    Inception(832, 256, (160, 320), (32, 128), 128),
                    Inception(832, 384, (182, 384), (48, 128), 128),
                    paddle.nn.AvgPool2D(2))
        self.flatten=paddle.nn.Flatten()
        self.b6 = paddle.nn.Linear(1024, num_classes)
        
    def forward(self, x):
        x = self.b1(x)
        x = self.b2(x)
        x = self.b3(x)
        x = self.b4(x)
        x = self.b5(x)
        x = self.flatten(x)
        x = self.b6(x)
        return x

epoch_num = 20
batch_size = 256
learning_rate = 0.001

val_acc_history = []
val_loss_history = []

def train(model):
    #启动训练模式
    model.train()

    opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=model.parameters())
    train_loader = paddle.io.DataLoader(cifar10_train, shuffle=True, batch_size=batch_size)
    valid_loader = paddle.io.DataLoader(cifar10_test, batch_size=batch_size)

    for epoch in range(epoch_num):
        for batch_id, data in enumerate(train_loader()):
            x_data = paddle.cast(data[0], 'float32')
            y_data = paddle.cast(data[1], 'int64')
            y_data = paddle.reshape(y_data, (-1, 1))

            y_predict = model(x_data)
            loss = F.cross_entropy(y_predict, y_data)
            loss.backward()
            opt.step()
            opt.clear_grad()
        
        print("训练轮次: {}; 损失: {}".format(epoch, loss.numpy()))

        #启动评估模式
        model.eval()
        accuracies = []
        losses = []
        for batch_id, data in enumerate(valid_loader()):
            x_data = paddle.cast(data[0], 'float32')
            y_data = paddle.cast(data[1], 'int64')
            y_data = paddle.reshape(y_data, (-1, 1))

            y_predict = model(x_data)
            loss = F.cross_entropy(y_predict, y_data)
            acc = paddle.metric.accuracy(y_predict, y_data)
            accuracies.append(np.mean(acc.numpy()))
            losses.append(np.mean(loss.numpy()))

        avg_acc, avg_loss = np.mean(accuracies), np.mean(losses)
        print("评估准确度为:{};损失为:{}".format(avg_acc, avg_loss))
        val_acc_history.append(avg_acc)
        val_loss_history.append(avg_loss)
        model.train()

model = GoogLeNet(3, 10)
train(model)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170454.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 什么是HEVC?解释了高效视频编码,H.265和4K压缩

    什么是HEVC?解释了高效视频编码,H.265和4K压缩高效视频编码(HEVC),也称为H.265,可以通过蓝光最佳视频压缩方法实现两倍的压缩。但它是如何工作的,是否足以让我们看到更好看的4K内容? 我想称之为H.265,因为它听起来很酷,但它的全称是高效视频编码(HEVC)。它是高级视频编码(AVC)的新继承者,也称为H.264,它是蓝光使用的压缩方案之一。HEVC的想法是提…

  • linux单引号双引号反引号_unix和linux的区别

    linux单引号双引号反引号_unix和linux的区别特殊的赋值Shell中可以将数字或字符直接赋予变量,也可以将Linux命令的执行结果赋予变量,如下:(1)$count=9#将数字赋予变量count(2)$name=”ming”#将字符赋予变量name(3)$listc=`ls-la`#将Linux命令赋予listc,listc的值就是该命令的执行结果反引号的作用反引号的作用就是将反引号内的Lin…

    2022年10月25日
  • jdk环境变量的配置[通俗易懂]

    jdk环境变量的配置[通俗易懂]jdk下载并配置下载jdk下图是自己资源管理器中jdk的安装路径,双击然后next就好,不需要改什么配置手里没有安装包的,下载地址在这里:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html下面主要讲怎么配置Java的环境变量,也是为了以后哪天自己忘记了做个备份1.在电脑桌面右键点击“此电脑”的“属性”选项2.选择“高级系统设置”选项…

  • java关于日期的运算等处理方法

    java关于日期的运算等处理方法

  • 光流法:Farneback

    光流法:Farneback光流法:Farnback光流法:Farnback基本假设Farneback光流法图像模型位移估计Reference现实世界中,万物都在在运动,且运动的速度和方向可能均不同,这就构成了运动场。物体的运动投影在图像上反应的是像素的移动。这种像素的瞬时移动速度就是光流。光流法是利用图像序列中的像素在时间域上的变化、相邻帧之间的相关性来找到的上一帧跟当前帧间存在的…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号