有没有办法在同一张图上同时绘制训练损失和验证损失?
很容易为它们中的每一个单独设置两个单独的标量摘要,但这会将它们放在单独的图表上。如果两者都显示在同一个图表中,则更容易看到它们之间的差距以及它们是否由于过度拟合而开始发散。
有没有内置的方法来做到这一点?如果没有,解决方法?非常感谢!
有没有办法在同一张图上同时绘制训练损失和验证损失?
很容易为它们中的每一个单独设置两个单独的标量摘要,但这会将它们放在单独的图表上。如果两者都显示在同一个图表中,则更容易看到它们之间的差距以及它们是否由于过度拟合而开始发散。
有没有内置的方法来做到这一点?如果没有,解决方法?非常感谢!
您可以将验证损失和训练损失之间的差异绘制为自己的标量摘要来跟踪差异,而不是单独显示两条线。
这并没有在单个图上提供尽可能多的信息(与添加两个摘要相比),但它有助于能够比较多个运行(而不是每次运行添加多个摘要)。
非常感谢 niko 关于自定义标量的提示。
我被官方弄糊涂了,custom_scalar_demo.py
因为事情太多了,我不得不研究了很长时间才弄清楚它是如何工作的。
为了准确显示为现有模型创建自定义标量图需要做什么,我整理了以下完整示例:
# + <
# We need these to make a custom protocol buffer to display custom scalars.
# See https://developers.google.com/protocol-buffers/
from tensorboard.plugins.custom_scalar import layout_pb2
from tensorboard.summary.v1 import custom_scalar_pb
# >
import tensorflow as tf
from time import time
import re
# Initial values
(x0, y0) = (-1, 1)
# This is useful only when re-running code (e.g. Jupyter).
tf.reset_default_graph()
# Set up variables.
x = tf.Variable(x0, name="X", dtype=tf.float64)
y = tf.Variable(y0, name="Y", dtype=tf.float64)
# Define loss function and give it a name.
loss = tf.square(x - 3*y) + tf.square(x+y)
loss = tf.identity(loss, name='my_loss')
# Define the op for performing gradient descent.
minimize_step_op = tf.train.GradientDescentOptimizer(0.092).minimize(loss)
# List quantities to summarize in a dictionary
# with (key, value) = (name, Tensor).
to_summarize = dict(
X = x,
Y_plus_2 = y + 2,
)
# Build scalar summaries corresponding to to_summarize.
# This should be done in a separate name scope to avoid name collisions
# between summaries and their respective tensors. The name scope also
# gives a title to a group of scalars in TensorBoard.
with tf.name_scope('scalar_summaries'):
my_var_summary_op = tf.summary.merge(
[tf.summary.scalar(name, var)
for name, var in to_summarize.items()
]
)
# + <
# This constructs the layout for the custom scalar, and specifies
# which scalars to plot.
layout_summary = custom_scalar_pb(
layout_pb2.Layout(category=[
layout_pb2.Category(
title='Custom scalar summary group',
chart=[
layout_pb2.Chart(
title='Custom scalar summary chart',
multiline=layout_pb2.MultilineChartContent(
# regex to select only summaries which
# are in "scalar_summaries" name scope:
tag=[r'^scalar_summaries\/']
)
)
])
])
)
# >
# Create session.
with tf.Session() as sess:
# Initialize session.
sess.run(tf.global_variables_initializer())
# Create writer.
with tf.summary.FileWriter(f'./logs/session_{int(time())}') as writer:
# Write the session graph.
writer.add_graph(sess.graph) # (not necessary for scalars)
# + <
# Define the layout for creating custom scalars in terms
# of the scalars.
writer.add_summary(layout_summary)
# >
# Main iteration loop.
for i in range(50):
current_summary = sess.run(my_var_summary_op)
writer.add_summary(current_summary, global_step=i)
writer.flush()
sess.run(minimize_step_op)
上面包含一个由三个代码块增强的“原始模型”,由
# + <
[code to add custom scalars goes here]
# >
我的“原始模型”有这些标量:
这张图:
我修改后的模型具有相同的标量和图形,以及以下自定义标量:
这个自定义标量图只是结合了原始两个标量图的布局。
不幸的是,结果图很难阅读,因为两个值具有相同的颜色。(它们仅通过标记来区分。)然而,这与 TensorBoard 的每个日志具有一种颜色的约定一致。
思路如下。您有一组变量要绘制在单个图表中。作为先决条件,TensorBoard 应该在“SCALARS”标题下单独绘制每个变量。(这是通过为每个变量创建一个标量摘要,然后将这些摘要写入日志来完成的。这里没有新内容。)
为了在同一张图表中绘制多个变量,我们告诉 TensorBoard 这些摘要中的哪一个要组合在一起。然后将指定的摘要组合到“CUSTOM SCALARS”标题下的单个图表中。我们通过在日志开头编写一次“布局”来实现这一点。一旦 TensorBoard 收到布局,它会在更新普通的“SCALARS”时自动在“CUSTOM SCALARS”下生成一个组合图表。
假设您的“原始模型”已经将变量(作为标量摘要)发送到 TensorBoard,唯一需要的修改是在主迭代循环开始之前注入布局。每个自定义标量图表通过正则表达式选择要绘制的摘要。因此,对于要一起绘制的每组变量,将变量各自的摘要放在单独的名称范围中会很有用。(这样您的正则表达式可以简单地选择该名称范围内的所有摘要。)
重要提示:生成变量摘要的操作与变量本身不同。例如,如果我有一个变量ns1/my_var
,我可以创建一个摘要ns2/summary_op_for_myvar
。自定义标量图表布局只关心摘要操作,而不关心原始变量的名称或范围。
这是一个示例,创建两个tf.summary.FileWriter
共享同一个根目录的 s。创建一个由两个stf.summary.scalar
共享的。tf.summary.FileWriter
在每个时间步,获取summary
并更新每个tf.summary.FileWriter
.
import os
import tqdm
import tensorflow as tf
def tb_test():
sess = tf.Session()
x = tf.placeholder(dtype=tf.float32)
summary = tf.summary.scalar('Values', x)
merged = tf.summary.merge_all()
sess.run(tf.global_variables_initializer())
writer_1 = tf.summary.FileWriter(os.path.join('tb_summary', 'train'))
writer_2 = tf.summary.FileWriter(os.path.join('tb_summary', 'eval'))
for i in tqdm.tqdm(range(200)):
# train
summary_1 = sess.run(merged, feed_dict={x: i-10})
writer_1.add_summary(summary_1, i)
# eval
summary_2 = sess.run(merged, feed_dict={x: i+10})
writer_2.add_summary(summary_2, i)
writer_1.close()
writer_2.close()
if __name__ == '__main__':
tb_test()
结果如下:
橙色线表示评估阶段的结果,蓝色线表示训练阶段的数据。
另外,TF 团队有一篇非常有用的帖子,您可以参考。
仅针对通过搜索遇到此问题的任何人:当前实现此目标的最佳实践是仅SummaryWriter.add_scalars
使用torch.utils.tensorboard
. 从文档:
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter()
r = 5
for i in range(100):
writer.add_scalars('run_14h', {'xsinx':i*np.sin(i/r),
'xcosx':i*np.cos(i/r),
'tanx': np.tan(i/r)}, i)
writer.close()
# This call adds three values to the same scalar plot with the tag
# 'run_14h' in TensorBoard's scalar section.
预期结果:
为了完整起见,自从 tensorboard 1.5.0 以来,这现在是可能的。
您可以使用自定义标量插件。为此,您需要首先进行 tensorboard 布局配置并将其写入事件文件。从张量板示例:
import tensorflow as tf
from tensorboard import summary
from tensorboard.plugins.custom_scalar import layout_pb2
# The layout has to be specified and written only once, not at every step
layout_summary = summary.custom_scalar_pb(layout_pb2.Layout(
category=[
layout_pb2.Category(
title='losses',
chart=[
layout_pb2.Chart(
title='losses',
multiline=layout_pb2.MultilineChartContent(
tag=[r'loss.*'],
)),
layout_pb2.Chart(
title='baz',
margin=layout_pb2.MarginChartContent(
series=[
layout_pb2.MarginChartContent.Series(
value='loss/baz/scalar_summary',
lower='baz_lower/baz/scalar_summary',
upper='baz_upper/baz/scalar_summary'),
],
)),
]),
layout_pb2.Category(
title='trig functions',
chart=[
layout_pb2.Chart(
title='wave trig functions',
multiline=layout_pb2.MultilineChartContent(
tag=[r'trigFunctions/cosine', r'trigFunctions/sine'],
)),
# The range of tangent is different. Let's give it its own chart.
layout_pb2.Chart(
title='tan',
multiline=layout_pb2.MultilineChartContent(
tag=[r'trigFunctions/tangent'],
)),
],
# This category we care less about. Let's make it initially closed.
closed=True),
]))
writer = tf.summary.FileWriter(".")
writer.add_summary(layout_summary)
# ...
# Add any summary data you want to the file
# ...
writer.close()
ACategory
是Chart
s 组。每个Chart
对应于一个单独的图,该图一起显示几个标量。Chart
可以绘制简单的标量 ( MultilineChartContent
) 或填充区域(MarginChartContent
例如,当您想要绘制某个值的偏差时)。的tag
成员MultilineChartContent
必须是与tag
要在图表中分组的标量的 s 匹配的正则表达式列表。有关更多详细信息,请查看https://github.com/tensorflow/tensorboard/blob/master/tensorboard/plugins/custom_scalar/layout.proto中对象的原型定义。请注意,如果您有多个FileWriter
s 写入同一目录,则只需将布局写入其中一个文件中。将其写入单独的文件也可以。
要查看 TensorBoard 中的数据,您需要打开 Custom Scalars 选项卡。这是预期的示例图像https://user-images.githubusercontent.com/4221553/32865784-840edf52-ca19-11e7-88bc-1806b1243e0d.png
PyTorch 1.5 中的解决方案采用了两位作者的方法:
import os
from torch.utils.tensorboard import SummaryWriter
LOG_DIR = "experiment_dir"
train_writer = SummaryWriter(os.path.join(LOG_DIR, "train"))
val_writer = SummaryWriter(os.path.join(LOG_DIR, "val"))
# while in the training loop
for k, v in train_losses.items()
train_writer.add_scalar(k, v, global_step)
# in the validation loop
for k, v in val_losses.items()
val_writer.add_scalar(k, v, global_step)
# at the end
train_writer.close()
val_writer.close()
dict中的键必须与要在同一图表上分组的train_losses
那些匹配。val_losses
Tensorboard 是一个非常好的工具,但由于它的声明性,它很难让它完全按照你的意愿去做。
我建议您查看 Losswise ( https://losswise.com ) 来绘制和跟踪损失函数,作为 Tensorboard 的替代方案。使用 Losswise,您可以准确指定应该一起绘制的图形:
import losswise
losswise.set_api_key("project api key")
session = losswise.Session(tag='my_special_lstm', max_iter=10)
loss_graph = session.graph('loss', kind='min')
# train an iteration of your model...
loss_graph.append(x, {'train_loss': train_loss, 'validation_loss': validation_loss})
# keep training model...
session.done()
然后你会得到如下所示的东西:
请注意数据是如何通过调用显式馈送到特定图表的loss_graph.append
,然后该数据将显示在项目的仪表板中。
此外,对于上面的示例,Losswise 会自动生成一个包含列的表格,min(training_loss)
因此min(validation_loss)
您可以轻松地比较实验中的汇总统计信息。对于比较大量实验的结果非常有用。
请让我在@Lifu Huang给出的答案中提供一些代码示例。首先loger.py
从这里下载然后:
from logger import Logger
def train_model(parameters...):
N_EPOCHS = 15
# Set the logger
train_logger = Logger('./summaries/train_logs')
test_logger = Logger('./summaries/test_logs')
for epoch in range(N_EPOCHS):
# Code to get train_loss and test_loss
# ============ TensorBoard logging ============#
# Log the scalar values
train_info = {
'loss': train_loss,
}
test_info = {
'loss': test_loss,
}
for tag, value in train_info.items():
train_logger.scalar_summary(tag, value, step=epoch)
for tag, value in test_info.items():
test_logger.scalar_summary(tag, value, step=epoch)
最后你运行tensorboard --logdir=summaries/ --port=6006
,你得到: