DL with python(16)——tensorflow实现InceptionNet(GoogLeNet)

本文涉及到的是中国大学慕课《人工智能实践:Tensorflow笔记》第五讲第14节的内容,对tensorflow环境下经典卷积神经网络的搭建进行介绍,其基础是DL with python(14)——tensorflow实现CNN的“八股”中的代码,将其中第三步的代码替换为本文中的代码均可直接运行,其他部分无需改变。

经典的卷积神经网络有以下几种,这里介绍结构较为复杂的InceptionNet,其实现的方法也相对困难。
在这里插入图片描述

InceptionNet

InceptionNet 即 GoogLeNet,诞生于2014年,是当年ImageNet竞赛冠军,Top5错误率为6.67%。

Szegedy C, Liu W, Jia Y, et al. Going Deeper with Convolutions. In CVPR, 2015.

主要优点:一层内使用不同尺寸的卷积核,提升感知力(通过 padding 实现输出特征面积一致);使用 1 * 1 卷积核,改变输出特征 channel 数(减少网络参数)。

InceptionNet旨在通过增加网络的宽度来提升网络的能力,与 VGGNet 通过卷积层堆叠的方式(纵向)相比,是一个不同的方向(横向)。InceptionNet 模型的构建与 VGGNet 及之前的网络有所区别,不再是简单的纵向堆叠,要理解 InceptionNet 的结构,首先要理解它的基本单元,如下图所示。
DL with python(16)——tensorflow实现InceptionNet(GoogLeNet)_第1张图片
一个基本单元内部的结构如下。可以看到,InceptionNet 的基本单元中,卷积部分是比较统一的 C、B、A 典型结构,即卷积→BN→激活,激活均采用 Relu 激活函数,同时包含最大池化操作。
DL with python(16)——tensorflow实现InceptionNet(GoogLeNet)_第2张图片

网络搭建的步骤和代码

明白了基本结构,就可以进行网络的搭建了。InceptionNet的搭建主要分为三步:
1,构建一个ConvBNRelu类,包含卷积、批归一化、激活三个层,即CBA组合;
2,利用ConvBNRelu类构建Inception结构块,即网络的基本单元;
3,利用Inception结构块和其他网络层构建Inception Net;

第一步

在 Tensorflow 框架下利用 Keras 构建 InceptionNet 模型时,可以将 C、B、A 结构封装在一起,定义成一个新的 ConvBNRelu 类,以减少代码量,同时更便于阅读。

class ConvBNRelu(Model):
    def __init__(self, ch, kernelsz=3, strides=1, padding='same'): # 设置默认卷积核,步长,填充
        super(ConvBNRelu, self).__init__()
        self.model = tf.keras.models.Sequential([
            Conv2D(ch, kernelsz, strides=strides, padding=padding), # 卷积层
            BatchNormalization(), # 批归一化层
            Activation('relu')    # 激活函数层
        ])

    def call(self, x):  # 调用class类生成一个结构体
        x = self.model(x, training=False) #在training=False时,BN通过整个训练集计算均值、方差去做批归一化,training=True时,通过当前batch的均值、方差去做批归一化。推理时 training=False效果好
        return x

其中参数 ch 代表特征图的通道数,也即卷积核个数;kernelsz 代表卷积核尺寸;strides 代表卷积步长;padding 代表是否进行全零填充。

第二步

开始构建 InceptionNet 的基本单元,同样利用 class 定义的
方式,定义一个新的 InceptionBlk 类,利用这个类生成一个Inception结构块,即基本单元。

## 用ConvBNRelu类实现Inception结构块
class InceptionBlk(Model):
    def __init__(self, ch, strides=1):  # 默认步长为1
        super(InceptionBlk, self).__init__()
        self.ch = ch    # 卷积核大小
        self.strides = strides  # 步长
        self.c1 = ConvBNRelu(ch, kernelsz=1, strides=strides) # 第一个分支,括号内为参数
        self.c2_1 = ConvBNRelu(ch, kernelsz=1, strides=strides) # 第二个分支
        self.c2_2 = ConvBNRelu(ch, kernelsz=3, strides=1)
        self.c3_1 = ConvBNRelu(ch, kernelsz=1, strides=strides) # 第三个分支
        self.c3_2 = ConvBNRelu(ch, kernelsz=5, strides=1)
        self.p4_1 = MaxPool2D(3, strides=1, padding='same')    #第四个分支,最大池化+CBA
        self.c4_2 = ConvBNRelu(ch, kernelsz=1, strides=strides)

    def call(self, x):
        x1 = self.c1(x)
        x2_1 = self.c2_1(x)
        x2_2 = self.c2_2(x2_1)
        x3_1 = self.c3_1(x)
        x3_2 = self.c3_2(x3_1)
        x4_1 = self.p4_1(x)
        x4_2 = self.c4_2(x4_1)
        # 将四个分支的输出堆叠在一起,axis=3表示按照深度堆叠
        x = tf.concat([x1, x2_2, x3_2, x4_2], axis=3)
        return x

第三步

最后一步,利用前面的两步作为基础,搭配其他网络层,搭建10层的InceptionNet。具体内容见代码和注释:

## 利用Inception结构块搭建网络
class Inception10(Model):
    def __init__(self, num_blocks, num_classes, init_ch=16, **kwargs):  # 默认输出深度是16
        super(Inception10, self).__init__(**kwargs)
        self.in_channels = init_ch    # 输入的深度
        self.out_channels = init_ch   # 输出的深度
        self.num_blocks = num_blocks  # block的数量,每个含有2个结构块
        self.init_ch = init_ch        # 为了下一行调用init_ch
        self.c1 = ConvBNRelu(init_ch) # 第一个独立的卷积层

        self.blocks = tf.keras.models.Sequential()  # 构建网络中的block
        for block_id in range(num_blocks):  # 构建num_blocks个block
            for layer_id in range(2):   # 一个block中含有2个Inception结构块
                if layer_id == 0:       # 对于第一个Inception结构块
                    block = InceptionBlk(self.out_channels, strides=2)  # 通道数为前面定义的16,卷积步长为2,输出特征尺寸减半
                else:
                    block = InceptionBlk(self.out_channels, strides=1)  # 卷积步长为1
                self.blocks.add(block)
            # enlarger out_channels per block
            self.out_channels *= 2  #输出特征图的深度乘以2,第二个block的通道数为32

        self.p1 = GlobalAveragePooling2D()  # 全局池化层
        self.f1 = Dense(num_classes, activation='softmax') # 输出层

    def call(self, x):
        x = self.c1(x)
        x = self.blocks(x)
        x = self.p1(x)
        y = self.f1(x)
        return y

# 实例化网络,制定了block数是2,是一个10分类网络
model = Inception10(num_blocks=2, num_classes=10)  # 两个参数传递给Inception10的相应参数

运行结果

将以上三段代码依次连接起来,就是10层InceptionNet的搭建代码。因为网络较为复杂,且处理的数据过多,可以将小批量改为1024再运行程序,但是一般的电脑也会运行一阵子,CPU的利用率也会较高。

你可能感兴趣的:(Python深度学习,深度学习,python)