移动测试开发 卷积神经网络的常用改进

opentest-oper@360.cn · 2020年08月06日 · 1476 次阅读

背景

图片类任务经常使用卷积神经网络,网络结构中经常是使用全链接作为输出层,来实现分类或者回归。
全链接层的好处:由于其参数量级大,模型的拟合能力更强。

坏处:
① 对于数据的尺寸要求是固定的,因此我们有时需要 resize 图片,导致变形或者剪切损失图片信息
② 模型的参数基本都集中于全链接层,因此预测时间主要被全链接占用,实时性要求高的模型会有影响
③ 显存占用高
因此当我们关注实时性,可以适当牺牲准确性(卷积层复杂可以弥补准确性),并且输入数据尺寸是变化的时候,我们应该怎么做呢?

方法
去掉全链接层,使用全卷积神经网络,1*1 卷积层控制输出尺寸
靠近输出层的卷积层整体 maxpooling,1*1 卷积层控制输出尺寸

代码

我们以 mnist 作为例子:
全链接模型:
通过 d2 控制输出的 tensor 是 1*10

全链接

class DENSE_MNIST_MODEL(tf.keras.Model):
    def __init__(self):
        super(DENSE_MNIST_MODEL, self).__init__()
        self.conv1 = Conv2D(32, 3, activation='relu')
        self.flatten = Flatten()
        self.d1 = Dense(128, activation='relu')
        self.d2 = Dense(10, activation='softmax')

    def call(self, x):
        x = self.conv1(x)
        x = self.flatten(x)
        x = self.d1(x)
        return self.d2(x)

模型参数:

Model: "dense_mnist_model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) multiple 320
_________________________________________________________________
flatten (Flatten) multiple 0
_________________________________________________________________
dense (Dense) multiple 2769024
_________________________________________________________________
dense_1 (Dense) multiple 1290
=================================================================
Total params: 2,770,634
Trainable params: 2,770,634
Non-trainable params: 0

全卷积模型:

输入图片是 28*28,经过两个 maxpooling,步长是 2,变成了 7*7 大小,经过 7*7 卷积,padding=valid,则只保留了 channel 上的特征。再经过 1*1 卷积,控制 channel 为 10

全卷积

class FCN_MNIST_MODEL(tf.keras.Model):

    def __init__(self):
        super(FCN_MNIST_MODEL, self).__init__()
        self.conv1 = Conv2D(16, 3, 1, padding="same")
        self.conv2 = Conv2D(32, 3, 1, padding="same")
        self.conv3 = Conv2D(32, 7, 1, padding="valid")
        self.conv4 = Conv2D(10, 1, 1, padding="same")
        self.maxpool2d = MaxPool2D(2, 2, padding="valid")

    def call(self, inputs=(None, 28, 28, 1)):
        __output = self.conv1(inputs)
        __output = self.maxpool2d(__output)
        __output = self.conv2(__output)
        __output = self.maxpool2d(__output)
        __output = self.conv3(__output)
        __output = self.conv4(__output)
        shape = __output.shape
        __output = tf.squeeze(__output, axis=[1,2])
        return tf.nn.softmax(__output) 

模型参数:

Model: "fcn_mnist_model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) multiple 160
_________________________________________________________________
conv2d_1 (Conv2D) multiple 4640
_________________________________________________________________
conv2d_2 (Conv2D) multiple 50208
_________________________________________________________________
conv2d_3 (Conv2D) multiple 330
_________________________________________________________________
max_pooling2d (MaxPooling2D) multiple 0
=================================================================
Total params: 55,338
Trainable params: 55,338
Non-trainable params: 0

可变尺度模型:

通过卷积、maxpooling、avgpooling 将过程中的 tensor 变成 1*1*channel 尺寸,再通过 1*1 卷积,控制 channel 为 10。
注意同一个 batch 里的数据需要是同样的尺寸,我们都会进行 pad,所以常用做法是将尺度相近的图片放到同一个 batch 里,padding 后变成同一个尺寸。
这里选择是对靠近输出的卷积层做整层的 maxpooling。

class ARBITRARY_MNIST_MODEL(tf.keras.Model):

    def __init__(self):
        super(ARBITRARY_MNIST_MODEL, self).__init__()
        self.conv1 = Conv2D(16, 3, 1, padding="same")
        self.conv2 = Conv2D(32, 3, 1, padding="same")
        self.conv3 = Conv2D(10, 1, 1, padding="same")

    def call(self, inputs):
        __output = self.conv1(inputs)
        __output = self.conv2(__output)
        __output = tf.nn.max_pool2d(__output, __output.shape[1:3], 1, padding="VALID") # 这里也可以使用GlobalMaxPooling2D,再经过expand_dims变成四维
        __output = self.conv3(__output)
        __output = tf.squeeze(__output, axis=[1,2])
        return tf.nn.softmax(__output)

模型参数:

Model: "arbitrary_mnist_model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) multiple 160
_________________________________________________________________
conv2d_1 (Conv2D) multiple 4640
_________________________________________________________________
conv2d_2 (Conv2D) multiple 330
=================================================================
Total params: 5,130
Trainable params: 5,130
Non-trainable params: 0

数据对比

全链接模型(浅色的线是原始数据,深色的是平滑后的数据,周期只有 10)
损失值尾部上扬,与周期短和没有添加正则化方法有关,正则化不在本文讨论范围内。

全卷积模型

可变尺寸模型
10 个周期不够,准确度和损失还没有收敛,但是能大致看出趋势。

模型 参数量 Accuracy Loss
Dense 2,770,634 98.3% 0.068
FNC 55,338 98.2% 0.057
可变尺寸 5,130 90.7% 0.29

总结

落地部署中,我们不仅关注准确度,可能更关心响应时间,因此模型不能太复杂,全卷积是一个很好的思路。
如果我们的数据,包括图片、文本、音频等,如果尺寸变化幅度比较大,是否可以考虑将样本尺寸相近的数据放到同一个 batch,网络中不使用全链接,实现动态尺度模型。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册