前言
自13年以后出现的inception体系的网络,其主要的目的是认为识别物体所占图像的占比不同,所适合的卷积核大小不同,如下图所示,这是经典的inception V1的结构,其主要采用了大小为3,5,1的卷积核,但是这种做法也有一些粗暴,因为在设计网络的时候,不同卷积核的通道数是固定的,很难适应多种算法。
那么能否有一种网络模块,帮助我们针对于目标物不同占比的图像,选用不同的卷积核,答案是有的,2019CVPR的SKnet就是来解决这种问题的。
SKnet的基本结构如图所示,主要分为三个部分,分离,融合和选择,其中融合(红框内)采用了SEnet的思想,压缩感知。分离就是在同一层中采用不同大小卷积核,选择就选择不同大小卷积核所占的比重
代码
本节主要根据公式复现SKnet结构
其中的两个U都是X通过卷积得到的,实现方法:
############Spilt
U1=tf.layers.conv2d(x,channel,kernel1,strides=1, padding='same')
U2=tf.layers.conv2d(x,channel,kernel2,strides=1, padding='same')
############Fuse
U=U1+U2
这个公式主要是GAP,这里只用一行代码就可以实现:
S=tf.keras.layers.GlobalAvgPool2D()(U)
这个公式有batcnorm和relu函数,其中Ws的这个公式我们用1*1的卷积来代替:
S=tf.reshape(S,[-1,1,1,channel])
print(S)
Z=tf.nn.relu(tf.layers.batch_normalization(tf.layers.conv2d(S,32,1,strides=1, padding='same'),axis=-1,momentum=0.99,epsilon=0.001, center=True, scale=True,))
print(Z)
其中d的维度我们和文章选择一样,为32.
最后的softmax的实现比较巧妙采用了现conbine,然后在split的方案:
a=tf.layers.conv2d(Z,channel,1,strides=1, padding='same')
b=tf.layers.conv2d(Z,channel,1,strides=1, padding='same')
print(a,b)
combine=tf.concat([a,b],1)
print(combine)
combine=tf.nn.softmax(combine,axis=1)
print(combine)
a,b=tf.split(combine,num_or_size_splits=2, axis=1)
print(a,b)
完整代码以及结果
import tensorflow as tf
x = tf.placeholder(tf.float32,[None, 224, 224, 3])#输入图片大小
def SK_block(x,kernel1,kernel2,channel):
############Spilt
U1=tf.layers.conv2d(x,channel,kernel1,strides=1, padding='same')
U2=tf.layers.conv2d(x,channel,kernel2,strides=1, padding='same')
############Fuse
U=U1+U2
S=tf.keras.layers.GlobalAvgPool2D()(U)
print(S)
S=tf.reshape(S,[-1,1,1,channel])
print(S)
Z=tf.nn.relu(tf.layers.batch_normalization(tf.layers.conv2d(S,32,1,strides=1, padding='same'),axis=-1,momentum=0.99,epsilon=0.001, center=True, scale=True,))
print(Z)
a=tf.layers.conv2d(Z,channel,1,strides=1, padding='same')
b=tf.layers.conv2d(Z,channel,1,strides=1, padding='same')
print(a,b)
combine=tf.concat([a,b],1)
print(combine)
combine=tf.nn.softmax(combine,axis=1)
print(combine)
a,b=tf.split(combine,num_or_size_splits=2, axis=1)
print(a,b)
V=a*U1+b*U2
print(V)
return V
layer1=tf.layers.conv2d(x,256,3,strides=1, padding='same')
layer1=tf.nn.relu(layer1)
SK_block(layer1,3,5,256)
输出:
Tensor("global_average_pooling2d_19/Mean:0", shape=(?, 256), dtype=float32)
Tensor("Reshape_18:0", shape=(?, 1, 1, 256), dtype=float32)
Tensor("Relu_32:0", shape=(?, 1, 1, 32), dtype=float32)
Tensor("conv2d_96/BiasAdd:0", shape=(?, 1, 1, 256), dtype=float32) Tensor("conv2d_97/BiasAdd:0", shape=(?, 1, 1, 256), dtype=float32)
Tensor("concat_18:0", shape=(?, 2, 1, 256), dtype=float32)
Tensor("transpose_11:0", shape=(?, 2, 1, 256), dtype=float32)
Tensor("split_3:0", shape=(?, 1, 1, 256), dtype=float32) Tensor("split_3:1", shape=(?, 1, 1, 256), dtype=float32)
Tensor("add_21:0", shape=(?, 224, 224, 256), dtype=float32)