此页面由 Cloud Translation API 翻译。
Switch to English

培训与内置方法评估

在TensorFlow.org上查看 在GitHub上查看源代码 下载笔记本

建立

 import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
 

介绍

本指南涵盖使用内置API进行训练和验证时的训练,评估和预测(推断)模型(例如model.fit()model.evaluate()model.predict() )。

如果您有兴趣在指定自己的训练步骤函数时利用fit() ,请参阅指南“自定义fit()发生的事情”

如果您有兴趣从头开始编写自己的训练和评估循环,请参阅指南“从头开始编写训练循环”

通常,无论您是使用内置循环还是编写自己的循环,模型训练和评估都在每种Keras模型中严格按照相同的方式进行工作-顺序模型,使用Functional API构建的模型以及从头开始编写的模型模型子类化。

本指南不涉及分布式培训。有关分布式培训,请参阅我们的多GPU和分布式培训指南

API概述:第一个端到端示例

将数据传递到模型的内置训练循环时,您应该使用NumPy数组 (如果数据很小并且适合内存)或tf.data Dataset对象 。在接下来的几段中,我们将MNIST数据集用作NumPy数组,以演示如何使用优化程序,损失和指标。

让我们考虑以下模型(在这里,我们使用Functional API进行内置,但是它也可以是顺序模型或子类模型):

 inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, activation="softmax", name="predictions")(x)

model = keras.Model(inputs=inputs, outputs=outputs)
 

这是典型的端到端工作流的样子,包括:

  • 训练
  • 根据原始训练数据生成的保留集进行验证
  • 测试数据评估

在此示例中,我们将使用MNIST数据。

 (x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

# Preprocess the data (these are NumPy arrays)
x_train = x_train.reshape(60000, 784).astype("float32") / 255
x_test = x_test.reshape(10000, 784).astype("float32") / 255

y_train = y_train.astype("float32")
y_test = y_test.astype("float32")

# Reserve 10,000 samples for validation
x_val = x_train[-10000:]
y_val = y_train[-10000:]
x_train = x_train[:-10000]
y_train = y_train[:-10000]
 
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

我们指定训练配置(优化器,损失,指标):

 model.compile(
    optimizer=keras.optimizers.RMSprop(),  # Optimizer
    # Loss function to minimize
    loss=keras.losses.SparseCategoricalCrossentropy(),
    # List of metrics to monitor
    metrics=[keras.metrics.SparseCategoricalAccuracy()],
)
 

我们调用fit() ,它将通过将数据切成大小为“ batch_size”的“批次”,并针对给定数量的“时期”重复遍历整个数据集来训练模型。

 print("Fit model on training data")
history = model.fit(
    x_train,
    y_train,
    batch_size=64,
    epochs=2,
    # We pass some validation for
    # monitoring validation loss and metrics
    # at the end of each epoch
    validation_data=(x_val, y_val),
)
 
Fit model on training data
Epoch 1/2
782/782 [==============================] - 2s 2ms/step - loss: 0.3476 - sparse_categorical_accuracy: 0.9005 - val_loss: 0.1868 - val_sparse_categorical_accuracy: 0.9452
Epoch 2/2
782/782 [==============================] - 2s 2ms/step - loss: 0.1622 - sparse_categorical_accuracy: 0.9517 - val_loss: 0.1334 - val_sparse_categorical_accuracy: 0.9616

返回的“历史”对象保留训练期间的损失值和度量值的记录:

 history.history
 
{'loss': [0.34755897521972656, 0.16218683123588562],
 'sparse_categorical_accuracy': [0.9004799723625183, 0.9516800045967102],
 'val_loss': [0.18684418499469757, 0.13335685431957245],
 'val_sparse_categorical_accuracy': [0.9452000260353088, 0.9616000056266785]}

我们通过evaluate()在测试数据上evaluate()模型:

 # Evaluate the model on the test data using `evaluate`
print("Evaluate on test data")
results = model.evaluate(x_test, y_test, batch_size=128)
print("test loss, test acc:", results)

# Generate predictions (probabilities -- the output of the last layer)
# on new data using `predict`
print("Generate predictions for 3 samples")
predictions = model.predict(x_test[:3])
print("predictions shape:", predictions.shape)
 
Evaluate on test data
79/79 [==============================] - 0s 2ms/step - loss: 0.1379 - sparse_categorical_accuracy: 0.9594
test loss, test acc: [0.13789668679237366, 0.9593999981880188]
Generate predictions for 3 samples
predictions shape: (3, 10)

现在,让我们详细回顾此工作流程的每一部分。

compile()方法:指定损失,指标和优化器

要使用fit()训练模型,您需要指定损失函数,优化器以及可选的一些要监视的指标。

您将它们作为compile()方法的参数传递给模型:

 model.compile(
    optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
    loss=keras.losses.SparseCategoricalCrossentropy(),
    metrics=[keras.metrics.SparseCategoricalAccuracy()],
)
 

metrics参数应为列表-您的模型可以具有任意数量的度量。

如果模型有多个输出,则可以为每个输出指定不同的损失和度量,并且可以调制每个输出对模型总损失的贡献。您可以在“将数据传递到多输入多输出模型”部分中找到有关此内容的更多详细信息。

请注意,如果您对默认设置感到满意,那么在很多情况下,可以通过字符串标识符将优化器,损失和指标指定为快捷方式:

 model.compile(
    optimizer="rmsprop",
    loss="sparse_categorical_crossentropy",
    metrics=["sparse_categorical_accuracy"],
)
 

为了以后重用,让我们在函数中放入模型定义和编译步骤。我们将在本指南的不同示例中多次调用它们。

 def get_uncompiled_model():
    inputs = keras.Input(shape=(784,), name="digits")
    x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
    x = layers.Dense(64, activation="relu", name="dense_2")(x)
    outputs = layers.Dense(10, activation="softmax", name="predictions")(x)
    model = keras.Model(inputs=inputs, outputs=outputs)
    return model


def get_compiled_model():
    model = get_uncompiled_model()
    model.compile(
        optimizer="rmsprop",
        loss="sparse_categorical_crossentropy",
        metrics=["sparse_categorical_accuracy"],
    )
    return model

 

提供许多内置的优化器,损失和指标

通常,您不必从头开始创建自己的损失,指标或优化程序,因为所需的可能已经是Keras API的一部分:

优化器:

  • SGD() (有或没有动量)
  • RMSprop()
  • Adam()
  • 等等

损失:

  • MeanSquaredError()
  • KLDivergence()
  • CosineSimilarity()
  • 等等

指标:

  • AUC()
  • Precision()
  • Recall()
  • 等等

海关损失

使用Keras提供两种方式来提供自定义损失。第一个示例创建一个接受输入y_truey_pred 。以下示例显示了一个损失函数,该函数计算实际数据和预测之间的均方误差:

 def custom_mean_squared_error(y_true, y_pred):
    return tf.math.reduce_mean(tf.square(y_true - y_pred))


model = get_uncompiled_model()
model.compile(optimizer=keras.optimizers.Adam(), loss=custom_mean_squared_error)

# We need to one-hot encode the labels to use MSE
y_train_one_hot = tf.one_hot(y_train, depth=10)
model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)
 
