Itsukaraの日記

最新IT技術を勉強・実践中。最近はDeep Learningに注力。

プロブラム出力データの定期的グラフ化ツール

Tensorboardのグラフが貧弱なので、自分でグラフ化ツールを試作した話を下記で書きましたが、多少アップデートしたので掲載します。少しでも役立つ方がいれば幸いです。

itsukara.hateblo.jp

新版では、グラフを一度表示後、ファイルに順次追加されていくデータを定期的に読み込んで、グラフをアップデートします。実行時間が長いプログラムの出力情報を定期的に確認する用途にはぴったりです。

使い方としては、例えば下記となります。

  • scriptコマンドで画面出力内容のファイルへの出力を開始
  • 上記環境で実行時間の長いプログラムを実行し出力を記録
  • 別ターミナルから本ツールを起動し定期的に上記出力をグラフ化

下記が、そのソースコードです。第1引数は、表示させるデータを含むテキストファイル名です。また、「prog = re.compile('t=\s*(\d+),s=\s*(\d+).*r=\s*(\d+)@END')」が、ファイル内のデータ書式のパターンなので、これを書き換えてください。後は、いろいろなオプション引数がありますので、プログラム中の引数の説明を見てください。なお、データが複数行に跨る場合は、書式パーンを複数用意し、read_data()の中で、パアーンマッチを複数回行えば大丈夫と思います。実際、当方も、データが2つの行に跨っている場合に、修正して使ってます。

import numpy as np
import argparse
import matplotlib.pyplot as plt
import time
import re
from operator import itemgetter

parser = argparse.ArgumentParser(description="plot data in A3C log file")
parser.add_argument('filename')
parser.add_argument('-x', '--x-column', type=int, default=1,
                    help="column index of x-axis (0 origin)")
parser.add_argument('-y', '--y-column', type=int, default=2,
                    help="column index of y-axis (0 origin)")
parser.add_argument('-a', '--average-number-of-samples', dest="ans", type=int, default=100,
                    help="average number of samples")
parser.add_argument('-s', '--scale', type=float, default=1e6,
                    help="scale factor: data in x-column is divided by SCALE")
parser.add_argument('-xl', '--xlabel', default="M steps",
                    help="label of x-axis")
parser.add_argument('-yl', '--ylabel', default="Score",
                    help="label of y-axis")
parser.add_argument('-t', '--title', default=None,
                    help="title of figure")
parser.add_argument('-n', '--interval', type=int, default=60,
                    help="interval of refresh")

def read_data(f):
  data = []
  line = f.readline()
  while line != "":
    match = prog.match(line)
    if match:
      t = float(match.group(1))
      s = float(match.group(2))
      r = float(match.group(3))
      data.append([t, s, r])
    line = f.readline()
  return data
def draw_graph(ax, data):
# sort data along args.x_column and make it np.array again
  data = sorted(data, key=itemgetter(args.x_column))
  data = np.array(data)

  x = data[:, args.x_column]
  y = data[:, args.y_column]
  x = x / args.scale
  ax.plot(x, y, ',')

  weight = np.ones(args.ans, dtype=np.float)/args.ans
  y_average = np.convolve(y, weight, 'valid')
  header = np.ones(args.ans - 1) * y_average[0]
  y_average = np.hstack((header, y_average))
  ax.plot(x, y_average)

  ax.set_xlabel(args.xlabel)
  ax.set_ylabel(args.ylabel)

  ax.grid(linewidth=1, linestyle="-", alpha=0.1)

args = parser.parse_args()
if args.title is None:
  args.title = args.filename

f = open(args.filename, "r")
prog = re.compile('t=\s*(\d+),s=\s*(\d+).*r=\s*(\d+)@END')

data = []
fig = plt.figure(args.title)
ax = fig.add_subplot(111)
ax.set_title(args.title)
while True:
  new_data = read_data(f)
  print(len(new_data), "lines added")
  data.extend(new_data)
  ax.clear()
  draw_graph(ax, data)
  plt.pause(args.interval)

あとがき

Githubに載せるほどでもない量なので、ソースコードをブログに直接載せました。