Тензорный поток: как отображать пользовательские изображения в тензокарте (например, графики Matplotlib)

В разделе Image Dashboard раздела Tendorboard ReadMe говорится:

Поскольку панель управления изображения поддерживает произвольные png, вы можете использовать это для встраивания пользовательских визуализаций (например, матплотблибковых диаграмм) в TensorBoard.

Я вижу, как изображение pyplot можно было записать в файл, прочитать в виде тензора, а затем использовать с tf.image_summary(), чтобы записать его в TensorBoard, но это утверждение из readme предполагает, что существует более прямой способ, Здесь? Если да, есть ли дополнительная документация и/или примеры того, как это сделать эффективно?

Ответ 1

Это очень легко сделать, если у вас есть изображение в буфере памяти. Ниже я показываю пример, в котором пипот сохраняется в буфере, а затем преобразуется в представление изображения TF, которое затем отправляется в сводку изображений.

import io
import matplotlib.pyplot as plt
import tensorflow as tf


def gen_plot():
    """Create a pyplot plot and save to buffer."""
    plt.figure()
    plt.plot([1, 2])
    plt.title("test")
    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    buf.seek(0)
    return buf


# Prepare the plot
plot_buf = gen_plot()

# Convert PNG buffer to TF image
image = tf.image.decode_png(plot_buf.getvalue(), channels=4)

# Add the batch dimension
image = tf.expand_dims(image, 0)

# Add image summary
summary_op = tf.summary.image("plot", image)

# Session
with tf.Session() as sess:
    # Run
    summary = sess.run(summary_op)
    # Write summary
    writer = tf.train.SummaryWriter('./logs')
    writer.add_summary(summary)
    writer.close()

Это дает следующую визуализацию TensorBoard:

введите описание изображения здесь

Ответ 2

Далее script не использует промежуточную кодировку RGB/PNG. Это также устраняет проблему с дополнительной конструкцией операции во время выполнения, повторное использование одного резюме.

Ожидается, что размер фигуры останется неизменным во время выполнения

Решение, которое работает:

import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np

def get_figure():
  fig = plt.figure(num=0, figsize=(6, 4), dpi=300)
  fig.clf()
  return fig


def fig2rgb_array(fig, expand=True):
  fig.canvas.draw()
  buf = fig.canvas.tostring_rgb()
  ncols, nrows = fig.canvas.get_width_height()
  shape = (nrows, ncols, 3) if not expand else (1, nrows, ncols, 3)
  return np.fromstring(buf, dtype=np.uint8).reshape(shape)


def figure_to_summary(fig):
  image = fig2rgb_array(fig)
  summary_writer.add_summary(
    vis_summary.eval(feed_dict={vis_placeholder: image}))


if __name__ == '__main__':
      # construct graph
      x = tf.Variable(initial_value=tf.random_uniform((2, 10)))
      inc = x.assign(x + 1)

      # construct summary
      fig = get_figure()
      vis_placeholder = tf.placeholder(tf.uint8, fig2rgb_array(fig).shape)
      vis_summary = tf.summary.image('custom', vis_placeholder)

      with tf.Session() as sess:
        tf.global_variables_initializer().run()
        summary_writer = tf.summary.FileWriter('./tmp', sess.graph)

        for i in range(100):
          # execute step
          _, values = sess.run([inc, x])
          # draw on the plot
          fig = get_figure()
          plt.subplot('111').scatter(values[0], values[1])
          # save the summary
          figure_to_summary(fig)

Ответ 3

Немного поздно с моим ответом. С tf-matplotlib простой график рассеяния сводится к:

import tensorflow as tf
import numpy as np

import tfmpl

@tfmpl.figure_tensor
def draw_scatter(scaled, colors): 
    '''Draw scatter plots. One for each color.'''  
    figs = tfmpl.create_figures(len(colors), figsize=(4,4))
    for idx, f in enumerate(figs):
        ax = f.add_subplot(111)
        ax.axis('off')
        ax.scatter(scaled[:, 0], scaled[:, 1], c=colors[idx])
        f.tight_layout()

    return figs

with tf.Session(graph=tf.Graph()) as sess:

    # A point cloud that can be scaled by the user
    points = tf.constant(
        np.random.normal(loc=0.0, scale=1.0, size=(100, 2)).astype(np.float32)
    )
    scale = tf.placeholder(tf.float32)        
    scaled = points*scale

    # Note, 'scaled' above is a tensor. Its being passed 'draw_scatter' below. 
    # However, when 'draw_scatter' is invoked, the tensor will be evaluated and a
    # numpy array representing its content is provided.   
    image_tensor = draw_scatter(scaled, ['r', 'g'])
    image_summary = tf.summary.image('scatter', image_tensor)      
    all_summaries = tf.summary.merge_all() 

    writer = tf.summary.FileWriter('log', sess.graph)
    summary = sess.run(all_summaries, feed_dict={scale: 2.})
    writer.add_summary(summary, global_step=0)

При выполнении это приводит к следующему графику внутри Tensorboard

Обратите внимание, что tf-matplotlib заботится об оценке любых тензорных входов, избегает pyplot резьбой на pyplot и поддерживает блиты для критического построения времени выполнения.

Ответ 4

Это намеревается завершить ответ Анджей Pronobis. Следуя внимательно своему хорошему сообщению, я настроил этот минимальный рабочий пример:

    plt.figure()
    plt.plot([1, 2])
    plt.title("test")
    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    buf.seek(0)
    image = tf.image.decode_png(buf.getvalue(), channels=4)
    image = tf.expand_dims(image, 0)
    summary = tf.summary.image("test", image, max_outputs=1)
    writer.add_summary(summary, step)

Где автор является экземпляром tf.summary.FileWriter. Это дало мне следующую ошибку: AttributeError: объект "Тензор" не имеет атрибута "значение" Для чего этот столбец github имел решение: сводка должна быть оценена (преобразована в строку) перед добавлением к записи. Таким образом, рабочий код для меня остался следующим (просто добавьте вызов .eval() в последней строке):

    plt.figure()
    plt.plot([1, 2])
    plt.title("test")
    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    buf.seek(0)
    image = tf.image.decode_png(buf.getvalue(), channels=4)
    image = tf.expand_dims(image, 0)
    summary = tf.summary.image("test", image, max_outputs=1)
    writer.add_summary(summary.eval(), step)

Это может быть достаточно коротким, чтобы быть комментарием к его ответу, но их можно легко упустить (и я могу делать что-то другое по-другому), так что вот оно, надеюсь, это поможет!

Cheers,
Андрес