782/782 [==============================] - 1s 1ms/step - loss: 0.0156

<tensorflow.python.keras.callbacks.History at 0x7fda002fecf8>

如果您需要一个损失函数,该函数需要使用y_truey_pred旁边的参数,则可以将tf.keras.losses.Loss类子类化,并实现以下两种方法:

  • __init__(self) :接受在损失函数调用期间传递的参数
  • call(self, y_true, y_pred) :使用目标(y_true)和模型预测(y_pred)计算模型的损失

假设您要使用均方误差,但是要加上一个附加项,它会消除激励值远离0.5的预测值(我们假设分类目标是一键编码的,并且取值介于0到1之间)。这激励了模型不要过于自信,这可能有助于减少过度拟合(在尝试之前,我们不知道它是否有效!)。

这是您的处理方式:

 class CustomMSE(keras.losses.Loss):
    def __init__(self, regularization_factor=0.1, name="custom_mse"):
        super().__init__(name=name)
        self.regularization_factor = regularization_factor

    def call(self, y_true, y_pred):
        mse = tf.math.reduce_mean(tf.square(y_true - y_pred))
        reg = tf.math.reduce_mean(tf.square(0.5 - y_pred))
        return mse + reg * self.regularization_factor


model = get_uncompiled_model()
model.compile(optimizer=keras.optimizers.Adam(), loss=CustomMSE())

y_train_one_hot = tf.one_hot(y_train, depth=10)
model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)
 
782/782 [==============================] - 1s 1ms/step - loss: 0.0386

<tensorflow.python.keras.callbacks.History at 0x7fda002b95f8>

自定义指标

如果需要不属于API的指标,则可以通过将tf.keras.metrics.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() ,因为在某些情况下,结果计算可能会非常昂贵,并且只能定期执行。

这是一个简单的示例,显示了如何实现CategoricalTruePositives指标,该指标计算将多少样本正确分类为属于给定类别的样本:

 class CategoricalTruePositives(keras.metrics.Metric):
    def __init__(self, name="categorical_true_positives", **kwargs):
        super(CategoricalTruePositives, self).__init__(name=name, **kwargs)
        self.true_positives = self.add_weight(name="ctp", initializer="zeros")

    def update_state(self, y_true, y_pred, sample_weight=None):
        y_pred = tf.reshape(tf.argmax(y_pred, axis=1), shape=(-1, 1))
        values = tf.cast(y_true, "int32") == tf.cast(y_pred, "int32")
        values = tf.cast(values, "float32")
        if sample_weight is not None:
            sample_weight = tf.cast(sample_weight, "float32")
            values = tf.multiply(values, sample_weight)
        self.true_positives.assign_add(tf.reduce_sum(values))

    def result(self):
        return self.true_positives

    def reset_states(self):
        # The state of the metric will be reset at the start of each epoch.
        self.true_positives.assign(0.0)


model = get_uncompiled_model()
model.compile(
    optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
    loss=keras.losses.SparseCategoricalCrossentropy(),
    metrics=[CategoricalTruePositives()],
)
model.fit(x_train, y_train, batch_size=64, epochs=3)
 
Epoch 1/3
782/782 [==============================] - 1s 2ms/step - loss: 0.3406 - categorical_true_positives: 45130.0000
Epoch 2/3
782/782 [==============================] - 1s 2ms/step - loss: 0.1591 - categorical_true_positives: 47607.0000
Epoch 3/3
782/782 [==============================] - 2s 2ms/step - loss: 0.1140 - categorical_true_positives: 48302.0000

