最新版本:https://www.mashangxue123.com/tensorflow/tf2-guide-keras-training_and_evaluation.html 英文版本:https://tensorflow.google.cn/beta/guide/keras/training_and_evaluation 翻译建议PR:https://github.com/mashangxue/tensorflow2-zh/edit/master/r2/guide/keras/training_and_evaluation.md
本指南涵盖了TensorFlow 2.0在两种主要情况下的训练、评估和预测(推理)模型:
model.fit()
, model.evaluate()
, model.predict()
)。这将在“使用内置的训练和评估循环”一节中讨论。GradientTape
对象从头开始编写自定义循环时。这在 “从零开始编写您自己的训练和评估循环” 小节中有介绍。一般来说,无论您是使用内置循环还是编写自己的循环,模型训练和评估在每种Keras模型(Sequential顺序模型、使用使用函数式API构建的模型以及通过模型子类从零开始编写的模型)中都严格按照相同的方式工作。
本指南不包括分布式训练。
安装
1pip install pydot
2apt-get install graphviz
3pip install tensorflow-gpu==2.0.0-alpha0
导入
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.
将数据传递给模型的内置训练循环时,您应该使用Numpy数组(如果数据很小并且适合内存)或tf.data数据集对象。在接下来的几段中,我们将使用MNIST数据集作为Numpy数组,以演示如何使用优化器,损失和指标。
让我们考虑以下模型(这里,我们使用Functional API构建,但它也可以是顺序模型或子类模型):
xxxxxxxxxx
91from tensorflow import keras
2from tensorflow.keras import layers
3
4inputs = keras.Input(shape=(784,), name='digits')
5x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
6x = layers.Dense(64, activation='relu', name='dense_2')(x)
7outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
8
9model = keras.Model(inputs=inputs, outputs=outputs)
以下是典型的端到端工作流程的外观,包括训练,对原始训练数据生成的保留集的验证,以及最终对测试数据的评估:
xxxxxxxxxx
461# Load a toy dataset for the sake of this example
2(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
3
4# Preprocess the data (these are Numpy arrays)
5x_train = x_train.reshape(60000, 784).astype('float32') / 255
6x_test = x_test.reshape(10000, 784).astype('float32') / 255
7
8# Reserve 10,000 samples for validation
9x_val = x_train[-10000:]
10y_val = y_train[-10000:]
11x_train = x_train[:-10000]
12y_train = y_train[:-10000]
13
14# Specify the training configuration (optimizer, loss, metrics)
15model.compile(optimizer=keras.optimizers.RMSprop(), # Optimizer
16 # Loss function to minimize
17 loss=keras.losses.SparseCategoricalCrossentropy(),
18 # List of metrics to monitor
19 metrics=[keras.metrics.SparseCategoricalAccuracy()])
20
21# Train the model by slicing the data into "batches"
22# of size "batch_size", and repeatedly iterating over
23# the entire dataset for a given number of "epochs"
24print('# Fit model on training data')
25history = model.fit(x_train, y_train,
26 batch_size=64,
27 epochs=3,
28 # We pass some validation for
29 # monitoring validation loss and metrics
30 # at the end of each epoch
31 validation_data=(x_val, y_val))
32
33# The returned "history" object holds a record
34# of the loss values and metric values during training
35print('\nhistory dict:', history.history)
36
37# Evaluate the model on the test data using `evaluate`
38print('\n# Evaluate on test data')
39results = model.evaluate(x_test, y_test, batch_size=128)
40print('test loss, test acc:', results)
41
42# Generate predictions (probabilities -- the output of the last layer)
43# on new data using `predict`
44print('\n# Generate predictions for 3 samples')
45predictions = model.predict(x_test[:3])
46print('predictions shape:', predictions.shape)
要训练合适的模型,您需要指定一个损失函数,一个优化器,以及可选的一些要监控的指标。
您将这些作为 compile()
方法的参数传递给模型:
xxxxxxxxxx
31model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
2 loss=keras.losses.SparseCategoricalCrossentropy(),
3 metrics=[keras.metrics.SparseCategoricalAccuracy()])
metrics
参数应该是一个列表(您的模型可以包含任意数量的度量标准)。
如果您的模型有多个输出,您可以为每个输出指定不同的损失和度量,并且您可以调整每个输出对模型总损失的贡献。 您将在“将数据传递到多输入、多输出模型”一节中找到更多关于此的详细信息。
注意,在很多情况下,损失和指标是通过字符串标识符指定的,作为一种快捷方式:
xxxxxxxxxx
31model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
2 loss='sparse_categorical_crossentropy',
3 metrics=['sparse_categorical_accuracy'])
为了以后的重用,我们将模型定义和编译步骤放在函数中;我们将在本指南的不同示例中多次调用它们。
xxxxxxxxxx
141def get_uncompiled_model():
2 inputs = keras.Input(shape=(784,), name='digits')
3 x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
4 x = layers.Dense(64, activation='relu', name='dense_2')(x)
5 outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
6 model = keras.Model(inputs=inputs, outputs=outputs)
7 return model
8
9def get_compiled_model():
10 model = get_uncompiled_model()
11 model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
12 loss='sparse_categorical_crossentropy',
13 metrics=['sparse_categorical_accuracy'])
14 return model
通常,您不必从头开始创建自己的损失,指标或优化器,因为您需要的可能已经是Keras API的一部分: Optimizers优化器:
SGD()
(with or without momentum)RMSprop()
Adam()
Losses损失:
MeanSquaredError()
KLDivergence()
CosineSimilarity()
Metrics指标:
AUC()
Precision()
Recall()
如果您需要不属于API的指标,则可以通过继承Metric类轻松创建自定义指标。
您需要实现4种方法:
__init__(self)
, 您将在其中为指标创建状态变量update_state(self, y_true, y_pred, sample_weight=None)
, 它使用目标y_true
和模型预测y_pred
来更新状态变量。result(self)
, 它使用状态变量来计算最终结果。reset_states(self)
, 它重新初始化度量的状态。状态更新和结果计算是分开的(分别在update_state()
和 result()
中)因为在某些情况下,结果计算可能非常昂贵,并且只能定期进行。
这是一个简单的例子,展示了如何实现一个 CatgoricalTruePositives
指标,它计算了正确分类为属于给定类的样本数量:
xxxxxxxxxx
301class CatgoricalTruePositives(keras.metrics.Metric):
2
3 def __init__(self, name='categorical_true_positives', **kwargs):
4 super(CatgoricalTruePositives, self).__init__(name=name, **kwargs)
5 self.true_positives = self.add_weight(name='tp', initializer='zeros')
6
7 def update_state(self, y_true, y_pred, sample_weight=None):
8 y_pred = tf.argmax(y_pred)
9 values = tf.equal(tf.cast(y_true, 'int32'), tf.cast(y_pred, 'int32'))
10 values = tf.cast(values, 'float32')
11 if sample_weight is not None:
12 sample_weight = tf.cast(sample_weight, 'float32')
13 values = tf.multiply(values, sample_weight)
14 self.true_positives.assign_add(tf.reduce_sum(values))
15
16 def result(self):
17 return self.true_positives
18
19 def reset_states(self):
20 # The state of the metric will be reset at the start of each epoch.
21 self.true_positives.assign(0.)
22
23
24model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
25 loss=keras.losses.SparseCategoricalCrossentropy(),
26 metrics=[CatgoricalTruePositives()])
27model.fit(x_train, y_train,
28 batch_size=64,
29 epochs=3)
30
绝大多数损失和指标可以从y_true
和y_pred
计算,其中y_pred
是模型的输出。但不是全部。例如,正则化损失可能仅需要激活层(在这种情况下没有目标),并且该激活可能不是模型输出。
在这种情况下,您可以从自定义图层的call
方法中调用 self.add_loss(loss_value)
。这是一个添加活动正则化的简单示例(请注意,活动正则化是内置于所有Keras层中的 - 此层仅用于提供具体示例):
xxxxxxxxxx
241class ActivityRegularizationLayer(layers.Layer):
2
3 def call(self, inputs):
4 self.add_loss(tf.reduce_sum(inputs) * 0.1)
5 return inputs # Pass-through layer.
6
7inputs = keras.Input(shape=(784,), name='digits')
8x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
9
10# Insert activity regularization as a layer
11x = ActivityRegularizationLayer()(x)
12
13x = layers.Dense(64, activation='relu', name='dense_2')(x)
14outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
15
16model = keras.Model(inputs=inputs, outputs=outputs)
17model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
18 loss='sparse_categorical_crossentropy')
19
20# The displayed loss will be much higher than before
21# due to the regularization component.
22model.fit(x_train, y_train,
23 batch_size=64,
24 epochs=1)
您可以执行相同的记录度量标准值:
xxxxxxxxxx
281class MetricLoggingLayer(layers.Layer):
2
3 def call(self, inputs):
4 # The `aggregation` argument defines
5 # how to aggregate the per-batch values
6 # over each epoch:
7 # in this case we simply average them.
8 self.add_metric(keras.backend.std(inputs),
9 name='std_of_activation',
10 aggregation='mean')
11 return inputs # Pass-through layer.
12
13
14inputs = keras.Input(shape=(784,), name='digits')
15x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
16
17# Insert std logging as a layer.
18x = MetricLoggingLayer()(x)
19
20x = layers.Dense(64, activation='relu', name='dense_2')(x)
21outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
22
23model = keras.Model(inputs=inputs, outputs=outputs)
24model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
25 loss='sparse_categorical_crossentropy')
26model.fit(x_train, y_train,
27 batch_size=64,
28 epochs=1)
xxxxxxxxxx
1150000/50000 [==============================] - 4s 76us/sample - loss: 0.3366 - std_of_activation: 0.9773
在 Functional API 中,您还可以调用 model.add_loss(loss_tensor)
, 或 model.add_metric(metric_tensor, name, aggregation)
。
这是一个简单的例子:
xxxxxxxxxx
171inputs = keras.Input(shape=(784,), name='digits')
2x1 = layers.Dense(64, activation='relu', name='dense_1')(inputs)
3x2 = layers.Dense(64, activation='relu', name='dense_2')(x1)
4outputs = layers.Dense(10, activation='softmax', name='predictions')(x2)
5model = keras.Model(inputs=inputs, outputs=outputs)
6
7model.add_loss(tf.reduce_sum(x1) * 0.1)
8
9model.add_metric(keras.backend.std(x1),
10 name='std_of_activation',
11 aggregation='mean')
12
13model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
14 loss='sparse_categorical_crossentropy')
15model.fit(x_train, y_train,
16 batch_size=64,
17 epochs=1)
xxxxxxxxxx
1150000/50000 [==============================] - 4s 80us/sample - loss: 2.5158 - std_of_activation: 0.0020
在您看到的第一个端到端示例中,我们使用 validation_data
参数将Numpy数组 (x_val, y_val)
的元组传递给模型,以便在每个时期结束时评估验证损失和验证指标。
这是另一个选项:参数 validation_split
允许您自动保留部分训练数据以进行验证。参数值表示要为验证保留的数据的分数,因此应将其设置为大于0且小于1的数字。例如,validation_split=0.2
表示“使用20%的数据进行验证”,validation_split=0.6
表示“使用60%的数据进行验证”。
计算验证的方法是:在任何混洗之前,通过fit
调用接收的数组的最后x%样本。
在使用Numpy数据进行训练时,您只能使用 validation_split
。
xxxxxxxxxx
21model = get_compiled_model()
2model.fit(x_train, y_train, batch_size=64, validation_split=0.2, epochs=3)
输出
xxxxxxxxxx
71Train on 40000 samples, validate on 10000 samples
2Epoch 1/3
340000/40000 [==============================] - 3s 82us/sample - loss: 0.3735 - sparse_categorical_accuracy: 0.8951 - val_loss: 0.2413 - val_sparse_categorical_accuracy: 0.9272
4Epoch 2/3
540000/40000 [==============================] - 3s 82us/sample - loss: 0.1688 - sparse_categorical_accuracy: 0.9499 - val_loss: 0.1781 - val_sparse_categorical_accuracy: 0.9468
6Epoch 3/3
740000/40000 [==============================] - 3s 79us/sample - loss: 0.1232 - sparse_categorical_accuracy: 0.9638 - val_loss: 0.1518 - val_sparse_categorical_accuracy: 0.9539
在过去的几段中,您已经了解了如何处理损失,度量和优化器,并且您已经看到,当您的数据作为Numpy数组传递时,如何在fit
中使用validation_data
和 validation_split
参数
现在让我们看一下您的数据以tf.data数据集的形式出现的情况。
tf.data API是TensorFlow 2.0中的一组实用程序,用于以快速和可伸缩的方式加载和预处理数据。
有关创建数据集的完整指南,请参阅the tf.data 文档。
您可以将数据集实例直接传递给方法 fit()
, evaluate()
, 和 predict()
:
xxxxxxxxxx
191model = get_compiled_model()
2
3# First, let's create a training Dataset instance.
4# For the sake of our example, we'll use the same MNIST data as before.
5train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
6# Shuffle and slice the dataset.
7train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
8
9# Now we get a test dataset.
10test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))
11test_dataset = test_dataset.batch(64)
12
13# Since the dataset already takes care of batching,
14# we don't pass a `batch_size` argument.
15model.fit(train_dataset, epochs=3)
16
17# You can also evaluate or predict on a dataset.
18print('\n# Evaluate')
19model.evaluate(test_dataset)
输出:
xxxxxxxxxx
91Epoch 1/3
2782/782 [==============================] - 5s 7ms/step - loss: 0.3250 - sparse_categorical_accuracy: 0.9074
3Epoch 2/3
4782/782 [==============================] - 4s 6ms/step - loss: 0.1484 - sparse_categorical_accuracy: 0.9559
5Epoch 3/3
6782/782 [==============================] - 4s 5ms/step - loss: 0.1074 - sparse_categorical_accuracy: 0.9685
7
8# Evaluate
9157/157 [==============================] - 1s 3ms/step - loss: 0.1137 - sparse_categorical_accuracy: 0.9665
请注意,数据集在每个周期的末尾都会重置,因此可以重复使用下一个周期。
如果您只想从此数据集中对特定数量的批次运行训练,则可以传递 steps_per_epoch
参数,该参数指定在继续下一个周期之前使用此数据集运行模型的训练步数。
如果这样做,数据集不会在每个周期的末尾重置,而是我们只是继续绘制下一批。数据集最终会耗尽数据(除非它是一个无限循环的数据集)。
xxxxxxxxxx
81model = get_compiled_model()
2
3# Prepare the training dataset
4train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
5train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
6
7# Only use the 100 batches per epoch (that's 64 * 100 samples)
8model.fit(train_dataset.take(100), epochs=3)
xxxxxxxxxx
61Epoch 1/3
2100/100 [==============================] - 1s 11ms/step - loss: 0.7733 - sparse_categorical_accuracy: 0.8067
3Epoch 2/3
4100/100 [==============================] - 0s 5ms/step - loss: 0.3706 - sparse_categorical_accuracy: 0.8922
5Epoch 3/3
6100/100 [==============================] - 1s 5ms/step - loss: 0.3379 - sparse_categorical_accuracy: 0.9011
您可以将数据集实例作为fit
中的validation_data
参数传递:
xxxxxxxxxx
111model = get_compiled_model()
2
3# Prepare the training dataset
4train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
5train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
6
7# Prepare the validation dataset
8val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
9val_dataset = val_dataset.batch(64)
10
11model.fit(train_dataset, epochs=3, validation_data=val_dataset)
xxxxxxxxxx
61Epoch 1/3
2782/782 [==============================] - 7s 8ms/step - loss: 0.3440 - sparse_categorical_accuracy: 0.9020 - val_loss: 0.1838 - val_sparse_categorical_accuracy: 0.9490
3Epoch 2/3
4782/782 [==============================] - 7s 9ms/step - loss: 0.1649 - sparse_categorical_accuracy: 0.9515 - val_loss: 0.1391 - val_sparse_categorical_accuracy: 0.9603
5Epoch 3/3
6782/782 [==============================] - 8s 10ms/step - loss: 0.1216 - sparse_categorical_accuracy: 0.9645 - val_loss: 0.1208 - val_sparse_categorical_accuracy: 0.9672
在每个周期结束时,模型将迭代验证数据集并计算验证损失和验证指标。
如果你想只在这个数据集中特定数量的批次上运行验证,你可以传递“validation_steps”参数,它指定了模型在中断验证并进入下一个周期之前,应该与验证数据集一起运行多少个验证步骤:
xxxxxxxxxx
141model = get_compiled_model()
2
3# Prepare the training dataset
4train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
5train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
6
7# Prepare the validation dataset
8val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
9val_dataset = val_dataset.batch(64)
10
11model.fit(train_dataset, epochs=3,
12 # Only run validation using the first 10 batches of the dataset
13 # using the `validation_steps` argument
14 validation_data=val_dataset, validation_steps=10)
xxxxxxxxxx
61Epoch 1/3
2782/782 [==============================] - 9s 12ms/step - loss: 0.3359 - sparse_categorical_accuracy: 0.9053 - val_loss: 0.3095 - val_sparse_categorical_accuracy: 0.9187
3Epoch 2/3
4782/782 [==============================] - 7s 9ms/step - loss: 0.1593 - sparse_categorical_accuracy: 0.9528 - val_loss: 0.2196 - val_sparse_categorical_accuracy: 0.9438
5Epoch 3/3
6782/782 [==============================] - 7s 9ms/step - loss: 0.1158 - sparse_categorical_accuracy: 0.9661 - val_loss: 0.1840 - val_sparse_categorical_accuracy: 0.9469
请注意,验证数据集将在每次使用后重置(这样您将始终评估从epoch到epoch的相同样本)。从数据集对象进行训练时,不支持参数validation_split(从训练数据生成保持集),因为此功能需要能够索引数据集的样本,这通常是数据集API无法实现的。
除了Numpy数组和TensorFlow数据集之外,还可以使用Pandas数据帧或产生批量的Python生成器来训练Keras模型。
通常,如果数据很小并且适合内存,我们建议您使用Numpy输入数据,否则使用数据集。
除了输入数据和目标数据之外,还可以在使用 fit
时将样本权重或类权重传递给模型:
sample_weight
和class_weight
参数。 (input_batch, target_batch, sample_weight_batch)
."sample weights" 数组是一个数字数组,用于指定批处理中每个样本在计算总损失时应具有多少权重。它通常用于不平衡的分类问题(这个想法是为了给予很少见的类别更多的权重)。当使用的权重是1和0时,该数组可以用作损失函数的掩码(完全丢弃某些样本对总损失的贡献)。
"class weights" 字典是同一概念的更具体的实例:它将类索引映射到应该用于属于该类的样本的样本权重。例如,如果类“0”比数据中的类“1”少两倍,则可以使用 class_weight={0: 1., 1: 0.5}
.
这是一个Numpy示例,我们使用类权重class weights或样本权重sample weights来更加重视 class #5 的正确分类(这是MNIST数据集中的数字“5”)。
xxxxxxxxxx
211import numpy as np
2
3class_weight = {0: 1., 1: 1., 2: 1., 3: 1., 4: 1.,
4 # Set weight "2" for class "5",
5 # making this class 2x more important
6 5: 2.,
7 6: 1., 7: 1., 8: 1., 9: 1.}
8model.fit(x_train, y_train,
9 class_weight=class_weight,
10 batch_size=64,
11 epochs=4)
12
13# Here's the same example using `sample_weight` instead:
14sample_weight = np.ones(shape=(len(y_train),))
15sample_weight[y_train == 5] = 2.
16
17model = get_compiled_model()
18model.fit(x_train, y_train,
19 sample_weight=sample_weight,
20 batch_size=64,
21 epochs=4)
xxxxxxxxxx
51Epoch 1/4
250000/50000 [==============================] - 4s 89us/sample - loss: 0.1040 - sparse_categorical_accuracy: 0.9715
3.....
4Epoch 4/4
550000/50000 [==============================] - 4s 83us/sample - loss: 0.1016 - sparse_categorical_accuracy: 0.9719
这是一个匹配的数据集示例:
xxxxxxxxxx
131sample_weight = np.ones(shape=(len(y_train),))
2sample_weight[y_train == 5] = 2.
3
4# Create a Dataset that includes sample weights
5# (3rd element in the return tuple).
6train_dataset = tf.data.Dataset.from_tensor_slices(
7 (x_train, y_train, sample_weight))
8
9# Shuffle and slice the dataset.
10train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
11
12model = get_compiled_model()
13model.fit(train_dataset, epochs=3)
xxxxxxxxxx
61Epoch 1/3
2782/782 [==============================] - 9s 11ms/step - loss: 0.3666 - sparse_categorical_accuracy: 0.9046
3Epoch 2/3
4782/782 [==============================] - 7s 9ms/step - loss: 0.1646 - sparse_categorical_accuracy: 0.9539
5Epoch 3/3
6782/782 [==============================] - 7s 9ms/step - loss: 0.1178 - sparse_categorical_accuracy: 0.9677
在前面的例子中,我们考虑的是一个带有单个输入的模型(形状为 (764,)
的张量)和单个输出(形状为 (10,)
的预测张量)。但是具有多个输入或输出的模型呢?
考虑下面的模型,它有一个形状为 (32, 32, 3)
的形状输入(即“(高度,宽度,通道)”)和形状为 (None, 10)
的时间序列输入(即“(时间步长,特征)”)。我们的模型将根据这些输入的组合计算两个输出:“得分”(形状为(1,)
和5个类别(形状为(10,)
)的概率分布。
xxxxxxxxxx
191from tensorflow import keras
2from tensorflow.keras import layers
3
4image_input = keras.Input(shape=(32, 32, 3), name='img_input')
5timeseries_input = keras.Input(shape=(None, 10), name='ts_input')
6
7x1 = layers.Conv2D(3, 3)(image_input)
8x1 = layers.GlobalMaxPooling2D()(x1)
9
10x2 = layers.Conv1D(3, 3)(timeseries_input)
11x2 = layers.GlobalMaxPooling1D()(x2)
12
13x = layers.concatenate([x1, x2])
14
15score_output = layers.Dense(1, name='score_output')(x)
16class_output = layers.Dense(5, activation='softmax', name='class_output')(x)
17
18model = keras.Model(inputs=[image_input, timeseries_input],
19 outputs=[score_output, class_output])
让我们绘制这个模型,这样你就可以清楚地看到我们在这里做的事情(请注意,图中显示的形状是批量形状,而不是每个样本的形状)。
xxxxxxxxxx
21
2keras.utils.plot_model(model, 'multi_input_and_output_model.png', show_shapes=True)
在编译时,我们可以通过将损失函数作为列表传递给不同的输出指定不同的损失:
xxxxxxxxxx
41model.compile(
2optimizer=keras.optimizers.RMSprop(1e-3),
3loss=[keras.losses.MeanSquaredError(),
4keras.losses.CategoricalCrossentropy()])
如果我们只将单个损失函数传递给模型,则相同的损失函数将应用于每个输出,这在这里是不合适的。
同样适用于指标:
xxxxxxxxxx
71model.compile(
2optimizer=keras.optimizers.RMSprop(1e-3),
3loss=[keras.losses.MeanSquaredError(),
4keras.losses.CategoricalCrossentropy()],
5metrics=[[keras.metrics.MeanAbsolutePercentageError(),
6keras.metrics.MeanAbsoluteError()],
7[keras.metrics.CategoricalAccuracy()]])
由于我们为输出层指定了名称,因此我们还可以通过dict指定每个输出的损失和指标:
xxxxxxxxxx
71model.compile(
2 optimizer=keras.optimizers.RMSprop(1e-3),
3 loss={'score_output': keras.losses.MeanSquaredError(),
4 'class_output': keras.losses.CategoricalCrossentropy()},
5 metrics={'score_output': [keras.metrics.MeanAbsolutePercentageError(),
6 keras.metrics.MeanAbsoluteError()],
7 'class_output': [keras.metrics.CategoricalAccuracy()]})
如果您有超过2个输出,我们建议使用显式名称和dicts。
可以给不同的输出特定损失赋予不同的权重(例如,可能希望通过使用loss_weight
参数赋予2x类损失的重要性来保留我们示例中的“得分”损失特权:
xxxxxxxxxx
81model.compile(
2 optimizer=keras.optimizers.RMSprop(1e-3),
3 loss={'score_output': keras.losses.MeanSquaredError(),
4 'class_output': keras.losses.CategoricalCrossentropy()},
5 metrics={'score_output': [keras.metrics.MeanAbsolutePercentageError(),
6 keras.metrics.MeanAbsoluteError()],
7 'class_output': [keras.metrics.CategoricalAccuracy()]},
8 loss_weight={'score_output': 2., 'class_output': 1.})
您还可以选择不计算某些输出的损失,如果这些输出用于预测但不用于训练:
xxxxxxxxxx
91# List loss version
2model.compile(
3 optimizer=keras.optimizers.RMSprop(1e-3),
4 loss=[None, keras.losses.CategoricalCrossentropy()])
5
6# Or dict loss version
7model.compile(
8 optimizer=keras.optimizers.RMSprop(1e-3),
9 loss={'class_output': keras.losses.CategoricalCrossentropy()})
将数据传递给fit
中的多输入或多输出模型的工作方式与在compile
中指定损失函数的方式类似:
你可以传递Numpy数组列表(1:1映射到接收到损失函数的输出)或者将输出名称映射到Numpy训练数据数组。
xxxxxxxxxx
211model.compile(
2 optimizer=keras.optimizers.RMSprop(1e-3),
3 loss=[keras.losses.MeanSquaredError(),
4 keras.losses.CategoricalCrossentropy()])
5
6# Generate dummy Numpy data
7img_data = np.random.random_sample(size=(100, 32, 32, 3))
8ts_data = np.random.random_sample(size=(100, 20, 10))
9score_targets = np.random.random_sample(size=(100, 1))
10class_targets = np.random.random_sample(size=(100, 5))
11
12# Fit on lists
13model.fit([img_data, ts_data], [score_targets, class_targets],
14 batch_size=32,
15 epochs=3)
16
17# Alernatively, fit on dicts
18model.fit({'img_input': img_data, 'ts_input': ts_data},
19 {'score_output': score_targets, 'class_output': class_targets},
20 batch_size=32,
21 epochs=3)
这是数据集用例:类似于我们为Numpy数组所做的,数据集应该返回一个dicts元组。
xxxxxxxxxx
61train_dataset = tf.data.Dataset.from_tensor_slices(
2 ({'img_input': img_data, 'ts_input': ts_data},
3 {'score_output': score_targets, 'class_output': class_targets}))
4train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
5
6model.fit(train_dataset, epochs=3)
Keras中的回调是在训练期间(在周期开始时,批处理结束时,周期结束时等)在不同点调用的对象,可用于实现以下行为:
回调可以作为列表传递给你对 fit
的调用:
xxxxxxxxxx
171model = get_compiled_model()
2
3callbacks = [
4 keras.callbacks.EarlyStopping(
5 # Stop training when `val_loss` is no longer improving
6 monitor='val_loss',
7 # "no longer improving" being defined as "no better than 1e-2 less"
8 min_delta=1e-2,
9 # "no longer improving" being further defined as "for at least 2 epochs"
10 patience=2,
11 verbose=1)
12]
13model.fit(x_train, y_train,
14 epochs=20,
15 batch_size=64,
16 callbacks=callbacks,
17 validation_split=0.2)
ModelCheckpoint
: 定期保存模型。EarlyStopping
: 当训练不再改进验证指标时停止训练。TensorBoard
: 定期编写可在TensorBoard中显示的模型日志(更多细节见“可视化”)。CSVLogger
: 将丢失和指标数据丢失到CSV文件。
您可以通过扩展基类 keras.callbacks.Callback
来创建自定义回调。回调可以通过类属性 self.model
访问其关联的模型。
以下是在训练期间保存每批损失值列表的简单示例:
xxxxxxxxxx
71class LossHistory(keras.callbacks.Callback):
2
3 def on_train_begin(self, logs):
4 self.losses = []
5
6 def on_batch_end(self, batch, logs):
7 self.losses.append(logs.get('loss'))
当您在相对较大的数据集上训练模型时,以频繁的间隔保存模型的检查点至关重要。
实现此目的的最简单方法是使用 ModelCheckpoint
回调:
xxxxxxxxxx
181model = get_compiled_model()
2
3callbacks = [
4 keras.callbacks.ModelCheckpoint(
5 filepath='mymodel_{epoch}.h5',
6 # Path where to save the model
7 # The two parameters below mean that we will overwrite
8 # the current checkpoint if and only if
9 # the `val_loss` score has improved.
10 save_best_only=True,
11 monitor='val_loss',
12 verbose=1)
13]
14model.fit(x_train, y_train,
15 epochs=3,
16 batch_size=64,
17 callbacks=callbacks,
18 validation_split=0.2)
您也可以编写自己的回调来保存和恢复模型。
有关序列化和保存的完整指南,请参见保存和序列化模型指南。
在训练深度学习模型时,一个常见的模式是随着训练的进展逐步减少学习。这通常被称为“学习速度衰减”。
学习衰减计划可以是静态的(预先固定,作为当前周期或当前批处理索引的函数),也可以是动态的(响应模型当前的行为,特别是验证损失)。
您可以通过在优化程序中将计划对象作为learning_rate参数传递,轻松使用静态学习速率衰减计划:
xxxxxxxxxx
81initial_learning_rate = 0.1
2lr_schedule = keras.optimizers.schedules.ExponentialDecay(
3 initial_learning_rate,
4 decay_steps=100000,
5 decay_rate=0.96,
6 staircase=True)
7
8optimizer = keras.optimizers.RMSprop(learning_rate=lr_schedule)
有几个内置的schedule表: ExponentialDecay
, PiecewiseConstantDecay
, PolynomialDecay
, and InverseTimeDecay
.
使用这些schedule对象无法实现动态学习速率计划(例如,当验证损失不再改善时降低学习速率),因为优化器无法访问验证指标。
然而,回调确实可以访问所有指标,包括验证指标!
因此,可以通过使用回调函数来修改优化器上的当前学习率来实现这种模式。
事实上,它甚至内置在 ReduceLROnPlateau
回调函数中。
在训练期间密切关注你的模型的最好方法是使用 TensorBoard ,这是一个基于浏览器的应用程序,你可以在本地运行,它为你提供:
如果您已经使用pip安装了TensorFlow,那么您应该能够从命令行启动TensorBoard:
xxxxxxxxxx
11tensorboard --logdir=/full_path_to_your_logs
在Keras模型和 fit
方法中使用TensorBoard的最简单方法是TensorBoard回调。
在最简单的情况下,只需指定回写日志的位置,就可以了:
xxxxxxxxxx
21tensorboard_cbk = keras.callbacks.TensorBoard(log_dir='/full_path_to_your_logs')
2model.fit(dataset, epochs=10, callbacks=[tensorboard_cbk])
TensorBoard回调有许多有用的选项,包括是否记录嵌入,直方图以及写入日志的频率:
xxxxxxxxxx
51keras.callbacks.TensorBoard(
2 log_dir='/full_path_to_your_logs',
3 histogram_freq=0, # How often to log histogram visualizations
4 embeddings_freq=0, # How often to log embedding visualizations
5 update_freq='epoch') # How often to write logs (default: once per epoch)
如果你想要训练和评估循环的级别低于 fit()
和 evaluate()
提供的的级别,你应该自己编写。它实际上非常简单!但是你应该准备好自己做更多的调试。
在“GradientTape”范围内调用模型使您可以根据损失值检索图层的可训练权重的梯度。使用优化器实例,您可以使用这些梯度来更新这些变量(可以使用model.trainable_weights
检索)。
让我们重用第一部分中的初始MNIST模型,让我们使用带有自定义训练循环的小批量梯度训练它。
xxxxxxxxxx
491# Get the model.
2inputs = keras.Input(shape=(784,), name='digits')
3x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
4x = layers.Dense(64, activation='relu', name='dense_2')(x)
5outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
6model = keras.Model(inputs=inputs, outputs=outputs)
7
8# Instantiate an optimizer.
9optimizer = keras.optimizers.SGD(learning_rate=1e-3)
10# Instantiate a loss function.
11loss_fn = keras.losses.SparseCategoricalCrossentropy()
12
13# Prepare the training dataset.
14batch_size = 64
15train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
16train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)
17
18# Iterate over epochs.
19for epoch in range(3):
20 print('Start of epoch %d' % (epoch,))
21
22 # Iterate over the batches of the dataset.
23 for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
24
25 # Open a GradientTape to record the operations run
26 # during the forward pass, which enables autodifferentiation.
27 with tf.GradientTape() as tape:
28
29 # Run the forward pass of the layer.
30 # The operations that the layer applies
31 # to its inputs are going to be recorded
32 # on the GradientTape.
33 logits = model(x_batch_train) # Logits for this minibatch
34
35 # Compute the loss value for this minibatch.
36 loss_value = loss_fn(y_batch_train, logits)
37
38 # Use the gradient tape to automatically retrieve
39 # the gradients of the trainable variables with respect to the loss.
40 grads = tape.gradient(loss_value, model.trainable_weights)
41
42 # Run one step of gradient descent by updating
43 # the value of the variables to minimize the loss.
44 optimizer.apply_gradients(zip(grads, model.trainable_weights))
45
46 # Log every 200 batches.
47 if step % 200 == 0:
48 print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))
49 print('Seen so far: %s samples' % ((step + 1) * 64))
让我们添加指标。您可以很容易地在这样的训练循环中重用内置的指标(或您编写的自定义指标)。这是流程:
metric.update_state()
metric.result()
metric.reset_states()
让我们使用这些知识在每个周期结束时计算验证数据的 SparseCategoricalAccuracy
:
xxxxxxxxxx
601# Get model
2inputs = keras.Input(shape=(784,), name='digits')
3x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
4x = layers.Dense(64, activation='relu', name='dense_2')(x)
5outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
6model = keras.Model(inputs=inputs, outputs=outputs)
7
8# Instantiate an optimizer to train the model.
9optimizer = keras.optimizers.SGD(learning_rate=1e-3)
10# Instantiate a loss function.
11loss_fn = keras.losses.SparseCategoricalCrossentropy()
12
13# Prepare the metrics.
14train_acc_metric = keras.metrics.SparseCategoricalAccuracy()
15val_acc_metric = keras.metrics.SparseCategoricalAccuracy()
16
17# Prepare the training dataset.
18batch_size = 64
19train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
20train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)
21
22# Prepare the validation dataset.
23val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
24val_dataset = val_dataset.batch(64)
25
26
27# Iterate over epochs.
28for epoch in range(3):
29 print('Start of epoch %d' % (epoch,))
30
31 # Iterate over the batches of the dataset.
32 for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
33 with tf.GradientTape() as tape:
34 logits = model(x_batch_train)
35 loss_value = loss_fn(y_batch_train, logits)
36 grads = tape.gradient(loss_value, model.trainable_weights)
37 optimizer.apply_gradients(zip(grads, model.trainable_weights))
38
39 # Update training metric.
40 train_acc_metric(y_batch_train, logits)
41
42 # Log every 200 batches.
43 if step % 200 == 0:
44 print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))
45 print('Seen so far: %s samples' % ((step + 1) * 64))
46
47 # Display metrics at the end of each epoch.
48 train_acc = train_acc_metric.result()
49 print('Training acc over epoch: %s' % (float(train_acc),))
50 # Reset training metrics at the end of each epoch
51 train_acc_metric.reset_states()
52
53 # Run a validation loop at the end of each epoch.
54 for x_batch_val, y_batch_val in val_dataset:
55 val_logits = model(x_batch_val)
56 # Update val metrics
57 val_acc_metric(y_batch_val, val_logits)
58 val_acc = val_acc_metric.result()
59 val_acc_metric.reset_states()
60 print('Validation acc: %s' % (float(val_acc),))
您在上一节中看到,通过在call
方法中调用 self.add_loss(value)
,可以通过图层添加正则化损失。
在一般情况下,您需要在自定义训练循环中考虑这些损失(除非您自己编写模型并且您已经知道它不会造成这样的损失)。
回想一下上一节的这个例子,它的特点是一个层会产生正则化损失:
xxxxxxxxxx
151class ActivityRegularizationLayer(layers.Layer):
2
3 def call(self, inputs):
4 self.add_loss(1e-2 * tf.reduce_sum(inputs))
5 return inputs
6
7inputs = keras.Input(shape=(784,), name='digits')
8x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
9# Insert activity regularization as a layer
10x = ActivityRegularizationLayer()(x)
11x = layers.Dense(64, activation='relu', name='dense_2')(x)
12outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
13
14model = keras.Model(inputs=inputs, outputs=outputs)
15
当您调用模型时,如下所示:
xxxxxxxxxx
11logits = model(x_train)
它在前向传递期间产生的损失被添加到 model.losses
属性中:
xxxxxxxxxx
21logits = model(x_train[:64])
2print(model.losses)
跟踪损失首先在模型 __call__
开始时清除,因此您只能看到在这一次前进过程中产生的损失。例如,重复调用模型然后查询 losses
只显示最后一次调用期间创建的最新损失:
xxxxxxxxxx
41logits = model(x_train[:64])
2logits = model(x_train[64: 128])
3logits = model(x_train[128: 192])
4print(model.losses)
要在训练期间考虑这些损失,您所要做的就是修改训练循环,将 sum(model.losses)
添加到您的总损失中:
xxxxxxxxxx
201optimizer = keras.optimizers.SGD(learning_rate=1e-3)
2
3for epoch in range(3):
4 print('Start of epoch %d' % (epoch,))
5
6 for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
7 with tf.GradientTape() as tape:
8 logits = model(x_batch_train)
9 loss_value = loss_fn(y_batch_train, logits)
10
11 # Add extra losses created during this forward pass:
12 loss_value += sum(model.losses)
13
14 grads = tape.gradient(loss_value, model.trainable_weights)
15 optimizer.apply_gradients(zip(grads, model.trainable_weights))
16
17 # Log every 200 batches.
18 if step % 200 == 0:
19 print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))
20 print('Seen so far: %s samples' % ((step + 1) * 64))
那是拼图的最后一块!你已经到了本指南的末尾。
现在您已经了解了有关使用内置训练循环以及从头开始编写自己的知识的所有信息。