安装
1pip install pydot
2apt-get install graphviz
导入库
x1from __future__ import absolute_import, division, print_function, unicode_literals
2
3import tensorflow as tf
4
5tf.keras.backend.clear_session() # For easy reset of notebook state.
您已经熟悉使用 keras.Sequential()
来创建模型。函数式 API是一种创建比 Sequential
更灵活的模型的方法:它可以处理具有非线性拓扑的模型,具有共享层的模型以及具有多个输入或输出的模型。
它基于这样一种思想,即深度学习模型通常是由层组成的有向无环图(DAG)。函数API是一组用于构建层图的工具。
考虑以下模型:
xxxxxxxxxx
91(input: 784-dimensional vectors)
2 ↧
3[Dense (64 units, relu activation)]
4 ↧
5[Dense (64 units, relu activation)]
6 ↧
7[Dense (10 units, softmax activation)]
8 ↧
9(output: probability distribution over 10 classes)
这是一个3层的简单图表。 要使用函数API构建这个模型,首先要创建一个输入节点:
xxxxxxxxxx
31from tensorflow import keras
2
3inputs = keras.Input(shape=(784,))
这里我们只指定数据的形状:784维向量。无论总是省略批量大小,我们只指定每个样本的形状。对于用于形状 (32, 32, 3)
的图像的输入,我们将使用:
xxxxxxxxxx
11img_inputs = keras.Input(shape=(32, 32, 3))
返回的内容,inputs
,包含有关您希望提供给模型的输入数据的形状和类型的信息:
xxxxxxxxxx
31inputs.shape
2
3inputs.dtype
xxxxxxxxxx
21TensorShape([None, 784])
2tf.float32
通过调用这个输入对象上的一个层,可以在层图中创建一个新节点:
xxxxxxxxxx
41from tensorflow.keras import layers
2
3dense = layers.Dense(64, activation='relu')
4x = dense(inputs)
“层调用”操作就像从“输入”向我们创建的这个层绘制一个箭头。我们把输入“传递”到 dense
层,得到x。
让我们在图层中添加几个层:
xxxxxxxxxx
21x = layers.Dense(64, activation='relu')(x)
2outputs = layers.Dense(10, activation='softmax')(x)
此时,我们可以通过在图层中指定其输入和输出来创建模型:
xxxxxxxxxx
11model = keras.Model(inputs=inputs, outputs=outputs)
回顾一下,这是我们的完整模型定义过程:
xxxxxxxxxx
61inputs = keras.Input(shape=(784,), name='img')
2x = layers.Dense(64, activation='relu')(inputs)
3x = layers.Dense(64, activation='relu')(x)
4outputs = layers.Dense(10, activation='softmax')(x)
5
6model = keras.Model(inputs=inputs, outputs=outputs, name='mnist_model')
让我们看一下模型摘要的样子:
xxxxxxxxxx
11model.summary()
xxxxxxxxxx
161Model: "mnist_model"
2_________________________________________________________________
3Layer (type) Output Shape Param #
4=================================================================
5img (InputLayer) [(None, 784)] 0
6_________________________________________________________________
7dense_3 (Dense) (None, 64) 50240
8_________________________________________________________________
9dense_4 (Dense) (None, 64) 4160
10_________________________________________________________________
11dense_5 (Dense) (None, 10) 650
12=================================================================
13Total params: 55,050
14Trainable params: 55,050
15Non-trainable params: 0
16_________________________________________________________________
我们还可以将模型绘制为图形:
xxxxxxxxxx
11keras.utils.plot_model(model, 'my_first_model.png')
并可选择在绘制的图形中显示每个图层的输入和输出形状:
xxxxxxxxxx
11keras.utils.plot_model(model, 'my_first_model_with_shape_info.png', show_shapes=True)
这个图和我们编写的代码几乎完全相同。在代码版本中,连接箭头只是由调用操作替换。
"graph of layers" 是深度学习模型的非常直观的心理图像,而函数API是一种创建模型的方法,可以很好地反映这种心理图像。
对于使用函数API构建的模型和顺序模型,评估和推理的工作方式完全相同。
这是一个快速演示。
在这里,我们加载MNIST图像数据,将其重新整形为矢量,使模型适合数据(同时监控验证分割的性能),最后我们在测试数据上评估我们的模型:
xxxxxxxxxx
141(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
2x_train = x_train.reshape(60000, 784).astype('float32') / 255
3x_test = x_test.reshape(10000, 784).astype('float32') / 255
4
5model.compile(loss='sparse_categorical_crossentropy',
6 optimizer=keras.optimizers.RMSprop(),
7 metrics=['accuracy'])
8history = model.fit(x_train, y_train,
9 batch_size=64,
10 epochs=5,
11 validation_split=0.2)
12test_scores = model.evaluate(x_test, y_test, verbose=0)
13print('Test loss:', test_scores[0])
14print('Test accuracy:', test_scores[1])
xxxxxxxxxx
61Train on 48000 samples, validate on 12000 samples
2......
3Epoch 5/5
448000/48000 [==============================] - 3s 55us/sample - loss: 0.0759 - accuracy: 0.9770 - val_loss: 0.1139 - val_accuracy: 0.9670
5Test loss: 0.100577776569454
6Test accuracy: 0.9696
有关模型训练和评估的完整指南,请参阅训练和评估指南。
对于使用函数API构建的模型和顺序模型,保存和序列化的工作方式完全相同。
保存函数模型的标准方法是调用model.save()将整个模型保存到一个文件中。稍后,您可以从该文件重新创建相同的模型,即使您不再能够访问创建模型的代码。
这个文件包括:
compile
的东西),如果有的话xxxxxxxxxx
41model.save('path_to_my_model.h5')
2del model
3# Recreate the exact same model purely from the file:
4model = keras.models.load_model('path_to_my_model.h5')
有关模型保存的完整指南,请参阅保存和序列化模型指南。
在函数API中,通过在层图中指定它们的输入和输出来创建模型。这意味着一个图层图可以用来生成多个模型。
在下面的示例中,我们使用相同的层堆栈来实例化两个模型:将图像输入转换为16维向量的编码器 encoder
模型,以及用于训练的端到端自动编码器autoencoder
模型。
xxxxxxxxxx
201encoder_input = keras.Input(shape=(28, 28, 1), name='img')
2x = layers.Conv2D(16, 3, activation='relu')(encoder_input)
3x = layers.Conv2D(32, 3, activation='relu')(x)
4x = layers.MaxPooling2D(3)(x)
5x = layers.Conv2D(32, 3, activation='relu')(x)
6x = layers.Conv2D(16, 3, activation='relu')(x)
7encoder_output = layers.GlobalMaxPooling2D()(x)
8
9encoder = keras.Model(encoder_input, encoder_output, name='encoder')
10encoder.summary()
11
12x = layers.Reshape((4, 4, 1))(encoder_output)
13x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
14x = layers.Conv2DTranspose(32, 3, activation='relu')(x)
15x = layers.UpSampling2D(3)(x)
16x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
17decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x)
18
19autoencoder = keras.Model(encoder_input, decoder_output, name='autoencoder')
20autoencoder.summary()
请注意,我们使解码架构与编码架构严格对称,因此我们得到的输出形状与输入形状(28,28,1)
相同。Conv2D
层的反面是 Conv2DTranspose
层MaxPooling2D
层的反面是 UpSampling2D
层。
您可以将任何模型视为一个图层,方法是在输入或另一个图层的输出上调用它。请注意,通过调用模型,您不仅可以重用模型的体系结构,还可以重用其权重。
让我们看看它是如何运作的。以下是对自动编码器示例的不同看法,该示例创建编码器模型,解码器模型,并在两次调用中链接它们以获取自动编码器模型:
xxxxxxxxxx
271encoder_input = keras.Input(shape=(28, 28, 1), name='original_img')
2x = layers.Conv2D(16, 3, activation='relu')(encoder_input)
3x = layers.Conv2D(32, 3, activation='relu')(x)
4x = layers.MaxPooling2D(3)(x)
5x = layers.Conv2D(32, 3, activation='relu')(x)
6x = layers.Conv2D(16, 3, activation='relu')(x)
7encoder_output = layers.GlobalMaxPooling2D()(x)
8
9encoder = keras.Model(encoder_input, encoder_output, name='encoder')
10encoder.summary()
11
12decoder_input = keras.Input(shape=(16,), name='encoded_img')
13x = layers.Reshape((4, 4, 1))(decoder_input)
14x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
15x = layers.Conv2DTranspose(32, 3, activation='relu')(x)
16x = layers.UpSampling2D(3)(x)
17x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
18decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x)
19
20decoder = keras.Model(decoder_input, decoder_output, name='decoder')
21decoder.summary()
22
23autoencoder_input = keras.Input(shape=(28, 28, 1), name='img')
24encoded_img = encoder(autoencoder_input)
25decoded_img = decoder(encoded_img)
26autoencoder = keras.Model(autoencoder_input, decoded_img, name='autoencoder')
27autoencoder.summary()
如您所见,模型可以嵌套:模型可以包含子模型(因为模型就像一个层)。
模型嵌套的常见用例是集成。作为一个例子,这里是如何将一组模型集成到一个平均其预测的模型中:
xxxxxxxxxx
151def get_model():
2 inputs = keras.Input(shape=(128,))
3 outputs = layers.Dense(1, activation='sigmoid')(inputs)
4 return keras.Model(inputs, outputs)
5
6model1 = get_model()
7model2 = get_model()
8model3 = get_model()
9
10inputs = keras.Input(shape=(128,))
11y1 = model1(inputs)
12y2 = model2(inputs)
13y3 = model3(inputs)
14outputs = layers.average([y1, y2, y3])
15ensemble_model = keras.Model(inputs=inputs, outputs=outputs)
functional API使操作多个输入和输出变得容易。使用Sequential API无法处理此问题。
这是一个简单的例子。
假设您正在构建一个系统,按照优先级对定制的发行票据进行排序,并将它们路由到正确的部门。
你的模型将有3个输入:
它将有两个输出:
让我们用Functional API在几行中构建这个模型。
xxxxxxxxxx
291num_tags = 12 # Number of unique issue tags
2num_words = 10000 # Size of vocabulary obtained when preprocessing text data
3num_departments = 4 # Number of departments for predictions
4
5title_input = keras.Input(shape=(None,), name='title') # Variable-length sequence of ints
6body_input = keras.Input(shape=(None,), name='body') # Variable-length sequence of ints
7tags_input = keras.Input(shape=(num_tags,), name='tags') # Binary vectors of size `num_tags`
8
9# Embed each word in the title into a 64-dimensional vector
10title_features = layers.Embedding(num_words, 64)(title_input)
11# Embed each word in the text into a 64-dimensional vector
12body_features = layers.Embedding(num_words, 64)(body_input)
13
14# Reduce sequence of embedded words in the title into a single 128-dimensional vector
15title_features = layers.LSTM(128)(title_features)
16# Reduce sequence of embedded words in the body into a single 32-dimensional vector
17body_features = layers.LSTM(32)(body_features)
18
19# Merge all available features into a single large vector via concatenation
20x = layers.concatenate([title_features, body_features, tags_input])
21
22# Stick a logistic regression for priority prediction on top of the features
23priority_pred = layers.Dense(1, activation='sigmoid', name='priority')(x)
24# Stick a department classifier on top of the features
25department_pred = layers.Dense(num_departments, activation='softmax', name='department')(x)
26
27# Instantiate an end-to-end model predicting both priority and department
28model = keras.Model(inputs=[title_input, body_input, tags_input],
29 outputs=[priority_pred, department_pred])
让我们绘制模型:
xxxxxxxxxx
11keras.utils.plot_model(model, 'multi_input_and_output_model.png', show_shapes=True)
编译此模型时,我们可以为每个输出分配不同的损耗。您甚至可以为每个损失分配不同的权重,以调整它们对总训练损失的贡献。
xxxxxxxxxx
31model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
2 loss=['binary_crossentropy', 'categorical_crossentropy'],
3 loss_weights=[1., 0.2])
由于我们为输出图层指定了名称,因此我们也可以像这样指定损失:
xxxxxxxxxx
41model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
2 loss={'priority': 'binary_crossentropy',
3 'department': 'categorical_crossentropy'},
4 loss_weights=[1., 0.2])
我们可以通过传递Numpy输入和目标数组列表来训练模型:
xxxxxxxxxx
141import numpy as np
2
3# Dummy input data
4title_data = np.random.randint(num_words, size=(1280, 10))
5body_data = np.random.randint(num_words, size=(1280, 100))
6tags_data = np.random.randint(2, size=(1280, num_tags)).astype('float32')
7# Dummy target data
8priority_targets = np.random.random(size=(1280, 1))
9dept_targets = np.random.randint(2, size=(1280, num_departments))
10
11model.fit({'title': title_data, 'body': body_data, 'tags': tags_data},
12 {'priority': priority_targets, 'department': dept_targets},
13 epochs=2,
14 batch_size=32)
xxxxxxxxxx
31....
2Epoch 2/2
31280/1280 [==============================] - 11s 9ms/sample - loss: 1.2137 - priority_loss: 0.6489 - department_loss: 2.8242
当使用Dataset
对象调用fit时,它应该产生一个列表元组,如 ([title_data, body_data, tags_data], [priority_targets, dept_targets])
或者一个字典的元组 ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets})
。
有关更详细的说明,请参阅完整的训练和评估指南。
除了具有多个输入和输出的模型之外,Functional API还可以轻松地操作非线性连接拓扑,也就是说,层不按顺序连接的模型。这也无法使用Sequential API处理(如名称所示)。
一个常见的用例是残差连接。
让我们为CIFAR10构建一个玩具ResNet模型来演示这个
xxxxxxxxxx
211inputs = keras.Input(shape=(32, 32, 3), name='img')
2x = layers.Conv2D(32, 3, activation='relu')(inputs)
3x = layers.Conv2D(64, 3, activation='relu')(x)
4block_1_output = layers.MaxPooling2D(3)(x)
5
6x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_1_output)
7x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
8block_2_output = layers.add([x, block_1_output])
9
10x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_2_output)
11x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
12block_3_output = layers.add([x, block_2_output])
13
14x = layers.Conv2D(64, 3, activation='relu')(block_3_output)
15x = layers.GlobalAveragePooling2D()(x)
16x = layers.Dense(256, activation='relu')(x)
17x = layers.Dropout(0.5)(x)
18outputs = layers.Dense(10, activation='softmax')(x)
19
20model = keras.Model(inputs, outputs, name='toy_resnet')
21model.summary()
让我们绘制模型:
xxxxxxxxxx
11keras.utils.plot_model(model, 'mini_resnet.png', show_shapes=True)
我们来训练吧:
xxxxxxxxxx
131(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
2x_train = x_train.astype('float32') / 255.
3x_test = x_test.astype('float32') / 255.
4y_train = keras.utils.to_categorical(y_train, 10)
5y_test = keras.utils.to_categorical(y_test, 10)
6
7model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
8 loss='categorical_crossentropy',
9 metrics=['acc'])
10model.fit(x_train, y_train,
11 batch_size=64,
12 epochs=1,
13 validation_split=0.2)
xxxxxxxxxx
21Train on 40000 samples, validate on 10000 samples
240000/40000 [==============================] - 318s 8ms/sample - loss: 1.9034 - acc: 0.2767 - val_loss: 1.6173 - val_acc: 0.3870
函数式API的另一个好用途是使用共享层的模型。共享图层是在同一模型中多次重复使用的图层实例:它们学习与层图中的多个路径对应的特征。
共享层通常用于编码来自类似空间的输入(例如,两个不同的文本,具有相似的词汇表),因为它们可以在这些不同的输入上共享信息,并且可以在更少的时间内训练这样的模型数据。如果在其中一个输入中看到给定的单词,那将有利于处理通过共享层的所有输入。
要在Functional API中共享图层,只需多次调用同一图层实例即可。例如,这是一个跨两个不同文本输入共享的嵌入Embedding
层:
xxxxxxxxxx
121# Embedding for 1000 unique words mapped to 128-dimensional vectors
2shared_embedding = layers.Embedding(1000, 128)
3
4# Variable-length sequence of integers
5text_input_a = keras.Input(shape=(None,), dtype='int32')
6
7# Variable-length sequence of integers
8text_input_b = keras.Input(shape=(None,), dtype='int32')
9
10# We reuse the same layer to encode both inputs
11encoded_input_a = shared_embedding(text_input_a)
12encoded_input_b = shared_embedding(text_input_b)
因为您在Functional API中操作的层图是静态数据结构,所以可以访问和检查它。这就是我们如何将功能模型绘制为图像的方式。
这也意味着我们可以访问中间层的激活(图中的“节点”)并在其他地方重用它们。例如,这对于特征提取非常有用!
让我们看一个例子。这是一个在ImageNet上预先训练权重的VGG19模型:
xxxxxxxxxx
31from tensorflow.keras.applications import VGG19
2
3vgg19 = VGG19()
这些是模型的中间激活,通过查询图形数据结构获得:
xxxxxxxxxx
11features_list = [layer.output for layer in vgg19.layers]
我们可以使用这些功能来创建一个新的特征提取模型,它返回中间层激活的值(我们可以在3行中完成所有这些操作)。
xxxxxxxxxx
41feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)
2
3img = np.random.random((1, 224, 224, 3)).astype('float32')
4extracted_features = feat_extraction_model(img)
除了其他方面,这在实现神经风格迁移时派上用场。
tf.keras拥有广泛的内置层。这里有一些例子:
Conv1D
, Conv2D
, Conv3D
, Conv2DTranspose
, etc.MaxPooling1D
, MaxPooling2D
, MaxPooling3D
, AveragePooling1D
, etc.GRU
, LSTM
, ConvLSTM2D
, etc.BatchNormalization
, Dropout
, Embedding
, etc.如果找不到所需的内容,可以通过创建自己的图层来扩展API。
所有图层都是Layer
类的子类,并实现:
call
方法,指定由层完成的计算。build
方法,它创建了图层的权重(请注意,这只是一种样式约定;您也可以在 __init__
中创建权重)。要了解有关从头开始创建图层的更多信息,请查看该指南 Guide to writing layers and models from scratch.
这是一个Dense
层的简单实现:
xxxxxxxxxx
211class CustomDense(layers.Layer):
2
3 def __init__(self, units=32):
4 super(CustomDense, self).__init__()
5 self.units = units
6
7 def build(self, input_shape):
8 self.w = self.add_weight(shape=(input_shape[-1], self.units),
9 initializer='random_normal',
10 trainable=True)
11 self.b = self.add_weight(shape=(self.units,),
12 initializer='random_normal',
13 trainable=True)
14
15 def call(self, inputs):
16 return tf.matmul(inputs, self.w) + self.b
17
18inputs = keras.Input((4,))
19outputs = CustomDense(10)(inputs)
20
21model = keras.Model(inputs, outputs)
如果希望自定义层支持序列化,还应定义 get_config
方法,该方法返回层实例的构造函数参数:
xxxxxxxxxx
291class CustomDense(layers.Layer):
2
3 def __init__(self, units=32):
4 super(CustomDense, self).__init__()
5 self.units = units
6
7 def build(self, input_shape):
8 self.w = self.add_weight(shape=(input_shape[-1], self.units),
9 initializer='random_normal',
10 trainable=True)
11 self.b = self.add_weight(shape=(self.units,),
12 initializer='random_normal',
13 trainable=True)
14
15 def call(self, inputs):
16 return tf.matmul(inputs, self.w) + self.b
17
18 def get_config(self):
19 return {'units': self.units}
20
21
22inputs = keras.Input((4,))
23outputs = CustomDense(10)(inputs)
24
25model = keras.Model(inputs, outputs)
26config = model.get_config()
27
28new_model = keras.Model.from_config(
29 config, custom_objects={'CustomDense': CustomDense})
或者,你也可以实现类方法 from_config(cls, config)
,它负责在给定配置字典的情况下重新创建一个层实例。from_config
的默认实现是:
xxxxxxxxxx
21def from_config(cls, config):
2 return cls(**config)
如何决定是使用Functional API创建新模型,还是直接将Model
类继承?
通常,Functional API更高级,更容易和更安全使用,并且具有子模型不支持的许多功能。
但是,在创建不易表达为层的有向非循环图的模型时,模型子类化为您提供了更大的灵活性(例如,您无法使用Functional API实现Tree-RNN,您必须直接将Model
子类化)。
下面列出的属性对于Sequential模型也是如此(它们也是数据结构),但对于子类模型(Python字节码,而不是数据结构)则不然。
No super(MyClass, self).__init__(...)
, no def call(self, ...):
, etc.
比较:
xxxxxxxxxx
41inputs = keras.Input(shape=(32,))
2x = layers.Dense(64, activation='relu')(inputs)
3outputs = layers.Dense(10)(x)
4mlp = keras.Model(inputs, outputs)
使用子类型:
xxxxxxxxxx
161class MLP(keras.Model):
2
3 def __init__(self, **kwargs):
4 super(MLP, self).__init__(**kwargs)
5 self.dense_1 = layers.Dense(64, activation='relu')
6 self.dense_2 = layers.Dense(10)
7
8 def call(self, inputs):
9 x = self.dense_1(inputs)
10 return self.dense_2(x)
11
12# Instantiate the model.
13mlp = MLP()
14# Necessary to create the model's state.
15# The model doesn't have a state until it's called at least once.
16_ = mlp(tf.zeros((1, 32)))
在Functional API中,您的输入规范(shape和dtype)是事先创建的(通过Input
),每次调用一个图层时,图层都会检查传递给它的规范是否符合其假设,并且它会引发一个如果没有帮助错误消息。
这可以保证您可以运行使用Functional API构建的任何模型。所有调试(与收敛相关的调试除外)将在模型构建期间静态发生,而不是在执行时发生。这类似于编译器中的类型检查。
您可以将模型绘制为图形,并且可以轻松访问此图中的中间节点 - 例如,提取和重用中间层的激活,如前面的示例中所示:
xxxxxxxxxx
21features_list = [layer.output for layer in vgg19.layers]
2feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)
因为 Functional 模型是一种数据结构而不是一段代码,所以它可以安全地序列化,并且可以保存为单个文件,允许您重新创建完全相同的模型,而无需访问任何原始代码。有关详细信息,请参阅我们的保存和序列化指南。
Functional API将模型视为图层的DAG。对于大多数深度学习体系结构都是如此,但并非全部:例如,递归网络或树RNN不遵循此假设,并且无法在Functional API中实现。
在编写高级架构时,您可能希望执行“定义层的DAG”范围之外的事情:例如,您可能希望在模型实例上公开多个自定义训练和推理方法。这需要子类化。
为了更深入地了解Functional API和Model子类之间的差异,您可以阅读 What are Symbolic and Imperative APIs in TensorFlow 2.0?.
重要的是,在Functional API或Model子类化之间进行选择并不是一个二元决策,它将您限制为一类模型。tf.keras API中的所有模型都可以与每个模型进行交互,无论它们是顺序模型,功能模型还是从头开始编写的子类模型/层。
您始终可以使用Functional模型或Sequential模型作为子类 Model/Layer 的一部分:
xxxxxxxxxx
361units = 32
2timesteps = 10
3input_dim = 5
4
5# Define a Functional model
6inputs = keras.Input((None, units))
7x = layers.GlobalAveragePooling1D()(inputs)
8outputs = layers.Dense(1, activation='sigmoid')(x)
9model = keras.Model(inputs, outputs)
10
11
12class CustomRNN(layers.Layer):
13
14 def __init__(self):
15 super(CustomRNN, self).__init__()
16 self.units = units
17 self.projection_1 = layers.Dense(units=units, activation='tanh')
18 self.projection_2 = layers.Dense(units=units, activation='tanh')
19 # Our previously-defined Functional model
20 self.classifier = model
21
22 def call(self, inputs):
23 outputs = []
24 state = tf.zeros(shape=(inputs.shape[0], self.units))
25 for t in range(inputs.shape[1]):
26 x = inputs[:, t, :]
27 h = self.projection_1(x)
28 y = h + self.projection_2(state)
29 state = y
30 outputs.append(y)
31 features = tf.stack(outputs, axis=1)
32 print(features.shape)
33 return self.classifier(features)
34
35rnn_model = CustomRNN()
36_ = rnn_model(tf.zeros((1, timesteps, input_dim)))
xxxxxxxxxx
11(1, 10, 32)
相反,只要它实现了一个遵循以下模式之一的call
方法,你就可以在Functional API中使用任何子类Layer或Model:
call(self, inputs, **kwargs)
其中 inputs
是张量或张量的张量结构(例如张量列表),其中 **kwargs
是非张量参数(非输入)。call(self, inputs, training=None, **kwargs)
其中 training
是一个布尔值,表示该层是否应该在训练模式和推理模式下运行。call(self, inputs, mask=None, **kwargs)
其中 mask
是布尔掩码张量(例如对RNN有用)。call(self, inputs, training=None, mask=None, **kwargs)
-- 当然,您可以同时具有屏蔽和特定于训练的行为。此外,如果在自定义图层或模型上实现 get_config
方法,则使用它创建的功能模型仍将是可序列化和可克隆的。
这是一个快速示例,我们在Functional模型中使用从头开始编写的自定义RNN:
xxxxxxxxxx
381units = 32
2timesteps = 10
3input_dim = 5
4batch_size = 16
5
6
7class CustomRNN(layers.Layer):
8
9 def __init__(self):
10 super(CustomRNN, self).__init__()
11 self.units = units
12 self.projection_1 = layers.Dense(units=units, activation='tanh')
13 self.projection_2 = layers.Dense(units=units, activation='tanh')
14 self.classifier = layers.Dense(1, activation='sigmoid')
15
16 def call(self, inputs):
17 outputs = []
18 state = tf.zeros(shape=(inputs.shape[0], self.units))
19 for t in range(inputs.shape[1]):
20 x = inputs[:, t, :]
21 h = self.projection_1(x)
22 y = h + self.projection_2(state)
23 state = y
24 outputs.append(y)
25 features = tf.stack(outputs, axis=1)
26 return self.classifier(features)
27
28# Note that we specify a static batch size for the inputs with the `batch_shape`
29# arg, because the inner computation of `CustomRNN` requires a static batch size
30# (when we create the `state` zeros tensor).
31inputs = keras.Input(batch_shape=(batch_size, timesteps, input_dim))
32x = layers.Conv1D(32, 3)(inputs)
33outputs = CustomRNN()(x)
34
35model = keras.Model(inputs, outputs)
36
37rnn_model = CustomRNN()
38_ = rnn_model(tf.zeros((1, 10, 5)))
这就是我们关于函数API的指南的全部内容!
现在,您已经拥有了一套用于构建深度学习模型的强大工具。
最新版本:https://www.mashangxue123.com/tensorflow/tf2-guide-keras-functional.html 英文版本:https://tensorflow.google.cn/beta/guide/keras/functional 翻译建议PR:https://github.com/mashangxue/tensorflow2-zh/edit/master/r2/guide/keras/functional.md