<tensorflow.python.keras.callbacks.History at 0x7fda00152b70>

处理不符合标准签名的损失和指标

绝大多数损失和指标可以从y_truey_pred进行计算,其中y_pred是模型的输出。但不是所有人。例如,正则化损失可能仅需要激活层(在这种情况下没有目标),并且此激活可能不是模型输出。

在这种情况下,您可以从自定义图层的调用方法内部调用self.add_loss(loss_value) 。以这种方式添加的损失会在训练期间添加到“主要”损失中(传递给compile()损失)。这是一个添加活动正则化的简单示例(请注意,活动正则化内置在所有Keras层中-该层仅是为了提供一个具体示例):

 class ActivityRegularizationLayer(layers.Layer):
    def call(self, inputs):
        self.add_loss(tf.reduce_sum(inputs) * 0.1)
        return inputs  # Pass-through layer.


inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)

# Insert activity regularization as a layer
x = ActivityRegularizationLayer()(x)

x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, name="predictions")(x)

model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(
    optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
)

# The displayed loss will be much higher than before
# due to the regularization component.
model.fit(x_train, y_train, batch_size=64, epochs=1)
 
782/782 [==============================] - 1s 2ms/step - loss: 2.5240

<tensorflow.python.keras.callbacks.History at 0x7fda0008e828>

您可以使用add_metric()对记录指标值执行相同的操作:

 class MetricLoggingLayer(layers.Layer):
    def call(self, inputs):
        # The `aggregation` argument defines
        # how to aggregate the per-batch values
        # over each epoch:
        # in this case we simply average them.
        self.add_metric(
            keras.backend.std(inputs), name="std_of_activation", aggregation="mean"
        )
        return inputs  # Pass-through layer.


inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)

# Insert std logging as a layer.
x = MetricLoggingLayer()(x)

x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, name="predictions")(x)

model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(
    optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
)
model.fit(x_train, y_train, batch_size=64, epochs=1)
 
782/782 [==============================] - 1s 2ms/step - loss: 0.3414 - std_of_activation: 1.0120

<tensorflow.python.keras.callbacks.History at 0x7fd9b45f2d30>

Functional API中 ,您还可以调用model.add_loss(loss_tensor)model.add_metric(metric_tensor, name, aggregation)

这是一个简单的示例:

 inputs = keras.Input(shape=(784,), name="digits")
x1 = layers.Dense(64, activation="relu", name="dense_1")(inputs)
x2 = layers.Dense(64, activation="relu", name="dense_2")(x1)
outputs = layers.Dense(10, name="predictions")(x2)
model = keras.Model(inputs=inputs, outputs=outputs)

model.add_loss(tf.reduce_sum(x1) * 0.1)

model.add_metric(keras.backend.std(x1), name="std_of_activation", aggregation="mean")

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
)
model.fit(x_train, y_train, batch_size=64, epochs=1)
 
782/782 [==============================] - 1s 2ms/step - loss: 2.5604 - std_of_activation: 0.0023

<tensorflow.python.keras.callbacks.History at 0x7fd9b44ae518>

请注意,当您通过add_loss()传递损失时,可以在没有损失函数的情况下调用compile() ,因为该模型已经具有最小化的损失。

考虑以下LogisticEndpoint层:它以目标和logit作为输入,并通过add_loss()跟踪交叉熵损失。它还通过add_metric()跟踪分类准确性。

 class LogisticEndpoint(keras.layers.Layer):
    def __init__(self, name=None):
        super(LogisticEndpoint, self).__init__(name=name)
        self.loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)
        self.accuracy_fn = keras.metrics.BinaryAccuracy()

    def call(self, targets, logits, sample_weights=None):
        # Compute the training-time loss value and add it
        # to the layer using `self.add_loss()`.
        loss = self.loss_fn(targets, logits, sample_weights)
        self.add_loss(loss)

        # Log accuracy as a metric and add it
        # to the layer using `self.add_metric()`.
        acc = self.accuracy_fn(targets, logits, sample_weights)
        self.add_metric(acc, name="accuracy")

        # Return the inference-time prediction tensor (for `.predict()`).
        return tf.nn.softmax(logits)

 

您可以在具有两个输入(输入数据和目标)的模型中使用它,而无需loss参数即可进行编译,如下所示:

 import numpy as np

inputs = keras.Input(shape=(3,), name="inputs")
targets = keras.Input(shape=(10,), name="targets")
logits = keras.layers.Dense(10)(inputs)
predictions = LogisticEndpoint(name="predictions")(logits, targets)

model = keras.Model(inputs=[inputs, targets], outputs=predictions)
model.compile(optimizer="adam")  # No loss argument!

data = {
    "inputs": np.random.random((3, 3)),
    "targets": np.random.random((3, 10)),
}
model.fit(data)
 
1/1 [==============================] - 0s 1ms/step - loss: 0.9677 - binary_accuracy: 0.0000e+00

<tensorflow.python.keras.callbacks.History at 0x7fda002b9748>

有关训练多输入模型的更多信息,请参见将数据传递到多输入,多输出模型一节

自动区分验证保留集

在第一端部至端示例您看见,我们使用了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

 model = get_compiled_model()
