一 概述
在使用caffe的过程中,我们知道其在训练过程中会打印出相应的日志信息,可以帮助我们观察和判断模型是否收敛等。将日志信息重定向到文件中,然后利用python解析文件并用matlplotlib库即可从训日志信息中画出loss曲线和accuracy曲线。
二 生成日志文件
在caffe框架中,有些自带的训练脚本会提供输出日志的功能,在此提供通用的生成日志文件的方法,利用2>&1
和tee
命令将训练过程中产生的输出信息重定向到文件中。
具体用法: 训练命令 + 2>&1 | tee + 文件名,下面我们以pvanet的训练脚本为例。
#!/usr/bin/env sh
set -e
./tran_net.py \ #\表示连接不同行的命令
--gpu 0 \
--solver models/pvanet/example_train/solver.prototxt \
--weights models/pvanet/pretrained/pva9.1_pretrained_no_fc6.caffemodel \
--iters 60000 \
--cfg models/pvanet/cfgs/train.yml \
--imdb voc_2007_trainval \
2>&1 | tee ~/pvanet_train_own_voc2007_60000.log
2>&1 的意思是把标准错误输出重定向到标准输出,即同时打印出标准错误信息。2表示STDERR, 1表示STDOUT, >&重定向命令, tee表示读取标准输入的数据,并将其内容输出成文件。
最后我们即获得相应的日志文件pvanet_train_own_voc2007_60000.log
三 解析文件内容
用编辑器打开日志,我们观察到需要获得几个主要参数的值。迭代次数Iteration, 显示间隔display, 最大迭代次数max_iters ,loss_cls, loss_bbox等。
...
display: 20
...
Namespace(cfg_file='models/pvanet/cfgs/train.yml', gpu_id=0, imdb_name='voc_2007_trainval', max_iters=60000, pretrained_model='models/pvanet/pretrained/pva9.1_pretrained_no_fc6.caffemodel', randomize=False, set_cfgs=None, solver='models/pvanet/example_train/solver.prototxt')
...
I0223 23:58:42.539490 143427 solver.cpp:238] Iteration 3260, loss = 0.827335
I0223 23:58:42.539528 143427 solver.cpp:254] Train net output #0: loss_bbox = 0.326038 (* 1 = 0.326038 loss)
I0223 23:58:42.539535 143427 solver.cpp:254] Train net output #1: loss_cls = 0.488903 (* 1 = 0.488903 loss)
I0223 23:58:42.539541 143427 solver.cpp:254] Train net output #2: rpn_loss_bbox = 0.310344 (* 1 = 0.310344 loss)
I0223 23:58:42.539546 143427 solver.cpp:254] Train net output #3: rpn_loss_cls = 0.0431664 (* 1 = 0.0431664 loss)
I0223 23:58:42.539551 143427 sgd_solver.cpp:138] Iteration 3260, lr = 0.001
I0223 23:58:58.118363 143427 solver.cpp:238] Iteration 3280, loss = 0.808611
I0223 23:58:58.118402 143427 solver.cpp:254] Train net output #0: loss_bbox = 0.103488 (* 1 = 0.103488 loss)
I0223 23:58:58.118408 143427 solver.cpp:254] Train net output #1: loss_cls = 0.125076 (* 1 = 0.125076 loss)
I0223 23:58:58.118413 143427 solver.cpp:254] Train net output #2: rpn_loss_bbox = 0.0153892 (* 1 = 0.0153892 loss)
I0223 23:58:58.118418 143427 solver.cpp:254] Train net output #3: rpn_loss_cls = 0.094558 (* 1 = 0.094558 loss)
I0223 23:58:58.118425 143427 sgd_solver.cpp:138] Iteration 3280, lr = 0.001
利用正则表达式解析此文件,获得所需变量的list列表,设置迭代次数为横坐标,loss值为纵坐标。
画图利用python的matplotlib.pyplot中plot函数,代码参考了draw_loss.py进行简单的修改,具体代码如下:
# -*- coding:utf-8 -*-
import os
import argparse
from matplotlib import pyplot as plt
import numpy as np
import re
class Out_put(object):
def __init__(self, test_iter=None, max_iter=None, display=None, train_loss=[], test_accu=[], \
bbox_loss=[], cls_loss=[], rpn_cls_loss=[], rpn_bbox_loss=[]):
self.test_iter = test_iter
self.max_iter = max_iter
self.display = display
self.train_loss = train_loss
self.test_accu = test_accu #accuracy的值
self.bbox_loss = bbox_loss
self.cls_loss = cls_loss
self.rpn_cls_loss = rpn_cls_loss
self.rpn_bbox_loss = rpn_bbox_loss
class Loss(object):
def __init__(self, log, out_put):
self.log = log
self.out_put = out_put
def draw_loss(self):
#观察这几个list的维数与迭代次数的维数是否一致
print len(self.out_put.train_loss)
print len(self.out_put.bbox_loss)
print len(self.out_put.cls_loss)
print len(self.out_put.rpn_cls_loss)
print len(self.out_put.rpn_bbox_loss)
# set the figure
plt.figure(1)
#在一个figure中生成6个子图像
plt.subplot(231)
plt.ylabel('train_loss')
plt.xlabel('Iteration')
plt.plot(np.arange(0, self.out_put.max_iter, self.out_put.display), self.out_put.train_loss, 'bo')
# plt.plot(set_)
plt.subplot(232)
plt.plot(np.arange(0, self.out_put.max_iter, self.out_put.display), self.out_put.bbox_loss, 'r--')
plt.ylabel('bbox_loss')
plt.xlabel('Iteration')
plt.subplot(233)
plt.plot(np.arange(0, self.out_put.max_iter, self.out_put.display), self.out_put.cls_loss, 'g')
plt.ylabel('cls_loss')
plt.xlabel('Iteration')
plt.subplot(234)
plt.plot(np.arange(0, self.out_put.max_iter, self.out_put.display), self.out_put.rpn_cls_loss, 'y')
plt.ylabel('rpn_cls_loss')
plt.xlabel('Iteration')
plt.subplot(235)
plt.plot(np.arange(0, self.out_put.max_iter, self.out_put.display), self.out_put.rpn_bbox_loss, 'k')
plt.ylabel('rpn_bbox_loss')
plt.xlabel('Iteration')
# plt.subplot(236)
plt.show()
def load_loss(self):
log_file = open(self.log)
for line in open(self.log):
line = log_file.readline()
line = line.strip()
flag = line.find("max_iters")
if flag >= 0:
# self.out_put.max_iter = int(line[10:])
# print type(max_iters)
self.out_put.max_iter = int(re.findall(r'max_iters=(.*?),', line)[0])
flag = line.find("display")
if flag >= 0:
self.out_put.display = int(line[8:])
loss_value = re.findall(r'Iteration \d*?, loss = (.*?)$', line)
# self.out_put.train_loss.append(float(line[flag + 6:]))
if len(loss_value):
print line
# print self.out_put.train_loss
self.out_put.train_loss.append(float(loss_value[0]))
# print self.out_put.train_loss
flag = line.find('0: loss_bbox')
if flag >= 0:
# print line
bbox_loss_value = re.findall(r'.*?0: loss_bbox = (.*?) \(\*', line)[0]
# print bbox_loss_value
# print self.out_put.bbox_loss
self.out_put.bbox_loss.append(float(bbox_loss_value))
flag = line.find('1: loss_cls')
if flag >= 0:
self.out_put.cls_loss.append(float(re.findall(r'1: loss_cls = (.*?) \(\*', line)[0]))
flag = line.find('2: rpn_loss_bbox')
if flag >= 0:
self.out_put.rpn_cls_loss.append(float(re.findall(r'2: rpn_loss_bbox = (.*?) \(\*', line)[0]))
flag = line.find('3: rpn_loss_cls')
if flag >= 0:
self.out_put.rpn_bbox_loss.append(float(re.findall(r'3: rpn_loss_cls = (.*?) \(\*', line)[0]))
def get_args():
parser = argparse.ArgumentParser()
parser.add_argument("--log")
return parser.parse_args()
if __name__ == "__main__":
args = get_args()
if args.log == None or (not os.path.exists(args.log)):
print "check logfile"
exit(0)
out_put = Out_put(None, None, None, [], [])
loss = Loss(args.log, out_put)
loss.load_loss()
loss.draw_loss()
具体使用方式 python draw_loss_pvanet.py --log pvanet_train_own_voc2007_60000.log
四 结果
最终显示结果如下
不同的github项目由于作者修改了代码会生成略微不同的日志信息,不过方法都是相通的,只需在此代码基础上稍作修改即可使用。
ps: 这里没有accuracy信息,是因为没有设置验证集(trainval)的缘故, 在训练过程中没有抽取数据进行测试验证。
参考链接
[1] caffe_tool