Ответ 1
SummaryWriter
я делал, - это использовать два SummaryWriter
с разными SummaryWriter
журналов для набора обучения и набора для перекрестной проверки соответственно. И вы увидите что-то вроде этого:
Есть ли способ построить как потери на тренировки, так и потери валидации на одном и том же графике?
Легко иметь два отдельных скалярных сводки для каждого из них по отдельности, но это ставит их на отдельные графики. Если обе они отображаются на одном и том же графике, гораздо легче увидеть промежуток между ними и независимо от того, начинают ли они расходиться из-за переобучения.
Есть ли встроенный способ сделать это? Если нет, работа вокруг? Большое вам спасибо!
SummaryWriter
я делал, - это использовать два SummaryWriter
с разными SummaryWriter
журналов для набора обучения и набора для перекрестной проверки соответственно. И вы увидите что-то вроде этого:
Вместо того, чтобы отображать две строки отдельно, вы можете вместо этого разделить разницу между валидацией и потерей обучения как свое собственное скалярное резюме для отслеживания расхождения.
Это не дает столько информации об одном графике (по сравнению с добавлением двух сводок), но помогает с возможностью сравнивать несколько прогонов (и не добавлять несколько сводок за прогон).
Для полноты, начиная с тензорной доски 1.5.0, это теперь возможно.
Вы можете использовать пользовательский плагин скаляров. Для этого вам нужно сначала выполнить настройку макета тензорной доски и записать ее в файл событий. Из примера тензорной доски:
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 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 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()
Category
- это группа Chart
s. Каждая Chart
соответствует одному графику, который отображает несколько скаляров вместе. Chart
может MarginChartContent
простые скаляры (MultilineChartContent
) или заполненные области (MarginChartContent
, например, когда вы хотите построить график отклонения некоторого значения). Член tag
MultilineChartContent
должен быть списком регулярных выражений, которые соответствуют tag
скаляров, которые вы хотите сгруппировать в диаграмме. Для получения более подробной информации смотрите предварительные определения объектов в https://github.com/tensorflow/tensorboard/blob/master/tensorboard/plugins/custom_scalar/layout.proto. Обратите внимание, что если у вас есть несколько FileWriter
пишущих в один и тот же каталог, вы должны записать макет только в один из файлов. Запись его в отдельный файл также работает.
Чтобы просмотреть данные в TensorBoard, вам нужно открыть вкладку Custom Scalars. Вот пример изображения того, что ожидать https://user-images.githubusercontent.com/4221553/32865784-840edf52-ca19-11e7-88bc-1806b1243e0d.png
Большое спасибо Нико за подсказку о пользовательских скалярах.
Я был озадачен официальным 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, какую из этих сводок сгруппировать. Указанные сводные данные затем объединяются в один график под заголовком "ТАМОЖЕННЫЕ СКАЛЯРЫ". Мы достигаем этого, написав "Макет" один раз в начале журнала. Как только TensorBoard получает макет, он автоматически создает комбинированную диаграмму в разделе "ТАМОЖЕННЫЕ СКАЛЯРЫ", когда обновляются обычные "СКАЛЯРЫ".
Предполагая, что ваша "исходная модель" уже отправляет ваши переменные (в виде скалярных сводок) в TensorBoard, единственная необходимая модификация - это внедрить макет перед началом основного цикла итерации. Каждая пользовательская скалярная диаграмма выбирает, какие сводки строить с помощью регулярного выражения. Таким образом, для каждой группы переменных, которые должны быть нанесены на график вместе, может быть полезно поместить соответствующие резюме переменных в отдельную область имен. (Таким образом, ваше регулярное выражение может просто выбрать все резюме под этим именем области.)
Важное примечание: операция, которая генерирует сводку переменной, отличается от самой переменной. Например, если у меня есть переменная ns1/my_var
, я могу создать сводку ns2/summary_op_for_myvar
. Пользовательский макет диаграммы скаляров заботится только о сводной операции, а не об имени или области действия исходной переменной.
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)
, чтобы вы могли легко сравнивать сводную статистику в своих экспериментах. Очень полезно для сравнения результатов в большом количестве экспериментов.
Вот пример, создающий два tf.summary.FileWriter
которые совместно используют тот же корневой каталог. Создание tf.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, на который вы можете сослаться.