Keras 高级用法:函数式 API 7.1
Posted 神机喵算
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Keras 高级用法:函数式 API 7.1相关的知识,希望对你有一定的参考价值。
7.1.4 layer 有向无环图( DAG )
函数式 API,不仅仅可以构建多输入和多输出模型,也可以实现复杂的网络拓扑结构。我们利用Keras的神经网络能构件任意 layer 的有向无环图。其中的限定词“无环”意味着这些图不能有环。一个 layer 输入张量 x ,再生成 x ,这种情况是不可行的。只有一种处理 loop 的情形在循环 layer 内部:循环连接。
有些常见的神经网络是以 graph 实现的,其中两个有影响力的是:Inception 网络和残差连接。为了更好的理解如何使用函数式 API构建 layer 有向无环图,下面介绍用Keras如何实现两种神经网络。
Inception 网络
Inception 是卷积神经网络中流行的网络结构,它是由Google的Christian Szegedy 在2013 - 2014 年提出的。Inception 由一系列独立的小型网络堆叠而成,它们分成一些并行分支。Inception 最常见的形式是由三到四个分支,这些分支以 1 x 1 卷积开始,并紧跟一个 3 x 3 的卷积,最后将输出结果特征级联。这种设置有利于各个网络单独地学习空间特征和通道特征,比将它们联合学习更高效。更复的 Inception 一般涉及池化操作,不同空间尺寸的卷积(比如,在某些分支上用 5 x 5 代替 3 x 3 的卷积),以及不带空间尺寸卷积的分支(只有 1 x 1 卷积)。下面看下 Inception V3 的网络结构,见图 7.8。
图 7.8 Inception V3 的网络结构
1 x 1 卷积的作用
你已经知道了卷积是对输入张量沿着每块(tile)进行空间特征抽取(spatial patch),并对每个特征应用相同的变换。一种边界情况是:当特征抽取是由单个块组成时,卷积操作相当于通过 Dense layer 计算块向量。它将计算输入张量不同通道的混合信息特征,但是却没有跨整个空间混合信息特征(因为每次只计算一块)。
1 x 1 卷积,也称为pointwise卷积,它是 Inception 网络的特点,这些主要用来分解通道特征学习和空间特征学习。如果假设每个通道都是在空间上高度自相关的,那么这是合理的。但是不同的通道可能互相之间不是高度自相关的。
译者注:tile 指输入向量分块,也可指卷积核的分块;patch 上面指卷积核。
下面是用函数式 API 实现图 7.8 的 Inception网络,其中假设输入张量 x 的形状是 4D:
from keras import layers
'''Every branch has the same stride value (2),
which is necessary to keep all branch outputs the same size so you can concatenate them.
'''
branch_a = layers.Conv2D(128, 1,
activation='relu', strides=2)(x)
branch_b = layers.Conv2D(128, 1, activation='relu')(x)
'''In this branch, the striding occurs in the spatial convolution layer.
'''
branch_b = layers.Conv2D(128, 3, activation='relu', strides=2)(branch_b)
'''In this branch, the striding occurs in the average pooling layer.
'''
branch_c = layers.AveragePooling2D(3, strides=2)(x)
branch_c = layers.Conv2D(128, 3, activation='relu')(branch_c)
branch_d = layers.Conv2D(128, 1, activation='relu')(x)
branch_d = layers.Conv2D(128, 3, activation='relu')(branch_d)
branch_d = layers.Conv2D(128, 3, activation='relu', strides=2)(branch_d)
'''Concatenates the branch outputs to obtain the module output
'''
output = layers.concatenate(
[branch_a, branch_b, branch_c, branch_d], axis=-1)
注意,完整的 Inception V3网络结构在Keras keras.applications.inception_v3.InceptionV3可用,包括在ImageNet数据集上预先训练的权重。另一相近的模型是 Xception模型,Xception是“extreme inception”的缩写,是由 Inception启发而来的卷积结构。它利用 channel-wise 和 space-wise 特征分开学习的思想,并且用 depthwise separable convolution (由 depthwise convolution 紧跟 pointwise convolution组成。depthwise convolution的每个输入通道分开处理,pointwise convolution即 1 x 1 卷积)代替了 Inception网络结构。Xception和 Inception V3有相同的参数数量,却性能更好。
残差连接
残差连接(residual connection)是一个常见的有向无环图网络组件,在许多2015 年后的网络结构中发现,包括 Xception 。它解决了困扰大规模深度学习模型的两个常见问题:梯度消失和特征表示瓶颈。一般来说,超过十个 layer 的模型添加残差连接可能是有益的。
残差连接仅仅是将一个前一个层 layer 的输出作为后一层的输入,有效地在序列网络结构中创建了一个快捷方式(shortcut)。假设两个激活函数具有相同的大小,则将前一个层 layer 的输出与后来的激活函数进行求和,而不是合并。若果激活函数大小不同,可以使用线性变换将前一层 layer 的激活转换成目标形状 (例如,不带激活函数的Dense layer,或者卷积特征图,不带激活函数的 1x1卷积)。
下面用 Keras 实现一个残差连接,输入张量 x 是 4D。当特征图大小相同,采用identity 残差连接:
from keras import layers
x = ...
'''Applies a transformation to x
'''
y = layers.Conv2D(128, 3, activation='relu', padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
'''Adds the original x back to the output features
'''
y = layers.add([y, x])
当特征图大小不同,采用线性残差连接:
from keras import layers
x = ...
y = layers.Conv2D(128, 3, activation='relu', padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.MaxPooling2D(2, strides=2)(y)
'''Uses a 1 × 1 convolution to
linearly downsample the original
x tensor to the same shape as y
'''
residual = layers.Conv2D(128, 1, strides=2, padding='same')(x)
'''Adds the residual tensor back to the output features
'''
y = layers.add([y, residual])
深度学习的特征表示瓶颈
在 Sequential 模型中,下一个特征表示层只能获取前一层经过激活函数的信息。假如某一层的特征维度太低,那么模型包含的特征会被该层限制。残差连接将更早一层的特征重新注入到下游层,从而部分地解决了深度学习模型中的特征表示瓶颈问题。
深度学习的梯度消失问题
反向传播算法是深度神经网络的主要算法,它从输出端损失传播反馈信号到上一层。假如反馈信号传输的层数太深,那么信号可能冗余或者完全丢失,导致神经网络不可训练。该问题被称作梯度消失( vanishing gradients )。
上面问题经常出现在深度网络和带循环的网络,这两种情况下反馈信号必须经过长系列传播。你已经熟悉的 LSTM layer 是用来解决循环神经网络的:它引入一个 carry 跟踪传播特征到主流程。残差连接在前向的深度神经网络中采用相似的方法,但是看起来更简单:引入一个纯线性特征信息跟踪到主流程,因此可以帮助深度神经网络梯度传播。
7.1.5 共享层权重
函数式 API 更重要的特性是多次重用 layer 实例。当你调用 layer 实例两次,也只会一次实例化,两次使用的相同的权重。这有利于你构建共享分支模型,这些分支共享相同的知识并执行相同的操作,即共享相同的特征表示,从不同的输入数据集同时学习这些特征表示。
一个例子就是评估两个句子的语义相似度的模型。该模型有两个输入(比较的两个句子),输出一个 取值范围为 0 到 1 的分数,其中 0 表示句子不相关,1 表示句子是相同的,或者仅是对彼此的重新表述。它应用在对话系统中的自然语言去重查询。
在本例子中,两个输入句子可以互换,因为语义相似是对称关系:A 和 B 相似等同于 B 和 A 相似。因此,这里我们使用单个 LSTM layer ,LSTM layer的特征表示是同时从两个输入学习的。这种也称为 Siamese LSTM 模型或者 shared LSTM。
下面用 Keras 函数式 API 实现共享层模型:
from keras import layers
from keras import Input
from keras.models import Model
'''Instantiates a single LSTM layer, once
'''
lstm = layers.LSTM(32)
'''Building the left branch of the model:
inputs are variable-length sequences of
vectors of size 128.
'''
left_input = Input(shape=(None, 128))
left_output = lstm(left_input)
'''Building the right branch of the model:
when you call an existing layer instance,
you reuse its weights.
'''
right_input = Input(shape=(None, 128))
right_output = lstm(right_input)
'''Builds the classifier on top
'''
merged = layers.concatenate([left_output, right_output], axis=-1) predictions = layers.Dense(1, activation='sigmoid')(merged)
'''Instantiating and training the model:
when you train such a model,
the weights of the LSTM layer are
updated based on both inputs.
'''
model = Model([left_input, right_input], predictions)
model.fit([left_data, right_data], targets)
7.1.6 模型作为层
重要的是,在函数式 API中,模型能当作 layer 来有效地使用,可以认为模型是“更大的 layer”。Sequential 和 Model 类都是这样,这意味着可以在输入张量上调用一个模型,并输出一个输出张量:
y = model(x)
如果一个模型有多个输入张量和多个输出张量,那么可以调用一个张量list:
y1, y2 = model([x1, x2])
当调用一个 model 实例时,你正在重用 model 的权重,就如同调用一个 layer 实例。简单地调用一个实例,无论它是一个 layer 实例还是一个 model 实例,都将重用实例已有的学习表示,这是非常直观的。
一个简单的重用一个 model 实例的例子是,使用双摄像头作为输入建立一个视觉模型:两个平行的摄像头,相距几厘米。这样的模型可以感知深度,这在许多应用中都有用。在合并两个提要之前,你不需要两个独立的模型来从左右两边的摄像机中提取视觉特征。这样处理可以在两个输入之间使用相同权重的层共享,从而共享相同的特征表示。下面是如何在 Keras 中实现这个功能:
from keras import layers
from keras import applications
from keras import Input
'''The base image-processing model is the Xception network (convolutional base only).
'''
xception_base = applications.Xception(weights=None, include_top=False)
'''The inputs are 250 × 250 RGB images.
'''
left_input = Input(shape=(250, 250, 3))
right_input = Input(shape=(250, 250, 3))
'''Calls the same vision model twice
'''
left_features = xception_base(left_input)
right_input = xception_base(right_input)
'''The merged features contain information from the right visual feed and the left visual feed.
'''
merged_features = layers.concatenate(
[left_features, right_input], axis=-1)
7.1.7 小结
本小节介绍了 Keras 的函数式 API ,它是构建高级神经网络结构必备的功能。下面总结下知识点:
逐步说明解释什么情况下使用函数式 API ,而不是线性堆叠的 layer;
如何用 Keras 函数式 API 构建多输入、多输出和复杂拓扑网络模型;
如何在不同的模型分支重用一个 layer 或者 model 的权重,多次调用相同的 layer 或者 model 实例。
招聘:服务端开发、机器学习工程师
地点:北京,直接后台私信即可。
未完待续。。。
Enjoy!
声明本资料仅供个人学习交流、研究,禁止用于其他目的。
上述内容加入了个人的理解和提炼(若有引起不适,请阅读原文),希望能用通俗易懂、行文流畅的表达方式呈现给新手。
若发现以上文章有任何不妥,请联系我。
以上是关于Keras 高级用法:函数式 API 7.1的主要内容,如果未能解决你的问题,请参考以下文章