model.fit(x_train, y_train, batch_size=64, validation_split=0.2, epochs=1)
 
625/625 [==============================] - 1s 2ms/step - loss: 0.3745 - sparse_categorical_accuracy: 0.8950 - val_loss: 0.2279 - val_sparse_categorical_accuracy: 0.9333

<tensorflow.python.keras.callbacks.History at 0x7fd9b43c6d68>

来自tf.data数据集的培训和评估

在过去的几段中,您已经了解了如何处理损失,指标和优化程序,还已经了解了如何在将数据作为NumPy数组传递时合适地使用validation_datavalidation_split参数。

现在让我们看一下数据以tf.data.Dataset对象形式出现的tf.data.Dataset

tf.data API是TensorFlow 2.0中的一组实用程序,用于以快速且可扩展的方式加载和预处理数据。

有关创建Datasets的完整指南,请参阅tf.data文档

您可以将Dataset实例直接传递给fit()evaluate()predict()

 model = get_compiled_model()

# First, let's create a training Dataset instance.
# For the sake of our example, we'll use the same MNIST data as before.
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
# Shuffle and slice the dataset.
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Now we get a test dataset.
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))
test_dataset = test_dataset.batch(64)

# Since the dataset already takes care of batching,
# we don't pass a `batch_size` argument.
model.fit(train_dataset, epochs=3)

# You can also evaluate or predict on a dataset.
print("Evaluate")
result = model.evaluate(test_dataset)
dict(zip(model.metrics_names, result))
 
Epoch 1/3
782/782 [==============================] - 2s 2ms/step - loss: 0.3373 - sparse_categorical_accuracy: 0.9045
Epoch 2/3
782/782 [==============================] - 2s 2ms/step - loss: 0.1582 - sparse_categorical_accuracy: 0.9528
Epoch 3/3
782/782 [==============================] - 2s 2ms/step - loss: 0.1146 - sparse_categorical_accuracy: 0.9658
Evaluate
157/157 [==============================] - 0s 1ms/step - loss: 0.1300 - sparse_categorical_accuracy: 0.9601

{'loss': 0.12998636066913605, 'sparse_categorical_accuracy': 0.960099995136261}

请注意,数据集会在每个时期结束时重置,因此可以在下一个时期重复使用。

如果您只想对来自此数据集的特定批次进行训练,则可以传递steps_per_epoch参数,该参数指定在继续下一个时期之前,该模型应使用此数据集运行多少训练步骤。

如果这样做,则不会在每个时期结束时重置数据集,而是继续绘制下一批。数据集最终将用完数据(除非它是无限循环的数据集)。

 model = get_compiled_model()

# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Only use the 100 batches per epoch (that's 64 * 100 samples)
model.fit(train_dataset, epochs=3, steps_per_epoch=100)
 
Epoch 1/3
100/100 [==============================] - 0s 2ms/step - loss: 0.7669 - sparse_categorical_accuracy: 0.8027
Epoch 2/3
100/100 [==============================] - 0s 2ms/step - loss: 0.3694 - sparse_categorical_accuracy: 0.8947
Epoch 3/3
100/100 [==============================] - 0s 2ms/step - loss: 0.3178 - sparse_categorical_accuracy: 0.9080

<tensorflow.python.keras.callbacks.History at 0x7fd9b40cdb70>

使用验证数据集

您可以在fit()中将Dataset实例作为validation_data参数传递:

 model = get_compiled_model()

# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Prepare the validation dataset
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(64)

model.fit(train_dataset, epochs=1, validation_data=val_dataset)
 
782/782 [==============================] - 2s 2ms/step - loss: 0.3401 - sparse_categorical_accuracy: 0.9047 - val_loss: 0.1726 - val_sparse_categorical_accuracy: 0.9521

<tensorflow.python.keras.callbacks.History at 0x7fd9647bef98>

在每个时期结束时,模型将遍历验证数据集并计算验证损失和验证指标。

如果您只想对该数据集中的特定批次运行验证,则可以传递validation_steps参数,该参数指定在中断验证并进入下一个时期之前,模型应使用验证数据集运行多少验证步骤:

 model = get_compiled_model()

# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Prepare the validation dataset
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(64)

model.fit(
    train_dataset,
    epochs=1,
    # Only run validation using the first 10 batches of the dataset
    # using the `validation_steps` argument
    validation_data=val_dataset,
    validation_steps=10,
)
 
782/782 [==============================] - 2s 2ms/step - loss: 0.3402 - sparse_categorical_accuracy: 0.9024 - val_loss: 0.3108 - val_sparse_categorical_accuracy: 0.9250

<tensorflow.python.keras.callbacks.History at 0x7fd9b4049ef0>

请注意,验证数据集将在每次使用后重置(以便您始终可以评估各个时期之间相同的样本)。

Dataset对象进行训练时,不支持参数validation_split (从训练数据生成保留集),因为此功能需要具有索引数据集样本的能力,而这通常是使用Dataset API不可能实现的。

支持其他输入格式

除了NumPy数组,渴望的张量和TensorFlow Datasets ,还可以使用Pandas数据框或从生成大量数据和标签的Python生成器中训练Keras模型。

特别是, keras.utils.Sequence类提供了一个简单的接口来构建Python数据生成器,该数据生成器可以进行多处理,并且可以改组。

