Deep Learning with Python
这篇文章是我学习《Deep Learning with Python》(第二版,François Chollet 著) 时写的系列笔记之一。文章的内容是从 Jupyter notebooks 转成 Markdown 的,你可以去 GitHub 或 Gitee 找到原始的 .ipynb
笔记本。
你可以去这个网站在线阅读这本书的正版原文(英文)。这本书的作者也给出了配套的 Jupyter notebooks。
本文为 第7章 高级的深度学习最佳实践 (Chapter 7. Advanced deep-learning best practices) 的笔记之一。
文章目录
7.1 Going beyond the Sequential model: the Keras functional API
不用 Sequential 模型的解决方案:Keras 函数式 API
我们之前用的 Sequential 模型是最基础、但常用的一种模型,它只有一个输入和一个输出,整个网络由层线性堆叠而成。
但是,有时我们的网络需要多个输入。比如预测衣服价格,输入商品信息、文本描述、图片,这三类信息应该分别用 Dense、RNN、CNN 处理,提取出信息后用一个合并模块把所有各种信息综合起来最终预测价格:
也有时,我们的网络需要多个输出(多个头)。比如输入一个小说,我们希望得到小说的分类,并推测写作时间。这个问题应该使用一个共用的模块去处理文本,提取信息,然后分别交给小说分类器、日期回归器去预测分类、写作时间:
还有时,有些复杂的网络会使用非线性的网络拓扑结构。比如一种叫 Inception 的东西,输入会被多个并行的卷积分支处理,然后将这些分支的输出合并为单个张量;还有种叫 residual connection (残差连接)的方法,将前面的输出张量与后面的输出张量相加,从而将前面的表示重新注入下游数据流中,防止信息处理流程中的信息损失:
这些网络都是图状(graph-like)的,是个网络结构,而不是 Sequential 那样的线性堆叠。要在 Keras 中实现这种复杂的模型,就需要使用 Keras 的函数式 API。
函数式 API
Keras 的函数式 API 把层当作函数来使用,接收张量并返回张量:
from tensorflow.keras import Input, layers
input_tensor = Input(shape=(32, )) # 输入张量
dense = layers.Dense(32, activation='relu') # 层函数
output_tensor = dense(input_tensor) # 输出张量
我们来用函数式 API 构建一个简单网络,和 Sequential 做比较:
# Sequential 模型
from tensorflow.keras.models import Sequential
from tensorflow.keras import layers
seq_model = Sequential()
seq_model.add(layers.Dense(32, activation='relu', input_shape=(64, )))
seq_model.add(layers.Dense(32, activation='relu'))
seq_model.add(layers.Dense(10, activation='softmax'))
seq_model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 32) 2080
_________________________________________________________________
dense_2 (Dense) (None, 32) 1056
_________________________________________________________________
dense_3 (Dense) (None, 10) 330
=================================================================
Total params: 3,466
Trainable params: 3,466
Non-trainable params: 0
_________________________________________________________________
# 函数式 API 模型
from tensorflow.keras.models import Model
from tensorflow.keras import Input
from tensorflow.keras import layers
input_tensor = Input(shape=(64, ))
x = layers.Dense(32, activation='relu')(input_tensor)
x = layers.Dense(32, activation='relu')(x)
output_tensor = layers.Dense(10, activation='softmax')(x)
func_model = Model(input_tensor, output_tensor)
func_model.summary()
Model: "functional_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, 64)] 0
_________________________________________________________________
dense_4 (Dense) (None, 32) 2080
_________________________________________________________________
dense_5 (Dense) (None, 32) 1056
_________________________________________________________________
dense_6 (Dense) (None, 10) 330
=================================================================
Total params: 3,466
Trainable params: 3,466
Non-trainable params: 0
_________________________________________________________________
Model 对象实例化的时候,只需给出输入张量和输入张量变换(经过各种层)得到的输出张量。Keras 会在自动找出从 input_tensor 到 output_tensor 所包含的每一层,并将这些层组合成一个图状的数据结构——一个 Model。
注意,output_tensor 必须是由对应的 input_tensor 变换得到的。如果用不相关的输入张量和输出张量来构建 Model,会爆 Graph disconnected 的 ValueError (书上写的 keras 是 RuntimeError,tf.keras 是 ValueError):
>>> unrelated_input = Input(shape=(32,))
>>> bad_model = Model(unrelated_input, output_tensor)
... # Traceback
ValueError: Graph disconnected: cannot obtain value for tensor Tensor("input_2:0", shape=(None, 64), dtype=float32) at layer "dense_4". The following previous layers were accessed without issue: []
也就是说,无法从指定的输入连接到输出形成一张图(Graph,那种数据结构,网络的那种)啦。
对这种函数式 API 构建的网络,编译、训练或评估都和 Sequential 相同。
# 编译
func_model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
# 随机训练数据
import numpy as np
x_train