总览
最近在学习TensorFlow 2的API,官方教程一开始就介绍了整合进TensorFlow中的Keras。学习了一些基本API之后发现Keras的API较为友善,并且非常具有结构性,基本工作流也有较为固定的模式。一图胜千言,在此先总结出tensorflow.keras
的基本工作流程:
接下来再把其中一些关键步骤的具体用法简单记录一下。
模型创建
第一步自然就是要定义出神经网络模型,Keras模型的基类为tensorflow.keras.Model,模型中的基本组件就是层结构(tensorflow.keras.layers.Layer)。神经网络可以看做就是层的有向无环图(DAG)。模型创建就是定义出模型中各层的具体结构是什么、层与层之间的关系是什么。下面简述几种常用的模型构建方式。
Sequential
Keras提供了tensorflow.keras.Sequential类可以更方便地直接使用。正如其名,Sequential表示一组顺序层结构组成的神经网络,适用于一个输入对应一个输出的情况。可以在创建模型实例时直接将层实例列表作为参数,一步构建完成:
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10)
])
也可以先不用参数创建实例,之后再使用Sequential的add方法,按顺序向模型实例中逐个添加层实例:
model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=(28, 28)))
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dense(10))
与add
对应,还有pop
方法,就是从层结构列表的末尾移出一个层实例。
函数式调用
上述Sequential模型仅适用于一个输入对应一个输出的简单神经网络。而目前有很多更复杂的、非线性神经网络结构。即可能单一输入会产生分支,也有可能多个分支共享一个层结构。因此Keras也提供了函数式API,可以更灵活地实现更复杂的神经网络。
所谓函数式API,即每个层对象都可以被直接函数式地调用,这种调用就指明了这个层的一个输入与输出关系。如:
inputs = keras.Input(shape=(784,))
dense = layers.Dense(64, activation="relu")
x = dense(inputs)
首先定义了一个Dense
层,之后直接函数式地调用层实例,以inputs
作为输入参数,赋值给变量x
。这样也就定义了从inputs
经过dense
到x
的输入输出关系。这样就可以灵活地定义不同层与各种输出输出之间的关系,最后再通过inputs
和outputs
参数初始化model
类,也就可以将连接inputs
到outputs
的所有层打包成一个模型:
encoder_input = keras.Input(shape=(28, 28, 1), name="original_img")
x = layers.Conv2D(16, 3, activation="relu")(encoder_input)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.Conv2D(16, 3, activation="relu")(x)
encoder_output = layers.GlobalMaxPooling2D()(x)
encoder = keras.Model(encoder_input, encoder_output, name="encoder")
decoder_input = keras.Input(shape=(16,), name="encoded_img")
x = layers.Reshape((4, 4, 1))(decoder_input)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
x = layers.Conv2DTranspose(32, 3, activation="relu")(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation="relu")(x)
decoder = keras.Model(decoder_input, decoder_output, name="decoder")
autoencoder_input = keras.Input(shape=(28, 28, 1), name="img")
encoded_img = encoder(autoencoder_input)
decoded_img = decoder(encoded_img)
autoencoder = keras.Model(autoencoder_input, decoded_img, name="autoencoder")
由于Model
类就是Layer
的子类,因此也可以对模型进行函数式调用,从输入产生输出。如上例就独立地定义了encoder
和decoder
两个模型,之后再通过两步对模型的函数式调用,将两个模型连接起来形成更复杂的autoencoder
模型。
小结
此外还有更高级的创建模型的方法,就是通过自定义Model类的子类来根据需求灵活自定义模型。这些官方指南——“编写自定义层和模型”中有详述,以后深入学习。
模型中的层也就都是tensorflow.keras.layers.Layer的具体子类,可以自己实现。Keras当然也准备了很多常用到的神经网络结构,均放在tensorflow.keras.layers包下,这些会在以后学习深挖。
个人总结构建模型这一步,也就是定义正向传播(Forward Propagation)的过程,强调如何从Input -> Output。
Model常用属性和方法
在此简单记录一些tensorflow.keras.Model中常用的属性和方法备忘:
model.layers
:该属性即为构建模型时加入的层结构列表;model.weights
:该属性即为模型中的所有参数,虽然属性名叫weights
,实际上包括偏置bias
参数;model.metrics_names
:该属性为模型训练或测试之后输出对应的标签,往往就是损失(loss)和各种监控指标(metrics)。注意只有在模型经过实际数据的训练(fit)或测试(evaluate)之后,该属性才会有值;model.summary()
:该方法比较常用,就是把模型的总结打印出来,可以快速概览模型的层级结构和参数数量,如:Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= keras_layer (KerasLayer) (None, 20) 400020 _________________________________________________________________ dense (Dense) (None, 16) 336 _________________________________________________________________ dense_1 (Dense) (None, 1) 17 ================================================================= Total params: 400,373 Trainable params: 400,373 Non-trainable params: 0 _________________________________________________________________
model.save_weights(filepath)
和model.load_weights(filepath)
:这一对方法就是用于存储和读取模型所有参数的方法,注意仅仅是存储参数,并没有存储模型的结构;model.save(filepath)
:该方法就是完整存储模型的方法,存储了模型的结构、参数以及优化器的状态。
其他方法就是模型训练、评估等关键流程的方法了,将会在下文中继续介绍。
模型编译
该步骤即为调用tensorflow.keras.Model.compile方法,其主要目的在于定义模型的损失函数、优化器和训练或测试过程中想要监控的指标,对应地就是方法中loss
、optimizer
、metrics
三个参数:
loss
参数(损失函数)的值就是tensorflow.keras.losses包下的类;optimizer
参数(优化器)的值就是tensorflow.keras.optimizers包下的类;metrics
参数是一个列表,列表中的值代表要监控的指标,具体即为tensorflow.keras.metrics包下的类。
这三个参数既可以是某个具体实例,也可以使用代表该类的字符串。如下例就是设置模型使用二分类交叉熵作为损失函数、使用Adam优化器来更新参数、监控指标为正确率:
model.compile(optimizer='adam',
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
个人总结模型编译这一步,也就是定义反向传播(Backward Propagation)的过程,强调了模型的目标和更新参数的方式。
模型训练
该步骤即为调用tensorflow.keras.Model.fit方法,该方法就是用于定义模型的具体训练过程中的各种设置,参数较多,下面按重要性从大到小的方式列出这些参数:
x
:训练集的输入,可以是Numpy的ndarray、TensorFlow的tensor,此时每一行代表一个数据。也可以是tensorflow.data.Dataset或tensorflow.keras.utils.Sequence;y
:x
对应的真实值,需要与x
类型一致(ndarray或tensor)。但若x
为tensorflow.data.Dataset或tensorflow.keras.utils.Sequence,则不需要y
,因为这两种类型中已经包含了输入值和真实值了;batch_size
:每一次参数更新输入的数据量,默认值为32。若x
为Dataset
或Sequence
时,也不需要该参数;epochs
:全部完整遍历训练集多少次;verbose
:输出模式。0 = silent, 1 = progress bar, 2 = one line per epoch。默认为1。非0时,均会打印出loss和compile时设置的metrics;validation_data
:验证集,一个二元组,分别为验证集的输入和输出;validation_split
:0-1的小数,即从训练集中分割出多少比例作为验证集;callbacks
:一个列表,其中元素均为tf.keras.callbacks.Callback或其子类的实例。该类中包含了各种训练过程中的回调函数,如on_batch_begin
、on_batch_end
、on_epoch_begin
、on_epoch_end
等。这些函数也都见名知意,就是在对应的训练过程的某个阶段开始或结束后的回调函数。如下例就是在每个epoch结束的时候打印一个“.”:class PrintDot(keras.callbacks.Callback): def on_epoch_end(self, epoch, logs): print('.', end='') history = model.fit( normed_train_data, train_labels, epochs=1000, validation_split = 0.2, verbose=0, callbacks=[PrintDot()])
训练记录
fit
函数的返回值是一个tensorflow.python.keras.callbacks.History对象,其中记录了训练过程中的一些细节:
- history:一个字典,其中包括了每个epoch训练集和验证集的损失和监控指标,带有前缀"val_"的key对应的就是验证集的记录。如:
{'accuracy': [0.62166667, 0.6613333, 0.6961333], 'loss': [0.6447853596051534, 0.5929351980527242, 0.5533953377087911], 'val_accuracy': [0.6462, 0.6841, 0.7026], 'val_loss': [0.6143643766641617, 0.5779335916042327, 0.542848551273346]}
; - params:一个字典,记录的就是训练的各种参数,如:
{'batch_size': None, 'epochs': 20, 'steps': 30, 'samples': None, 'verbose': 0, 'do_validation': True, 'metrics': ['loss', 'accuracy', 'val_loss', 'val_accuracy']}
; - epoch:一个整数数组,记录epoch的序数,长度与history中每个value的长度一致。往往以epoch为x,history中某个value作为y,绘制出训练过程中某个监控指标的变化情况。
模型评估
该步骤即为调用tensorflow.keras.Model.evaluate方法,该方法即为通过测试集数据来评估训练后的模型。该方法有一个参数return_dict
决定了返回值类型:默认为False
,表示返回值是一个列表,其中包括测试集的损失(loss)和监控指标(metrics);为True
时,则返回值是一个字典,字典的key就是损失和监控指标的name
属性,可以通过model.metrics_names
获取当前模型中所有监控指标的名称。
该方法中很多参数与训练模型的fit
函数类似:
x
和y
:即为测试集的输入和输出;batch_size
:批次大小;verbose
:输出模式;callbacks
:回调函列表;
模型使用
该步骤即为调用tensorflow.keras.Model.predict方法,使用已经训练后的模型对未知输入数据进行推断。由于是对未知数据进行推断,因此该方法中也就只需要x
参数设置输入值,输入数据按行(第一个维度)排列。返回值即为推断结果,是numpy的ndarray类型,同样按行排列。其他参数,如batch_size
、verbose
等与fit
和evaluate
方法中一样。