通常,我们建议您使用:

  • NumPy输入数据(如果您的数据很小并且适合内存)
  • Dataset对象(如果您有大型数据集,并且需要进行分布式培训)
  • Sequence ,如果你有大量的数据集,你需要做很多定制的Python端处理的,不能在TensorFlow(如果你依赖于数据加载或预处理外部库如)来完成的对象。

使用keras.utils.Sequence对象作为输入

keras.utils.Sequence是一个实用程序,您可以将其子类化以获得具有两个重要属性的Python生成器:

  • 它适用于多处理。
  • 可以将其洗牌(例如,在fit()传递shuffle=True时)。

Sequence必须实现两种方法:

  • __getitem__
  • __len__

__getitem__方法应返回完整的批处理。如果要在各个时期之间修改数据集,则可以实现on_epoch_end

这是一个简单的例子:

 from skimage.io import imread
from skimage.transform import resize
import numpy as np

# Here, `filenames` is list of path to the images
# and `labels` are the associated labels.

class CIFAR10Sequence(Sequence):
    def __init__(self, filenames, labels, batch_size):
        self.filenames, self.labels = filenames, labels
        self.batch_size = batch_size

    def __len__(self):
        return int(np.ceil(len(self.filenames) / float(self.batch_size)))

    def __getitem__(self, idx):
        batch_x = self.filenames[idx * self.batch_size:(idx + 1) * self.batch_size]
        batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size]
        return np.array([
            resize(imread(filename), (200, 200))
               for filename in batch_x]), np.array(batch_y)

sequence = CIFAR10Sequence(filenames, labels, batch_size)
model.fit(sequence, epochs=10)
 

使用样本加权和类别加权

在默认设置下,样品的重量由其在数据集中的频率决定。有两种方法可以对数据进行加权,而与采样频率无关:

  • 班级重量
  • 样品重量

班级重量

这是通过将字典传递给Model.fit()class_weight参数来设置的。该词典将类别索引映射到应用于属于该类别的样本的权重。

这可用于平衡类而不重采样,或用于训练对特定类更为重视的模型。

例如,如果类“ 0”是数据中类“ 1”的一半,则可以使用Model.fit(..., class_weight={0: 1., 1: 0.5})

这是一个NumPy示例,其中我们使用类别权重或样本权重来更加重视类别#5(在MNIST数据集中的数字“ 5”)的正确分类。

 import numpy as np

class_weight = {
    0: 1.0,
    1: 1.0,
    2: 1.0,
    3: 1.0,
    4: 1.0,
    # Set weight "2" for class "5",
    # making this class 2x more important
    5: 2.0,
    6: 1.0,
    7: 1.0,
    8: 1.0,
    9: 1.0,
}

print("Fit with class weight")
model = get_compiled_model()
model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epochs=1)
 
Fit with class weight
782/782 [==============================] - 1s 2ms/step - loss: 0.3811 - sparse_categorical_accuracy: 0.8999

<tensorflow.python.keras.callbacks.History at 0x7fd964557f60>

样品重量

为了进行细粒度控制,或者如果您不构建分类器,则可以使用“样本权重”。

  • 从NumPy数据进行训练时:将sample_weight参数传递给Model.fit()
  • tf.data或其他任何迭代器进行训练时:产量(input_batch, label_batch, sample_weight_batch)元组。

“样品重量”阵列是一个数字阵列,用于指定批次中每个样品在计算总损失时应具有的重量。它通常用于不平衡的分类问题(这种想法是将更多的权重分配给鲜为人知的类)。

当使用的权重为1和0时,该数组可用作损失函数的掩码 (完全丢弃某些样本对总损失的贡献)。

 sample_weight = np.ones(shape=(len(y_train),))
sample_weight[y_train == 5] = 2.0

print("Fit with sample weight")
model = get_compiled_model()
model.fit(x_train, y_train, sample_weight=sample_weight, batch_size=64, epochs=1)
 
Fit with sample weight
782/782 [==============================] - 1s 2ms/step - loss: 0.3763 - sparse_categorical_accuracy: 0.9000

<tensorflow.python.keras.callbacks.History at 0x7fd96440d0b8>

这是一个匹配的Dataset示例:

 sample_weight = np.ones(shape=(len(y_train),))
sample_weight[y_train == 5] = 2.0

# Create a Dataset that includes sample weights
# (3rd element in the return tuple).
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train, sample_weight))

# Shuffle and slice the dataset.
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

model = get_compiled_model()
model.fit(train_dataset, epochs=1)
 
782/782 [==============================] - 2s 2ms/step - loss: 0.3776 - sparse_categorical_accuracy: 0.9005

<tensorflow.python.keras.callbacks.History at 0x7fd96431d4e0>

将数据传递到多输入,多输出模型

在前面的示例中,我们考虑的模型具有单个输入(形状的张量(764,) )和单个输出(形状的预测张量(10,) )。但是具有多个输入或输出的模型呢?

考虑以下模型,该模型具有形状为(32, 32, 3)的图像输入(即(height, width, channels) )和形状为(None, 10)的时间序列输入(即(timesteps, features) )。我们的模型将具有根据这些输入的组合计算出的两个输出:“得分”(形状(1,) )和五个类别(形状(5,) )的概率分布。

 image_input = keras.Input(shape=(32, 32, 3), name="img_input")
timeseries_input = keras.Input(shape=(None, 10), name="ts_input")

