##### Copyright 2019 The TensorFlow Authors.
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
TensorBoard Scalar:在 Keras 中记录训练指标#
在 TensorFlow.org 上查看 | 在 Google Colab 中运行 | 在 Github 上查看源代码 |
文本特征向量#
机器学习总是涉及理解关键指标,例如损失,以及它们如何随着训练的进行而变化。 例如,这些指标可以帮助您了解模型是否过拟合,或者是否不必要地训练了太长时间。 您可能需要比较不同训练中的这些指标,以帮助调试和改善模型。
TensorBoard 的 Time Series Dashboard 允许您轻松地使用简单的 API 呈现这些指标。本教程提供了非常基本的示例,可帮助您在开发 Keras 模型时学习如何在 TensorBoard 中使用这些 API。您将学习如何使用 Keras TensorBoard 回调和 TensorFlow Summary API 来呈现默认和自定义标量。
安装#
# Load the TensorBoard notebook extension.
%load_ext tensorboard
from datetime import datetime
from packaging import version
import tensorflow as tf
from tensorflow import keras
from keras import backend as K
import numpy as np
print("TensorFlow version: ", tf.__version__)
assert version.parse(tf.__version__).release[0] >= 2, \
"This notebook requires TensorFlow 2.0 or above."
TensorFlow version: 2.8.2
# Clear any logs from previous runs
!rm -rf ./logs/
设置简单回归的数据#
您现在将使用 Keras 计算回归,即找到对应数据集的最佳拟合。 (虽然使用神经网络和梯度下降解决此类问题多此一举,但这却是一个非常容易理解的示例。)
您将使用 TensorBoard 观察训练和测试损失在各个时期之间如何变化。希望您会看到训练集和测试集损失随着时间的流逝而减少,然后保持稳定。
首先,大致沿线 y = 0.5x + 2 生成 1000 个数据点。将这些数据点分割为训练集和测试集。您希望神经网络能够学习这种关系。
data_size = 1000
# 80% of the data is for training.
train_pct = 0.8
train_size = int(data_size * train_pct)
# Create some input data between -1 and 1 and randomize it.
x = np.linspace(-1, 1, data_size)
np.random.shuffle(x)
# Generate the output data.
# y = 0.5x + 2 + noise
y = 0.5 * x + 2 + np.random.normal(0, 0.05, (data_size, ))
# Split into test and train pairs.
x_train, y_train = x[:train_size], y[:train_size]
x_test, y_test = x[train_size:], y[train_size:]
训练模型并记录损失#
您现在可以定义、训练和评估您的模型了。
要在训练时记录损失标量,请执行以下操作:
指定日志目录
将 TensorBoard 回调传递给 Keras 的 Model.fit()。
TensorBoard 从日志目录层次结构中读取日志数据。在此笔记本中,根日志目录为 logs/scalars
,后缀为带时间戳的子目录。带时间戳的子目录支持在使用 TensorBoard 并在模型上迭代时轻松识别和选择训练运行。
logdir = "logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir)
model = keras.models.Sequential([
keras.layers.Dense(16, input_dim=1),
keras.layers.Dense(1),
])
model.compile(
loss='mse', # keras.losses.mean_squared_error
optimizer=keras.optimizers.SGD(learning_rate=0.2),
)
print("Training ... With default parameters, this takes less than 10 seconds.")
training_history = model.fit(
x_train, # input
y_train, # output
batch_size=train_size,
verbose=0, # Suppress chatty output; use Tensorboard instead
epochs=100,
validation_data=(x_test, y_test),
callbacks=[tensorboard_callback],
)
print("Average test loss: ", np.average(training_history.history['loss']))
Training ... With default parameters, this takes less than 10 seconds.
Average test loss: 0.042797307365108284
使用 TensorBoard 检查损失#
现在,启动 TensorBoard,指定上面使用的根日志目录。
等待几秒钟,让 TensorBoard 的 UI 启动。
%tensorboard --logdir logs/scalars
您可能会看到 TensorBoard 显示消息“当前数据集没有活动的仪表板”。这是因为尚未保存初始日志记录数据。随着训练的进行,Keras 模型将开始记录数据。TensorBoard 将定期刷新并显示您的标量指标。如果您不耐烦,可以点击右上角的刷新箭头。
当您观察训练进度时,请注意训练和验证损失如何快速下降,然后保持稳定。事实上,您可以在 25 个周期后停止训练,因为在那之后训练并没有太大改善。
将鼠标悬停到计算图上可查看特定的数据点。您还可以尝试使用鼠标放大,或者选择其中一部分以查看更多详细信息。
请注意左侧的“运行”选择器。“运行”代表一轮训练的一组日志,在这种情况下为 Model.fit() 的结果。随着时间的推移,开发者通常要运行很多次,因为他们需要试验和开发自己的模型。
使用“运行”选择器选择特定运行,或者仅从训练或验证中进行选择。比较运行将帮助您评估哪个版本的代码可以更好地解决您的问题。
好的,TensorBoard 的损失图表明,训练和验证的损失都在持续下降,然后稳定下来。这意味着模型的指标可能非常好!现在看看模型在现实中的实际表现如何。
给定输入数据 (60, 25, 2),行 y = 0.5x + 2 应产生 (32, 14.5, 3)。模型的结果是否一致?
print(model.predict([60, 25, 2]))
# True values to compare predictions against:
# [[32.0]
# [14.5]
# [ 3.0]]
[[32.148884 ]
[14.562463 ]
[ 3.0056725]]
不错!
记录自定义标量#
如果您想记录自定义值(例如动态学习率),该怎么办?为此,您需要使用 TensorFlow Summary API。
重新训练回归模型并记录自定义学习率。具体步骤如下:
使用
tf.summary.create_file_writer()
创建文件编写器。定义一个自定义学习率函数。这将传递给 Keras LearningRateScheduler 回调。
在学习率函数内部,使用
tf.summary.scalar()
记录自定义学习率。将 LearningRateScheduler 回调传递给 Model.fit()。
通常,要记录自定义标量,您需要将 tf.summary.scalar()
与文件写入器一起使用。文件写入器负责将此运行的数据写入指定目录,并在您使用 tf.summary.scalar()
时隐式使用。
logdir = "logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S")
file_writer = tf.summary.create_file_writer(logdir + "/metrics")
file_writer.set_as_default()
def lr_schedule(epoch):
"""
Returns a custom learning rate that decreases as epochs progress.
"""
learning_rate = 0.2
if epoch > 10:
learning_rate = 0.02
if epoch > 20:
learning_rate = 0.01
if epoch > 50:
learning_rate = 0.005
tf.summary.scalar('learning rate', data=learning_rate, step=epoch)
return learning_rate
lr_callback = keras.callbacks.LearningRateScheduler(lr_schedule)
tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir)
model = keras.models.Sequential([
keras.layers.Dense(16, input_dim=1),
keras.layers.Dense(1),
])
model.compile(
loss='mse', # keras.losses.mean_squared_error
optimizer=keras.optimizers.SGD(),
)
training_history = model.fit(
x_train, # input
y_train, # output
batch_size=train_size,
verbose=0, # Suppress chatty output; use Tensorboard instead
epochs=100,
validation_data=(x_test, y_test),
callbacks=[tensorboard_callback, lr_callback],
)
我们再看一下 TensorBoard。
%tensorboard --logdir logs/scalars
使用左侧的“运行”选择器,请注意您有一个 <timestamp>/metrics
运行。选择此运行会显示“学习率”计算图,这样便可验证此运行期间学习率的进展。
您还可以将此运行的训练和验证损失曲线与您之前的运行进行比较。您可能还注意到学习率计划返回离散值,具体取决于时期,但学习率图可能看起来很平滑TensorBoard 有一个平滑参数,您可能需要将其调低至零才能查看未平滑的值。
这个模型怎么样?
print(model.predict([60, 25, 2]))
# True values to compare predictions against:
# [[32.0]
# [14.5]
# [ 3.0]]
[[31.958094 ]
[14.482997 ]
[ 2.9993598]]
批量日志记录#
首先,我们加载 MNIST 数据集,归一化数据并编写一个函数来创建一个用于将图像分成 10 类的简单 Keras 模型。
mnist = tf.keras.datasets.mnist
(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
def create_model():
return tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation='softmax')
])
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
11501568/11490434 [==============================] - 0s 0us/step
瞬时批次级日志记录#
以瞬时方式在批次级别记录指标可以向我们展示在每个周期内进行训练时批次间的波动程度,这对于调试而言非常有用。
将摘要编写器设置到其他日志目录:
log_dir = 'logs/batch_level/' + datetime.now().strftime("%Y%m%d-%H%M%S") + '/train'
train_writer = tf.summary.create_file_writer(log_dir)
要启用批次级日志记录,应通过重写模型类定义中的 train_step()
来定义自定义 tf.summary
指标,并将其包含在摘要编写器上下文中。这可以简单地组合成子类化模型定义,也可以扩展以编辑我们之前的函数式 API 模型,如下所示:
class MyModel(tf.keras.Model):
def __init__(self, model):
super().__init__()
self.model = model
def train_step(self, data):
x, y = data
with tf.GradientTape() as tape:
y_pred = self.model(x, training=True)
loss = self.compiled_loss(y, y_pred)
mse = tf.keras.losses.mean_squared_error(y, K.max(y_pred, axis=-1))
self.optimizer.minimize(loss, self.trainable_variables, tape=tape)
with train_writer.as_default(step=self._train_counter):
tf.summary.scalar('batch_loss', loss)
tf.summary.scalar('batch_mse', mse)
return self.compute_metrics(x, y, y_pred, None)
def call(self, x):
x = self.model(x)
return x
# Adds custom batch-level metrics to our previous Functional API model
model = MyModel(create_model())
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
定义我们的 TensorBoard 回调以将周期级和批次级指标记录到我们的日志目录中,并使用我们选择的 batch_size
调用 model.fit()
:
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir)
model.fit(x=x_train,
y=y_train,
epochs=5,
batch_size=500,
validation_data=(x_test, y_test),
callbacks=[tensorboard_callback])
Epoch 1/5
120/120 [==============================] - 5s 36ms/step - loss: 0.4379 - accuracy: 0.8788 - val_loss: 0.2041 - val_accuracy: 0.9430
Epoch 2/5
120/120 [==============================] - 4s 31ms/step - loss: 0.1875 - accuracy: 0.9471 - val_loss: 0.1462 - val_accuracy: 0.9591
Epoch 3/5
120/120 [==============================] - 3s 27ms/step - loss: 0.1355 - accuracy: 0.9613 - val_loss: 0.1170 - val_accuracy: 0.9670
Epoch 4/5
120/120 [==============================] - 3s 27ms/step - loss: 0.1058 - accuracy: 0.9694 - val_loss: 0.0954 - val_accuracy: 0.9723
Epoch 5/5
120/120 [==============================] - 3s 27ms/step - loss: 0.0872 - accuracy: 0.9752 - val_loss: 0.0843 - val_accuracy: 0.9749
<keras.callbacks.History at 0x7fce165a2fd0>
以新的日志目录打开 TensorBoard 并查看周期级和批次级指标:
%tensorboard --logdir logs/batch_level
累积批次级日志记录#
批次级日志记录也可累积式实现,对每个批次的指标与之前批次的指标求平均值,并在记录批次级指标时生成更加平滑的训练曲线。
将摘要编写器设置到其他日志目录:
log_dir = 'logs/batch_avg/' + datetime.now().strftime("%Y%m%d-%H%M%S") + '/train'
train_writer = tf.summary.create_file_writer(log_dir)
创建可以按批次记录的有状态指标:
batch_loss = tf.keras.metrics.Mean('batch_loss', dtype=tf.float32)
batch_accuracy = tf.keras.metrics.SparseCategoricalAccuracy('batch_accuracy')
同样,请在被重写的 train_step
方法中添加自定义 tf.summary
指标。要使批次级日志记录成为累积式,请使用我们定义的有状态指标基于每个训练步骤的数据计算累积结果。
class MyModel(tf.keras.Model):
def __init__(self, model):
super().__init__()
self.model = model
def train_step(self, data):
x, y = data
with tf.GradientTape() as tape:
y_pred = self.model(x, training=True)
loss = self.compiled_loss(y, y_pred)
self.optimizer.minimize(loss, self.trainable_variables, tape=tape)
batch_loss(loss)
batch_accuracy(y, y_pred)
with train_writer.as_default(step=self._train_counter):
tf.summary.scalar('batch_loss', batch_loss.result())
tf.summary.scalar('batch_accuracy', batch_accuracy.result())
return self.compute_metrics(x, y, y_pred, None)
def call(self, x):
x = self.model(x)
return x
# Adds custom batch-level metrics to our previous Functional API model
model = MyModel(create_model())
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
同样,定义我们的 TensorBoard 回调并使用我们选择的 batch_size
调用 model.fit()
:
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir)
model.fit(x=x_train,
y=y_train,
epochs=5,
batch_size=500,
validation_data=(x_test, y_test),
callbacks=[tensorboard_callback])
Epoch 1/5
120/120 [==============================] - 4s 27ms/step - loss: 0.4266 - accuracy: 0.8813 - val_loss: 0.2055 - val_accuracy: 0.9415
Epoch 2/5
120/120 [==============================] - 3s 26ms/step - loss: 0.1864 - accuracy: 0.9476 - val_loss: 0.1417 - val_accuracy: 0.9613
Epoch 3/5
120/120 [==============================] - 3s 27ms/step - loss: 0.1352 - accuracy: 0.9614 - val_loss: 0.1148 - val_accuracy: 0.9665
Epoch 4/5
120/120 [==============================] - 3s 26ms/step - loss: 0.1066 - accuracy: 0.9702 - val_loss: 0.0932 - val_accuracy: 0.9716
Epoch 5/5
120/120 [==============================] - 3s 27ms/step - loss: 0.0859 - accuracy: 0.9749 - val_loss: 0.0844 - val_accuracy: 0.9754
<keras.callbacks.History at 0x7fce15c39f50>
以新的日志目录打开 TensorBoard 并查看周期级和批次级指标:
%tensorboard --logdir logs/batch_avg
就是这样!您现已知道如何在 TensorBoard 中为各种用例创建自定义训练指标了。