一.引言
通过函数式 API,前面已经实现了多输入与多输出的模型,除此之外,函数式 API 还可以实现具有复杂内部拓扑结构的网络。 Keras 中的神经网络可以是层组成的有向无环图,无环代表图不能有循环,即张量 x 不能成为生成 x 的某一层的输入,但是允许循环连接,即循环层的内部循环。
二.Inception
1.模型结构
Inception 是一种流行的卷积神经网络的架构类型,传统的深度卷积模型一味追求深度,虽然模型尺寸很大,但是对应的梯度消失,梯度爆炸,训练时间过长等问题还是比较明显,Inception 是模块的堆叠,这些模块大都是小型的独立网络,被分为多个并行分支。Inception 模块常包含 3-4 个分支,首先是 1x1 的卷积,然后是 3x3 的卷积,最后将得到的结果结合在一起进行后续的任务,这种设置有助于网络分别学习空间特征和逐通道的特征,比两种特征同时进入复杂深度模型学习更加有效。这里一方面觉得思想有点类似集成学习,即利用多个学习器的结果,不同的是集成学习是投票法,这里是综合所有的结果随后进行后续的推断;另一方面和 wide&deep / deepFm 比较相似,1x1 与 3x3 卷积学习输入样本的不同维度特征,类似于通过线性和深度学习原始特征的低阶与高阶特征。如下模型结构来自 InceptionV3 :
from tensorflow.keras.applications.inception_v3 import InceptionV3

Tips: 1x1 卷积的使用 :
卷积能够在输入张量的每个方块周围提取空间图块,并对所有图块应用相同的变换。极端情况下是提取的图块只包含一个方块,这时卷积相当于对方块所处通道的向量经过一个 Dense 层,它计算得到的信息能够将输入张量通道中的信息混合在一起,所以也有人说1x1 卷积可以起到降维/升维的效果,这取决于 Dense 层的大小与原始数据通道的数量。 1x1 卷积又叫逐点卷积,如上所述它有助于区分通道与空间的特征学习,这建立在如下假设上:每个通道在跨越空间是高度自相关的,不同通道之间可能并不高度相关。以 RGB 图片为例,图像分为 R-G-B 三个通道,每个通道跨越空间高度自相关可以理解为单一通道上一个像素的周围像素点的值都与该中心像素有一定关联,这与我们的视觉体验相符,因为大部分图像的像素总是连续的,如果发生突变可以理解为这里是图像识别中的一个分界或者是一个边缘;不同通道之间可能并不高度相关可以理解为 R-G-B 三个颜色通道的像素值相关性不大,因为 1x1 卷积是为了学习通道的特征,如果一个通道内的值高度相关,那就没有必要混合在一起学习,像素值 R = x+1,G = 2 * (x+1),B = 3 * (x+1) 具有高度相关性,因为它们是倍数关系,如果有一定的先验信息则无需对共线性很强的通道数据进行过多的学习,只需要学习一个通道数据即可。
2.模型构建
参照 InceptionV3 的四个卷积层结构,这里依次构造 branch_a 到 branch_d,最终拼接并 Flatten 得到一个全连接层,随后做后续推断 ,为了提高训练效率,这里训练数据假设为具有十个类别的 64 x 64 x 3 的三通道 RGB 图像 :
# Inception 模块
num_samples = 10000
test_samples = 100
input = layers.Input(shape=(64, 64, 3), dtype='float32')
branch_a = layers.Conv2D(128, 1, activation='relu', strides=2)(input)
branch_b = layers.Conv2D(128, 1, activation='relu')(input)
branch_b = layers.Conv2D(128, 1, activation='relu', strides=2)(branch_b)
branch_c = layers.AveragePooling2D(3, strides=2, padding='same')(input)
branch_c = layers.Conv2D(128, 3, activation='relu', padding='same')(branch_c)
branch_d = layers.Conv2D(128, 1, activation='relu')(input)
branch_d = layers.Conv2D(128, 1, activation='relu')(branch_d)
branch_d = layers.Conv2D(128, 1, activation='relu', strides=2)(branch_d)
output = layers.concatenate([branch_a, branch_b, branch_c, branch_d], axis=-1)
output = Flatten()(output)
output = Dense(10, activation='softmax')(output)
model = Model(input, output)
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.summary()
这里代码参考 Python 深度学习2018,由于运行的是新的 tf 环境且书中并未给到测试数据样式与训练 Demo,所以这里自己踩了一些坑。
(1) branch_c 的 AvergePooling2d 和 Conv2D 都需要加入 padding,否则最后进行拼接是维度会报异常:
ValueError: A `Concatenate` layer requires inputs with matching shapes except for the concat axis. Got inputs shapes: [(None, 32, 32, 128), (None, 32, 32, 128), (None, 29, 29, 128), (None, 32, 32, 128)]
(2) 还有一个 Flatten 语法使用问题,最终结果 Flatten 时将 output 层直接传给了 Flatten 层 ,还是 keras 语法不熟悉呀 :
AttributeError: 'KerasTensor' object has no attribute 'lower'
正确用法 ✅ :
下面两种用法都正确
output = Flatten()(output)
----------我是分界线-----------
flattenLayer = Flatten()
output = flattenLayer(output)
错误用法 ❌ :
output = Flatten(output)
3.模型训练
训练前先看看模拟的图像大概什么样:
# 训练数据 samples x 64 x 64 x 3
train = np.random.randint(1, 100,
size=(num_samples, 64, 64, 3)).astype('float32')
# 标签 samples x 10
label = np.random.randint(0, 10, size=(num_samples,))
label = to_categorical(label)
print("label:", label.shape)
# 最大-最小值归一化
arr_min = np.min(train[0])
arr_max = np.max(train[0])
img_min_max = Image.fromarray((train[0] - arr_max) * 255 / (arr_max - arr_min), 'RGB')
img_min_max.show()
# 均值-方差归一化
mean = np.mean(train[0])
std = np.std(train[0])
img_mean_std = Image.fromarray( (train[0] - mean) * 255 / std, 'RGB')
img_mean_std.show()

mean-std 归一化得到的结果看着稍微有点图片的感觉,min-max 看着略显突兀,后续可以尝试一下对抗神经网络 GAN ,看看深度学习的作图能力怎么样,下面是训练与评估 demo,由于没有用到真实的数据,所以 loss 与 accuracy 都不做具体参考,有真实数据的话可以测试一下 Inception 是否能带来更好的效果 :
model.fit(train, label, epochs=10, batch_size=128)
test = np.random.randint(1, 100,
size=(test_samples, 64, 64, 3)).astype('float32')
test_label = np.random.randint(0, 10, size=(test_samples,))
test_label = to_categorical(test_label)
model.evaluate(test, test_label)
Epoch 1/10
79/79 [==============================] - 75s 939ms/step - loss: 1371.4792 - accuracy: 0.0946
Epoch 2/10
79/79 [==============================] - 76s 961ms/step - loss: 111.0031 - accuracy: 0.1018
......
Epoch 9/10
79/79 [==============================] - 72s 915ms/step - loss: 0.2061 - accuracy: 0.9853
Epoch 10/10
79/79 [==============================] - 73s 923ms/step - loss: 0.0458 - accuracy: 0.9963
更多推荐算法相关深度学习:深度学习导读专栏