x1 = layers.Conv2D(3, 3)(image_input)
x1 = layers.GlobalMaxPooling2D()(x1)

x2 = layers.Conv1D(3, 3)(timeseries_input)
x2 = layers.GlobalMaxPooling1D()(x2)

x = layers.concatenate([x1, x2])

score_output = layers.Dense(1, name="score_output")(x)
class_output = layers.Dense(5, name="class_output")(x)

model = keras.Model(
    inputs=[image_input, timeseries_input], outputs=[score_output, class_output]
)
 

让我们绘制这个模型,以便您可以清楚地看到我们在这里做什么(请注意,图中显示的形状是批处理形状,而不是按样本的形状)。

 keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)
 

png

在编译时,通过将损失函数作为列表传递,我们可以为不同的输出指定不同的损失:

 model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],
)
 

如果我们仅将单个损失函数传递给模型,则将相同的损失函数应用于每个输出(此处不合适)。

同样对于指标:

 model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],
    metrics=[
        [
            keras.metrics.MeanAbsolutePercentageError(),
            keras.metrics.MeanAbsoluteError(),
        ],
        [keras.metrics.CategoricalAccuracy()],
    ],
)
 

由于我们将名称命名为输出层,因此我们还可以通过dict指定每个输出的损失和指标:

 model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={
        "score_output": keras.losses.MeanSquaredError(),
        "class_output": keras.losses.CategoricalCrossentropy(),
    },
    metrics={
        "score_output": [
            keras.metrics.MeanAbsolutePercentageError(),
            keras.metrics.MeanAbsoluteError(),
        ],
        "class_output": [keras.metrics.CategoricalAccuracy()],
    },
)
 

如果您有两个以上的输出,我们建议使用显式名称和字典。

可以使用loss_weights参数为不同的特定于输出的损失赋予不同的权重(例如,在我们的示例中,我们可能希望通过赋予类损失的重要性2 loss_weights来赋予“得分”损失以特权):

 model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={
        "score_output": keras.losses.MeanSquaredError(),
        "class_output": keras.losses.CategoricalCrossentropy(),
    },
    metrics={
        "score_output": [
            keras.metrics.MeanAbsolutePercentageError(),
            keras.metrics.MeanAbsoluteError(),
        ],
        "class_output": [keras.metrics.CategoricalAccuracy()],
    },
    loss_weights={"score_output": 2.0, "class_output": 1.0},
)
 

您也可以选择不为某些输出计算损失,如果这些输出仅用于预测而不是训练:

 # List loss version
model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[None, keras.losses.CategoricalCrossentropy()],
)

# Or dict loss version
model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={"class_output": keras.losses.CategoricalCrossentropy()},
)
 

将数据合适地传递到多输入或多输出模型的工作方式与在编译中指定损失函数的方式类似:您可以传递NumPy数组的列表 (以1:1映射到接收损失函数的输出)或dict将输出名称映射到NumPy数组

 model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],
)

# Generate dummy NumPy data
img_data = np.random.random_sample(size=(100, 32, 32, 3))
ts_data = np.random.random_sample(size=(100, 20, 10))
score_targets = np.random.random_sample(size=(100, 1))
class_targets = np.random.random_sample(size=(100, 5))

# Fit on lists
model.fit([img_data, ts_data], [score_targets, class_targets], batch_size=32, epochs=1)

# Alternatively, fit on dicts
model.fit(
    {"img_input": img_data, "ts_input": ts_data},
    {"score_output": score_targets, "class_output": class_targets},
    batch_size=32,
    epochs=1,
)
 
4/4 [==============================] - 0s 6ms/step - loss: 11.1192 - score_output_loss: 0.5547 - class_output_loss: 10.5645
4/4 [==============================] - 0s 4ms/step - loss: 9.9594 - score_output_loss: 0.4204 - class_output_loss: 9.5390

<tensorflow.python.keras.callbacks.History at 0x7fd9b41e2390>

这是Dataset用例:与我们对NumPy数组所做的类似, Dataset应返回一个字典元组。

 train_dataset = tf.data.Dataset.from_tensor_slices(
    (
        {"img_input": img_data, "ts_input": ts_data},
        {"score_output": score_targets, "class_output": class_targets},
    )
)
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

model.fit(train_dataset, epochs=1)
 
2/2 [==============================] - 0s 8ms/step - loss: 9.7058 - score_output_loss: 0.3508 - class_output_loss: 9.3550

<tensorflow.python.keras.callbacks.History at 0x7fd964143e48>

使用回调

Keras中的回调是在训练期间(在某个时期开始时,在批处理结束时,在某个时期结束时等)在不同点调用的对象,可用于实现以下行为:

  • 在训练过程中的不同时间点进行验证(除了内置的按时间段验证)
  • 定期或在超过特定精度阈值时对模型进行检查
  • 当训练似乎停滞不前时,更改模型的学习率
  • 当训练似乎停滞不前时,对顶层进行微调
  • 在培训结束或超出某个性能阈值时发送电子邮件或即时消息通知
  • 等等。

回调可以作为列表传递给您对fit()调用:

 model = get_compiled_model()

callbacks = [
    keras.callbacks.EarlyStopping(
        # Stop training when `val_loss` is no longer improving
        monitor="val_loss",
        # "no longer improving" being defined as "no better than 1e-2 less"
        min_delta=1e-2,
        # "no longer improving" being further defined as "for at least 2 epochs"
        patience=2,
        verbose=1,
    )
]
model.fit(
    x_train,
    y_train,
    epochs=20,
    batch_size=64,
    callbacks=callbacks,
    validation_split=0.2,
)
 
Epoch 1/20
625/625 [==============================] - 1s 2ms/step - loss: 0.3744 - sparse_categorical_accuracy: 0.8949 - val_loss: 0.2301 - val_sparse_categorical_accuracy: 0.9336
Epoch 2/20
625/625 [==============================] - 1s 2ms/step - loss: 0.1762 - sparse_categorical_accuracy: 0.9485 - val_loss: 0.1818 - val_sparse_categorical_accuracy: 0.9463
Epoch 3/20
625/625 [==============================] - 1s 2ms/step - loss: 0.1292 - sparse_categorical_accuracy: 0.9616 - val_loss: 0.1620 - val_sparse_categorical_accuracy: 0.9524
Epoch 4/20
625/625 [==============================] - 1s 2ms/step - loss: 0.1014 - sparse_categorical_accuracy: 0.9699 - val_loss: 0.1486 - val_sparse_categorical_accuracy: 0.9566
Epoch 5/20
625/625 [==============================] - 1s 2ms/step - loss: 0.0847 - sparse_categorical_accuracy: 0.9751 - val_loss: 0.1612 - val_sparse_categorical_accuracy: 0.9533
Epoch 6/20
625/625 [==============================] - 1s 2ms/step - loss: 0.0699 - sparse_categorical_accuracy: 0.9788 - val_loss: 0.1447 - val_sparse_categorical_accuracy: 0.9600
Epoch 00006: early stopping

<tensorflow.python.keras.callbacks.History at 0x7fd95479b5c0>

许多内置的回调可用

  • ModelCheckpoint :定期保存模型。
  • EarlyStopping :当培训不再改善验证指标时,停止培训。
  • TensorBoard :定期编写可以在TensorBoard中可视化的模型日志(更多细节请参见“可视化”部分)。
  • CSVLogger :将损失和指标数据流式传输到CSV文件。
  • 等等

请参阅回调文档以获取完整列表。

编写自己的回调

您可以通过扩展基类keras.callbacks.Callback来创建自定义回调。回调可以通过类属性self.model访问其关联的模型。

确保阅读完整的编写自定义回调的指南

这是一个简单的示例,在训练过程中保存了每批次的损失值列表:

 class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs):
        self.per_batch_losses = []

    def on_batch_end(self, batch, logs):
        self.per_batch_losses.append(logs.get("loss"))

 

检查点模型

在相对较大的数据集上训练模型时,至关重要的是要定期保存模型的检查点。

最简单的方法是使用ModelCheckpoint回调:

 model = get_compiled_model()

callbacks = [
    keras.callbacks.ModelCheckpoint(
        # Path where to save the model
        # The two parameters below mean that we will overwrite
        # the current checkpoint if and only if
        # the `val_loss` score has improved.
        # The saved model name will include the current epoch.
        filepath="mymodel_{epoch}",
        save_best_only=True,  # Only save a model if `val_loss` has improved.
        monitor="val_loss",
        verbose=1,
    )
]
model.fit(
    x_train, y_train, epochs=2, batch_size=64, callbacks=callbacks, validation_split=0.2
)
 
Epoch 1/2
617/625 [============================>.] - ETA: 0s - loss: 0.3784 - sparse_categorical_accuracy: 0.8940
Epoch 00001: val_loss improved from inf to 0.22038, saving model to mymodel_1
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
INFO:tensorflow:Assets written to: mymodel_1/assets
625/625 [==============================] - 2s 3ms/step - loss: 0.3766 - sparse_categorical_accuracy: 0.8946 - val_loss: 0.2204 - val_sparse_categorical_accuracy: 0.9363
Epoch 2/2
595/625 [===========================>..] - ETA: 0s - loss: 0.1721 - sparse_categorical_accuracy: 0.9498
Epoch 00002: val_loss improved from 0.22038 to 0.18707, saving model to mymodel_2
INFO:tensorflow:Assets written to: mymodel_2/assets
625/625 [==============================] - 2s 3ms/step - loss: 0.1708 - sparse_categorical_accuracy: 0.9501 - val_loss: 0.1871 - val_sparse_categorical_accuracy: 0.9453

<tensorflow.python.keras.callbacks.History at 0x7fd9545c4a90>

ModelCheckpoint回调可用于实现容错功能:在随机中断训练的情况下,能够从模型的最后保存状态重新开始训练的功能。这是一个基本示例:

 import os

# Prepare a directory to store all the checkpoints.
checkpoint_dir = "./ckpt"
if not os.path.exists(checkpoint_dir):
    os.makedirs(checkpoint_dir)


def make_or_restore_model():
    # Either restore the latest model, or create a fresh one
    # if there is no checkpoint available.
    checkpoints = [checkpoint_dir + "/" + name for name in os.listdir(checkpoint_dir)]
    if checkpoints:
        latest_checkpoint = max(checkpoints, key=os.path.getctime)
        print("Restoring from", latest_checkpoint)
        return keras.models.load_model(latest_checkpoint)
    print("Creating a new model")
    return get_compiled_model()


