TF14——InceptionNet
TF14——InceptionNet
经典卷积网络

VInceptionNet诞生于2014年,当年ImageNet竞赛冠军,Top5错误率为6.67%,InceptionNet引入了Inceoption结构块,在同一层网络结构内使用不同尺寸的卷积核,提升了模型的感知力,使用了批标准化缓解了梯度消失
Inception结构块
InceptionNet的核心是它的基本单元Inception结构块

无论是GoogLeNet也就是Inception v1,还是inceptionNet的后续版本,比如v2、v3、v4都是基于Inception结构块搭建的网络
Inception结构快在用一层网络中,使用了多个尺寸的卷积核,可以提取不同尺寸的特征
- 通过
1*1卷积核,作用到输入特征图的每个像素点,通过设定少于输入特征图深度的1*1卷积核个数,减少了输出特征图深度,起到了降维的作用,减少了参数量和计算量 - Inception结构块包含四个分支,分别是
- 经过
1*1卷积核输出到卷积连接器, - 经过
1*1卷积核匹配3*3卷积核输出到卷积连接器, - 经过
1*1卷积核匹配5*5卷积核输出到卷积连接器, - 经过
3*3最大池化核配合1*1卷积核输出到卷积连接器,
- 经过
卷积连接器会把收到的这四路特征数据按深度方向拼接形成Inception结构块的输出

把这个Inception结构块用CBAPD描述出来,用颜色对应上Inception结构块内的操作

- 第一分支卷积采用16个
1*1卷积核,步长为1,全零填充采用BN操作,relu激活函数 - 第二分支卷积采用16个
1*1卷积核降维,步长为1,全零填充采用BN操作,relu激活函数,采用16个3*3卷积核,步长为1,全零填充,采用BN操作,relu激活函数 - 第三分支卷积采用16个
1*1卷积核降维,步长为1,全零填充采用BN操作,relu激活函数,采用16个5*5卷积核,步长为1,全零填充,采用BN操作,relu激活函数 - 第四分支先采用最大池化,池化核尺寸是
3*3,步长为1,全零填充,采用16个1*1卷积核降维,步长为1,全零填充,采用BN操作,relu激活函数
卷积连接器,把这四个分支按照深度方向堆叠在一起,构成Inception结构块的输出,由于Inception结构块中的卷积操作均采用了CBA结构,所以将其定义乘一个新的类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):
x=self.model(x)
return x
这样实现Inception结构块

- c1是第一个分支,使用ConvBNRelu卷积操作
- c2_1,c2_2是第二个分支,使用两次ConvBNRelu卷积操作
- c3_1,c3_2是第三个分支,使用两次ConvBNRelu卷积操作
- p4_1,p4_2是第四个分支,使用最大池化和ConvBNRelu卷积操作
- x1,x2_2,x3_2,x4_2是四个分支的输出,使用tf.concat函数将他们呢堆叠在一起,axis=3指定堆叠的维度是沿深度方向
tf.concat([tensor1,tensor2,tensor3,...],axis)
用来拼接张量,按哪个维度拼接
有了Inception结构快后,就可以搭建一个精简版的InceptionNet

源码:p40_cifar10_inception10.py
class Inception10(Model):
def __init__(self, num_blocks, num_classes, init_ch=16, **kwargs):
super(Inception10, self).__init__(**kwargs)
self.in_channels = init_ch
self.out_channels = init_ch
self.num_blocks = num_blocks
self.init_ch = init_ch
self.c1 = ConvBNRelu(init_ch)
self.blocks = tf.keras.models.Sequential()
for block_id in range(num_blocks):
for layer_id in range(2):
if layer_id == 0:
block = InceptionBlk(self.out_channels, strides=2)
else:
block = InceptionBlk(self.out_channels, strides=1)
self.blocks.add(block)
# enlarger out_channels per block
self.out_channels *= 2
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
model = Inception10(num_blocks=2, num_classes=10)
-
第一层采用
3*3卷积核,步长为1,全零填充,采用BN操作,relu激活,定义类Inception10时,设定了默认init_ch=16,也就是默认输出深度是16,定义ConvBNRelu类时,定义了默认卷积核边长是3,步长是1,全零填充,所以这里直接调用ConBNRelu就可以 -
随后是4个Inception结构块顺序相连,每两个Inception结构块组成一个block,每个block中的第一个Inception结构块,卷积步长是2,第二个Inception块,卷积步长是1,这使得第一个Inception结构块输出特征图尺寸减半,因此我们把输出特征图深度加深,尽可能保证抽取中信息的承载量一致
-
block_0设置的通道数是16,经过了四个分支,输出的深度为4*16=64,在这里给通道数加倍了,所以block_1通道数是block_0通道数的两倍是32
-
同样经过了四个分支,输出深度是4*32=128,这128个通道数据会被送入平均池化,送入10个分类的全连接
-
最后实例化了Inception10类,指定了inceptionNet的block数是2,也就是block_0和block_1,num_classes=10这里指定了网络是几分类,因为cifar10数据集是十分类, 所以这里是10
ps:由于现在的网络规模比较大,把batch_size调整到1024,让训练时,一次喂入神经网络的数据量多一些,以充分发挥显卡的性能,提高训练速度