Как суммировать резюме по нескольким партиям?
Предполагая, что у меня есть свод сводок, определенных как:
loss = ...
tf.scalar_summary("loss", loss)
# ...
summaries = tf.merge_all_summaries()
Я могу оценить тензор summaries
каждые несколько шагов на обучающих данных и передать результат на SummaryWriter
.
Результатом будет шумное резюме, потому что они вычисляются только по одной партии.
Тем не менее, я хотел бы вычислить резюме для всего набора данных валидации.
Конечно, я не могу передать набор данных валидации как одну партию, потому что она будет слишком большой.
Итак, я получаю итоговые выходы для каждой партии проверки.
Есть ли способ усреднить эти сводки так, чтобы они выглядели так, как если бы сумма вычислялась по всему набору проверки?
Ответы
Ответ 1
Сделайте усреднение вашей меры в Python и создайте новый объект Summary для каждого значения. Вот что я делаю:
accuracies = []
# Calculate your measure over as many batches as you need
for batch in validation_set:
accuracies.append(sess.run([training_op]))
# Take the mean of you measure
accuracy = np.mean(accuracies)
# Create a new Summary object with your measure
summary = tf.Summary()
summary.value.add(tag="%sAccuracy" % prefix, simple_value=accuracy)
# Add it to the Tensorboard summary writer
# Make sure to specify a step parameter to get nice graphs over time
summary_writer.add_summary(summary, global_step)
Ответ 2
Я бы не стал рассчитывать среднее за пределами графика.
Вы можете использовать tf.train.ExponentialMovingAverage:
ema = tf.train.ExponentialMovingAverage(decay=my_decay_value, zero_debias=True)
maintain_ema_op = ema.apply(your_losses_list)
# Create an op that will update the moving averages after each training step.
with tf.control_dependencies([your_original_train_op]):
train_op = tf.group(maintain_ema_op)
Затем используйте:
sess.run(train_op)
Это вызовет maintain_ema_op
потому что он определен как управляющая зависимость.
Чтобы получить свои экспоненциальные скользящие средние, используйте:
moving_average = ema.average(an_item_from_your_losses_list_above)
И получить его значение, используя:
value = sess.run(moving_average)
Это вычисляет скользящее среднее в вашем графике расчета.
Ответ 3
Я думаю, что всегда лучше позволить тензорному потоку делать вычисления.
Посмотрите на потоковые метрики. У них есть функция обновления для подачи информации о вашей текущей партии и функция для получения усредненной сводки. Это будет выглядеть примерно так:
accuracy = ...
streaming_accuracy, streaming_accuracy_update = tf.contrib.metrics.streaming_mean(accuracy)
streaming_accuracy_scalar = tf.summary.scalar('streaming_accuracy', streaming_accuracy)
# set up your session etc.
for i in iterations:
for b in batches:
sess.run([streaming_accuracy_update], feed_dict={...})
streaming_summ = sess.run(streaming_accuracy_scalar)
writer.add_summary(streaming_summary, i)
Также см. Документацию по тензорному потоку: https://www.tensorflow.org/versions/master/api_guides/python/contrib.metrics.
и этот вопрос: как накапливать сводную статистику в тензорном потоке
Ответ 4
Вы можете усреднить сохранение текущей суммы и пересчитать среднее значение после каждой партии, например:
loss_sum = tf.Variable(0.)
inc_op = tf.assign_add(loss_sum, loss)
clear_op = tf.assign(loss_sum, 0.)
average = loss_sum / batches
tf.scalar_summary("average_loss", average)
sess.run(clear_op)
for i in range(batches):
sess.run([loss, inc_op])
sess.run(average)
Ответ 5
Для дальнейшего использования API метрик TensorFlow теперь поддерживает это по умолчанию. Например, взгляните на tf.mean_squared_error
:
Для оценки метрики по потоку данных функция создает операцию update_op
которая обновляет эти переменные и возвращает mean_squared_error
. Внутри операция squared_error
вычисляет поэлементный квадрат разницы между predictions
и labels
. Затем update_op
увеличивает total
с уменьшенной суммой произведений weights
и squared_error
и увеличивает count
с уменьшенной суммой weights
.
Эти переменные total
и count
добавляются к набору метрических переменных, поэтому на практике вы должны сделать что-то вроде:
x_batch = tf.placeholder(...)
y_batch = tf.placeholder(...)
model_output = ...
mse, mse_update = tf.metrics.mean_squared_error(y_batch, model_output)
# This operation resets the metric internal variables to zero
metrics_init = tf.variables_initializer(
tf.get_default_graph().get_collection(tf.GraphKeys.METRIC_VARIABLES))
with tf.Session() as sess:
# Train...
# On evaluation step
sess.run(metrics_init)
for x_eval_batch, y_eval_batch in ...:
mse = sess.run(mse_update, feed_dict={x_batch: x_eval_batch, y_batch: y_eval_batch})
print('Evaluation MSE:', mse)
Ответ 6
Я нашел одно решение сам. Я думаю, что это немного странно, и я надеюсь, что есть более элегантное решение.
Во время настройки:
valid_loss_placeholder = tf.placeholder(dtype=tf.float32, shape=[])
valid_loss_summary = tf.scalar_summary("valid loss", valid_loss_placeholder)
Или для версий тензорного потока после 0.12 (изменение имени для tf.scalar_summary):
valid_loss_placeholder = tf.placeholder(dtype=tf.float32, shape=[])
valid_loss_summary = tf.summary.scalar("valid loss", valid_loss_placeholder)
В рамках цикла обучения:
# Compute valid loss in python by doing sess.run() for each batch
# and averaging
valid_loss = ...
summary = sess.run(valid_loss_summary, {valid_loss_placeholder: valid_loss})
summary_writer.add_summary(summary, step)
Ответ 7
По состоянию на август 2018 года показатели потоковой передачи были исключены. Тем не менее, нетрудно понять, что все показатели являются потоковыми. Итак, используйте tf.metrics.accuracy
.
Однако, если вы хотите, чтобы точность (или другая метрика) превышала только подмножество пакетов, вы можете использовать экспоненциальную скользящую среднюю, как в ответе @MZHm, или сбросить любой из tf.metric
, следуя этому очень информативному сообщению в блоге.
Ответ 8
В течение некоторого времени я сохраняю резюме только один раз за эпоху. Я никогда не знал, что сводка TensorFlows сохранит сводку только для последнего запуска.
В шоке я заглянул в эту проблему. Вот решение, которое я придумал (используя API набора данных):
loss = ...
train_op = ...
loss_metric, loss_metric_update = tf.metrics.mean(ae_loss)
tf.summary.scalar('loss', loss_metric)
merged = tf.summary.merge_all()
train_writer = tf.summary.FileWriter(os.path.join(res_dir, 'train'))
test_writer = tf.summary.FileWriter(os.path.join(res_dir, 'test'))
init_local = tf.initializers.local_variables()
init_global = tf.initializers.global_variables()
sess.run(init_global)
def train_run(epoch):
sess.run([dataset.train_init_op, init_local]) # test_init_op is the operation that switches to test data
for i in range(dataset.num_train_batches): # num_test_batches is the number of batches that should be run for the test set
sess.run([train_op, loss_metric_update])
summary, cur_loss = sess.run([merged, loss_metric])
train_writer.add_summary(summary, epoch)
return cur_loss
def test_run(epoch):
sess.run([dataset.test_init_op, init_local]) # test_init_op is the operation that switches to test data
for i in range(dataset.num_test_batches): # num_test_batches is the number of batches that should be run for the test set
sess.run(loss_metric_update)
summary, cur_loss = sess.run([merged, loss_metric])
test_writer.add_summary(summary, epoch)
return cur_loss
for epoch in range(epochs):
train_loss = train_run(epoch+1)
test_loss = test_run(epoch+1)
print("Epoch: {0:3}, loss: (train: {1:10.10f}, test: {2:10.10f})".format(epoch+1, train_loss, test_loss))
Для tf.metrics.mean()
я просто tf.metrics.mean()
интересующий меня тензор в tf.metrics.mean()
. Для каждого пакетного запуска я вызываю операцию обновления метрик. В конце каждой эпохи тензор метрик будет возвращать правильное среднее значение всех результатов партии.
Не забывайте инициализировать локальные переменные каждый раз, когда вы переключаетесь между тренировочными и тестовыми данными. В противном случае ваши показатели поезда и теста будут практически идентичны.
Ответ 9
У меня возникла та же проблема, когда я понял, что мне нужно перебирать свои данные проверки, когда пространство памяти ограничено и переполнены ошибки OOM.
Как говорится в нескольких ответах, в tf.metrics
это встроено, но я не использую tf.metrics
в своем проекте. Вдохновленный этим, я сделал это:
import tensorflow as tf
import numpy as np
def batch_persistent_mean(tensor):
# Make a variable that keeps track of the sum
accumulator = tf.Variable(initial_value=tf.zeros_like(tensor), dtype=tf.float32)
# Keep count of batches in accumulator (needed to estimate mean)
batch_nums = tf.Variable(initial_value=tf.zeros_like(tensor), dtype=tf.float32)
# Make an operation for accumulating, increasing batch count
accumulate_op = tf.assign_add(accumulator, tensor)
step_batch = tf.assign_add(batch_nums, 1)
update_op = tf.group([step_batch, accumulate_op])
eps = 1e-5
output_tensor = accumulator / (tf.nn.relu(batch_nums - eps) + eps)
# In regards to the tf.nn.relu, it a hacky zero_guard:
# if batch_nums are zero then return eps, else it'll be batch_nums
# Make an operation to reset
flush_op = tf.group([tf.assign(accumulator, 0), tf.assign(batch_nums, 0)])
return output_tensor, update_op, flush_op
# Make a variable that we want to accumulate
X = tf.Variable(0., dtype=tf.float32)
# Make our persistant mean operations
Xbar, upd, flush = batch_persistent_mean(X)
Теперь вы отправляете Xbar
в свое резюме, например, tf.scalar_summary("mean_of_x", Xbar)
, и там, где вы должны выполнить sess.run(X)
ранее, вы будете выполнять sess.run(upd)
. И между эпохами вы бы делали sess.run(flush)
.
Тестирование поведения:
sess = tf.InteractiveSession()
with tf.Session() as sess:
sess.run([tf.global_variables_initializer(), tf.local_variables_initializer()])
# Calculate the mean of 1+2+...+20
for i in range(20):
sess.run(upd, {X: i})
print(sess.run(Xbar), "=", np.mean(np.arange(20)))
for i in range(40):
sess.run(upd, {X: i})
# Now Xbar is the mean of (1+2+...+20+1+2+...+40):
print(sess.run(Xbar), "=", np.mean(np.concatenate([np.arange(20), np.arange(40)])))
# Now flush it
sess.run(flush)
print("flushed. Xbar=", sess.run(Xbar))
for i in range(40):
sess.run(upd, {X: i})
print(sess.run(Xbar), "=", np.mean(np.arange(40)))