model = make_or_restore_model()
callbacks = [
    # This callback saves a SavedModel every 100 batches.
    # We include the training loss in the saved model name.
    keras.callbacks.ModelCheckpoint(
        filepath=checkpoint_dir + "/ckpt-loss={loss:.2f}", save_freq=100
    )
]
model.fit(x_train, y_train, epochs=1, callbacks=callbacks)
 
Creating a new model
  83/1563 [>.............................] - ETA: 2s - loss: 1.0490 - sparse_categorical_accuracy: 0.7165INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.97/assets
 185/1563 [==>...........................] - ETA: 5s - loss: 0.7407 - sparse_categorical_accuracy: 0.7959INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.72/assets
 286/1563 [====>.........................] - ETA: 5s - loss: 0.6176 - sparse_categorical_accuracy: 0.8253INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.60/assets
 385/1563 [======>.......................] - ETA: 5s - loss: 0.5432 - sparse_categorical_accuracy: 0.8475INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.53/assets
 485/1563 [========>.....................] - ETA: 5s - loss: 0.4909 - sparse_categorical_accuracy: 0.8607INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.48/assets
 585/1563 [==========>...................] - ETA: 4s - loss: 0.4531 - sparse_categorical_accuracy: 0.8713INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.45/assets
 684/1563 [============>.................] - ETA: 4s - loss: 0.4262 - sparse_categorical_accuracy: 0.8788INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.42/assets
 784/1563 [==============>...............] - ETA: 3s - loss: 0.4066 - sparse_categorical_accuracy: 0.8845INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.40/assets
 882/1563 [===============>..............] - ETA: 3s - loss: 0.3863 - sparse_categorical_accuracy: 0.8902INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.38/assets
 985/1563 [=================>............] - ETA: 2s - loss: 0.3682 - sparse_categorical_accuracy: 0.8954INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.37/assets
1084/1563 [===================>..........] - ETA: 2s - loss: 0.3532 - sparse_categorical_accuracy: 0.8993INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.35/assets
1185/1563 [=====================>........] - ETA: 1s - loss: 0.3407 - sparse_categorical_accuracy: 0.9028INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.34/assets
1284/1563 [=======================>......] - ETA: 1s - loss: 0.3287 - sparse_categorical_accuracy: 0.9063INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.33/assets
1385/1563 [=========================>....] - ETA: 0s - loss: 0.3171 - sparse_categorical_accuracy: 0.9095INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.32/assets
1485/1563 [===========================>..] - ETA: 0s - loss: 0.3090 - sparse_categorical_accuracy: 0.9116INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.31/assets
1563/1563 [==============================] - 8s 5ms/step - loss: 0.3018 - sparse_categorical_accuracy: 0.9136

<tensorflow.python.keras.callbacks.History at 0x7fd954335be0>

您还调用了自己编写的回调来保存和恢复模型。

有关序列化和保存的完整指南,请参见《 模型的保存和序列化指南》

使用学习率时间表

训练深度学习模型的常见模式是随着训练的进行逐渐减少学习。这通常称为“学习率衰减”。

学习衰减进度表可以是静态的(根据当前纪元或当前批次索引预先确定),也可以是动态的(响应于模型的当前行为,尤其是验证损失)。

将时间表传递给优化器

通过将计划对象作为优化器中的learning_rate参数传递,您可以轻松使用静态学习率衰减计划:

 initial_learning_rate = 0.1
lr_schedule = keras.optimizers.schedules.ExponentialDecay(
    initial_learning_rate, decay_steps=100000, decay_rate=0.96, staircase=True
)

optimizer = keras.optimizers.RMSprop(learning_rate=lr_schedule)
 

有几个内置的调度程序可供使用: ExponentialDecayPiecewiseConstantDecayPolynomialDecayInverseTimeDecay

使用回调实现动态学习率计划

由于优化程序无法访问验证指标,因此无法使用这些计划对象实现动态学习率计划(例如,当验证损失不再改善时降低学习率)。

但是,回调确实可以访问所有指标,包括验证指标!因此,您可以通过使用回调来修改优化程序上的当前学习率,从而实现此模式。实际上,它甚至是作为ReduceLROnPlateau回调内置的。

可视化训练期间的损失和指标

在训练过程中关注模型的最好方法是使用TensorBoard ,这是一个基于浏览器的应用程序,可以在本地运行,为您提供:

  • 实时损失图以及用于评估和评估的指标
  • (可选)可视化图层激活的直方图
  • (可选)您的Embedding层学习的嵌入空间的3D可视化

如果您已经使用pip安装了TensorFlow,则应该能够从命令行启动TensorBoard:

 tensorboard --logdir=/full_path_to_your_logs
 

使用TensorBoard回调

将TensorBoard与Keras模型一起使用和fit方法的最简单方法是TensorBoard回调。

在最简单的情况下,只需指定您希望回调写日志的位置,就可以了:

 keras.callbacks.TensorBoard(
    log_dir="/full_path_to_your_logs",
    histogram_freq=0,  # How often to log histogram visualizations
    embeddings_freq=0,  # How often to log embedding visualizations
    update_freq="epoch",
)  # How often to write logs (default: once per epoch)
 
<tensorflow.python.keras.callbacks.TensorBoard at 0x7fda1f3062e8>

有关更多信息,请参见TensorBoard回调文档