TensorBoard - 在同一图表上绘制培训和验证损失?

gol*_*enk 35 machine-learning tensorflow tensorboard

有没有办法在同一图表上绘制训练损失和验证损失?

很容易为每个单独的标记提供两个单独的标量摘要,但这会将它们放在单独的图形上.如果两者都显示在同一图表中,则更容易看出它们之间的差距以及它们是否因过度拟合而开始出现分歧.

有没有内置的方法来做到这一点?如果没有,一个解决方法?非常感谢!

Lif*_*ang 23

我一直在做的解决方法是分别使用两个SummaryWriter具有不同日志目录的训练集和交叉验证集.你会看到这样的事情:

在此输入图像描述

  • 如何在SAME图上获得2次运行.当我尝试使用不同的作者创建2个摘要但同名时,我得到了"Loss"和"Loss_1" (4认同)
  • 您能否提供示例代码来回答您的问题?它将帮助那些在工作中遇到问题的人。 (3认同)
  • 那么我应该在哪个目录运行`tensorboard`,即`--logdir`参数是什么? (2认同)
  • 黄宇恒,把它们放在同一个父目录下。然后在--logdir中指定父目录。我通常创建一个目录 ./summaries/ 并将每个子目录放在那里。 (2认同)

小智 20

对于通过搜索遇到此问题的任何人:当前实现此目标的最佳实践是仅使用 中的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.
Run Code Online (Sandbox Code Playgroud)

预期结果:

预期结果图像


gol*_*enk 10

您可以将验证和训练损失之间的差异绘制为自己的标量汇总来跟踪分歧,而不是分别显示两条线.

这不会在单个图上提供尽可能多的信息(与添加两个摘要相比),但它有助于比较多个运行(并且不会在每次运行时添加多个摘要).


Ben*_*res 7

非常感谢 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)   
Run Code Online (Sandbox Code Playgroud)

以上由一个“原始模型”组成,由三个代码块表示

# + <
        [code to add custom scalars goes here]
#   >
Run Code Online (Sandbox Code Playgroud)

我的“原始模型”有这些标量:

在此处输入图片说明

这张图:

在此处输入图片说明

我修改后的模型具有相同的标量和图形,以及以下自定义标量:

在此处输入图片说明

这个自定义标量图只是一个结合了原始两个标量图的布局。

不幸的是,生成的图形很难阅读,因为两个值具有相同的颜色。(它们仅通过标记来区分。)然而,这与 TensorBoard 的每个日志具有一种颜色的惯例一致。

解释

思路如下。您有一些要在单个图表中绘制的变量。作为先决条件,TensorBoard 应该在“SCALARS”标题下单独绘制每个变量。(这是通过为每个变量创建一个标量摘要,然后将这些摘要写入日志来实现的。这里没有什么新内容。)

为了在同一个图表中绘制多个变量,我们告诉 TensorBoard 将这些摘要中的哪些组合在一起。然后将指定的摘要合并到“自定义标量”标题下的单个图表中。我们通过在日志的开头写一次“布局”来实现这一点。一旦 TensorBoard 收到布局,它会自动在“CUSTOM SCALARS”下生成一个组合图表,因为普通的“SCALARS”被更新。

假设您的“原始模型”已经将您的变量(作为标量摘要)发送到 TensorBoard,唯一需要的修改是在主迭代循环开始之前注入布局。每个自定义标量图表通过正则表达式选择要绘制的摘要。因此,对于要一起绘制的每组变量,将变量各自的摘要放入单独的名称范围会很有用。(这样您的正则表达式可以简单地选择该名称范围内的所有摘要。)

重要说明:生成变量摘要的操作与变量本身不同。例如,如果我有一个变量ns1/my_var,我可以创建一个摘要ns2/summary_op_for_myvar。自定义标量图表布局只关心摘要操作,而不关心原始变量的名称或范围。


Goi*_*Way 5

这是一个示例,创建两个tf.summary.FileWriter共享相同根目录的 s。创建一个tf.summary.scalar由两个tf.summary.FileWriters共享的。在每个时间步,获取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()
Run Code Online (Sandbox Code Playgroud)

结果如下:

在此处输入图片说明

橙色线表示评估阶段的结果,相应地,蓝色线表示训练阶段的数据。

另外,TF 团队有一篇非常有用的帖子,您可以参考。