可視化網(wǎng)絡(luò)模型
使用Netscope在線可視化
Netscope
Netscope能可視化神經(jīng)網(wǎng)絡(luò)體系結(jié)構(gòu)(或技術(shù)上說殖熟,Netscope能可視化任何有向無環(huán)圖)谁鳍。目前Netscope能可視化Caffe的prototxt 文件盆赤。網(wǎng)址為:ethereon.github.io/netscope/#/…Netscope的使用非常簡單反镇,只需要將prototxt的文件復(fù)制到Netscope的編輯框宇智,再按快捷鍵Shift+Enter即可得到網(wǎng)絡(luò)模型的可視化結(jié)構(gòu)。Netscope的優(yōu)點是顯示的網(wǎng)絡(luò)模型簡潔敌蜂,而且將鼠標放在右側(cè)可視化的網(wǎng)絡(luò)模型的任意模塊上拾枣,會顯示該模塊的具體參數(shù)。圖1以Faster R-CNN中ZF模型的train.prototxt文件為例
可視化圖像特征
關(guān)于圖像的可視化薇溃,我也使用過兩種兩種方式:
修改demo.py代碼輸出中間層結(jié)果
使用可視化工具deep-visualization-toolbox
修改demo.py
該部分是參考薛開宇的《caffe學(xué)習(xí)筆記》中的逐層特征可視化部分菌赖,還是以ZFNet網(wǎng)絡(luò)訓(xùn)練Pascal VOC為例,修改demo.py文件后沐序,代碼如下:
#!/usr/bin/env python
#-*-coding:utf-8-*-
import matplotlib
matplotlib.use('Agg')
import _init_paths
from fast_rcnn.config import cfg
from fast_rcnn.test import im_detect
from fast_rcnn.nms_wrapper import nms
from utils.timer import Timer
import matplotlib.pyplot as plt
import numpy as np
import scipy.io as sio
import caffe, os, sys, cv2
import argparse
CLASSES = ('__background__',
? ? ? ? ? 'aeroplane', 'bicycle', 'bird', 'boat',
? ? ? ? ? 'bottle', 'bus', 'car', 'cat', 'chair',
? ? ? ? ? 'cow', 'diningtable', 'dog', 'horse',
? ? ? ? ? 'motorbike', 'person', 'pottedplant',
? ? ? ? ? 'sheep', 'sofa', 'train', 'tvmonitor')
NETS = {'vgg16': ('VGG16',
? ? ? ? ? ? ? ? ? 'VGG16_faster_rcnn_final.caffemodel'),
? ? ? ? 'zf': ('ZF',
? ? ? ? ? ? ? ? ? 'zf_faster_rcnn_iter_2000.caffemodel')}
def vis_detections(im, class_name, dets, thresh=0.5):
? ? """Draw detected bounding boxes."""
? ? inds = np.where(dets[:, -1] >= thresh)[0]
? ? if len(inds) == 0:
? ? ? ? return
? ? im = im[:, :, (2, 1, 0)]
? ? fig, ax = plt.subplots(figsize=(12, 12))
? ? ax.imshow(im, aspect='equal')
? ? for i in inds:
? ? ? ? bbox = dets[i, :4]
? ? ? ? score = dets[i, -1]
? ? ? ? ax.add_patch(
? ? ? ? ? ? plt.Rectangle((bbox[0], bbox[1]),
? ? ? ? ? ? ? ? ? ? ? ? ? bbox[2] - bbox[0],
? ? ? ? ? ? ? ? ? ? ? ? ? bbox[3] - bbox[1], fill=False,
? ? ? ? ? ? ? ? ? ? ? ? ? edgecolor='red', linewidth=3.5)
? ? ? ? ? ? )
? ? ? ? ax.text(bbox[0], bbox[1] - 2,
? ? ? ? ? ? ? ? '{:s} {:.3f}'.format(class_name, score),
? ? ? ? ? ? ? ? bbox=dict(facecolor='blue', alpha=0.5),
? ? ? ? ? ? ? ? fontsize=14, color='white')
? ? ax.set_title(('{} detections with '
? ? ? ? ? ? ? ? ? 'p({} | box) >= {:.1f}').format(class_name, class_name,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? thresh),
? ? ? ? ? ? ? ? ? fontsize=14)
? ? plt.axis('off')
? ? plt.tight_layout()
? ? plt.draw()
def demo(net, image_name):
? ? """Detect object classes in an image using pre-computed object proposals."""
? ? # Load the demo image
? ? im_file = os.path.join(cfg.DATA_DIR, 'demo', image_name)
? ? im = cv2.imread(im_file)
? ? # Detect all object classes and regress object bounds
? ? timer = Timer()
? ? timer.tic()
? ? scores, boxes = im_detect(net, im)
? ? timer.toc()
? ? print ('Detection took {:.3f}s for '
? ? ? ? ? '{:d} object proposals').format(timer.total_time, boxes.shape[0])
? ? # Visualize detections for each class
? ? CONF_THRESH = 0.8
? ? NMS_THRESH = 0.3
? ? for cls_ind, cls in enumerate(CLASSES[1:]):
? ? ? ? cls_ind += 1 # because we skipped background
? ? ? ? cls_boxes = boxes[:, 4*cls_ind:4*(cls_ind + 1)]
? ? ? ? cls_scores = scores[:, cls_ind]
? ? ? ? dets = np.hstack((cls_boxes,
? ? ? ? ? ? ? ? ? ? ? ? ? cls_scores[:, np.newaxis])).astype(np.float32)
? ? ? ? keep = nms(dets, NMS_THRESH)
? ? ? ? dets = dets[keep, :]
? ? ? ? vis_detections(im, cls, dets, thresh=CONF_THRESH)
def parse_args():
? ? """Parse input arguments."""
? ? parser = argparse.ArgumentParser(description='Faster R-CNN demo')
? ? parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',
? ? ? ? ? ? ? ? ? ? ? ? default=0, type=int)
? ? parser.add_argument('--cpu', dest='cpu_mode',
? ? ? ? ? ? ? ? ? ? ? ? help='Use CPU mode (overrides --gpu)',
? ? ? ? ? ? ? ? ? ? ? ? action='store_true')
? ? parser.add_argument('--net', dest='demo_net', help='Network to use [zf]',
? ? ? ? ? ? ? ? ? ? ? ? choices=NETS.keys(), default='zf')
? ? args = parser.parse_args()
? ? return args
if __name__ == '__main__':
? ? cfg.TEST.HAS_RPN = True? # Use RPN for proposals
? ? args = parse_args()
? ? prototxt = os.path.join(cfg.MODELS_DIR, NETS[args.demo_net][0],
? ? ? ? ? ? ? ? ? ? ? ? ? ? 'faster_rcnn_alt_opt', 'faster_rcnn_test.pt')
? ? caffemodel = os.path.join(cfg.DATA_DIR, 'faster_rcnn_models',
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NETS[args.demo_net][1])
? ? if not os.path.isfile(caffemodel):
? ? ? ? raise IOError(('{:s} not found.\nDid you run ./data/script/'
? ? ? ? ? ? ? ? ? ? ? 'fetch_faster_rcnn_models.sh?').format(caffemodel))
? ? if args.cpu_mode:
? ? ? ? caffe.set_mode_cpu()
? ? else:
? ? ? ? caffe.set_mode_gpu()
? ? ? ? caffe.set_device(args.gpu_id)
? ? ? ? cfg.GPU_ID = args.gpu_id
? ? net = caffe.Net(prototxt, caffemodel, caffe.TEST)
#指定caffe路徑琉用,以下是我的caffe路徑
? ? caffe_root='/home/ouyang/GitRepository/py-faster-rcnn/caffe-fast-rcnn/'
? ? # import sys
? ? sys.path.insert(0, caffe_root+'python')
? ? # import caffe
? ? # #顯示的圖表大小為 10,圖形的插值是以最近為原則,圖像顏色是灰色
? ? plt.rcParams['figure.figsize'] = (10, 10)
? ? plt.rcParams['image.interpolation'] = 'nearest'
? ? plt.rcParams['image.cmap'] = 'gray'
? ? image_file = caffe_root+'examples/images/vehicle_0000015.jpg'?
? ? # 載入模型
? ? npload = caffe_root+ 'python/caffe/imagenet/ilsvrc_2012_mean.npy'?
? ? transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
? ? transformer.set_transpose('data', (2,0,1))
? ? transformer.set_mean('data', np.load(npload).mean(1).mean(1))
? ? # 參考模型的灰度為0~255,而不是0~1
? ? transformer.set_raw_scale('data', 255)
? ? # 由于參考模型色彩是BGR,需要將其轉(zhuǎn)換為RGB
? ? transformer.set_channel_swap('data', (2,1,0))
? ? im=caffe.io.load_image(image_file)
? ? net.blobs['data'].reshape(1,3,224,224)
? ? net.blobs['data'].data[...] = transformer.preprocess('data',im)
? ? # 顯示出各層的參數(shù)和形狀策幼,第一個是批次邑时,第二個是feature map數(shù)目,第三和第四是每個神經(jīng)元中圖片的長和寬
? ? print [(k,v.data.shape) for k,v in net.blobs.items()]
? ? #輸出網(wǎng)絡(luò)參數(shù)
? ? print [(k,v[0].data.shape) for k,v in net.params.items()]
? ? def show_image(im):
? ? ? ? if im.ndim==3:
? ? ? ? ? ? m=im[:,:,::-1]
? ? ? ? plt.imshow(im)
? ? ? ? #顯示圖片的方法
? ? ? ? plt.axis('off') # 不顯示坐標軸
? ? ? ? plt.show()? ?
? ? # 每個可視化的都是在一個由一個個網(wǎng)格組成
? ? def vis_square(data,padsize=1,padval=0):
? ? ? ? data-=data.min()
? ? ? ? data/=data.max()
? ? ? ? # force the number of filters to be square
? ? ? ? n=int(np.ceil(np.sqrt(data.shape[0])))
? ? ? ? padding=((0,n**2-data.shape[0]),(0,padsize),(0,padsize))+((0,0),)*(data.ndim-3)
? ? ? ? data=np.pad(data,padding,mode='constant',constant_values=(padval,padval))
? ? ? ? # 對圖像使用濾波器
? ? ? ? data=data.reshape((n,n)+data.shape[1:]).transpose((0,2,1,3)+tuple(range( 4,data.ndim+1)))
? ? ? ? data=data.reshape((n*data.shape[1],n*data.shape[3])+data.shape[4:])?
? ? ? ? #show_image(data)
? ? ? ? plt.imshow(data)
? ? ? ? plt.show()
? ? ? ? # 設(shè)置圖片的保存路徑垄惧,此處是我的路徑
? ? ? ? plt.savefig("./tools/Vehicle_2000/fc6.jpg")
? ? out = net.forward()
? ? image=net.blobs['data'].data[4].copy()
? ? image-=image.min()
? ? image/=image.max()
? ? # 顯示原始圖像
? ? show_image(image.transpose(1,2,0))
? ? #網(wǎng)絡(luò)提取conv1的卷積核
? ? filters = net.params['conv1'][0].data
? ? vis_square(filters.transpose(0, 2, 3, 1))
? ? #過濾后的輸出,96 張 featuremap
? ? feat =net.blobs['conv1'].data[0,:96]
? ? vis_square(feat,padval=1)
? ? #第二個卷積層,顯示全部的96個濾波器,每一個濾波器為一行刁愿。
? ? filters = net.params['conv2'][0].data
? ? vis_square(filters[:96].reshape(96**2, 5, 5))
? ? # #第二層輸出 256 張 featuremap
? ? feat = net.blobs['conv2'].data[0]
? ? vis_square(feat, padval=1)
? ? filters = net.params['conv3'][0].data
? ? vis_square(filters[:256].reshape(256**2, 3, 3))
? ? # 第三個卷積層:全部 384 個 feature map
? ? feat = net.blobs['conv3'].data[0]
? ? vis_square(feat, padval=0.5)
? ? #第四個卷積層,我們只顯示前面 48 個濾波器,每一個濾波器為一行。
? ? filters = net.params['conv4'][0].data
? ? vis_square(filters[:384].reshape(384**2, 3, 3))
? ? # 第四個卷積層:全部 384 個 feature map
? ? feat = net.blobs['conv4'].data[0]
? ? vis_square(feat, padval=0.5)
? ? # 第五個卷積層:全部 256 個 feature map
? ? filters = net.params['conv5'][0].data
? ? vis_square(filters[:384].reshape(384**2, 3, 3))
? ? feat = net.blobs['conv5'].data[0]
? ? vis_square(feat, padval=0.5)
? ? #第五個 pooling 層
? ? feat = net.blobs['fc6'].data[0]
? ? vis_square(feat, padval=1)
? ? 第六層輸出后的直方分布
? ? feat=net.blobs['fc6'].data[0]
? ? plt.subplot(2,1,1)
? ? plt.plot(feat.flat)
? ? plt.subplot(2,1,2)
? ? _=plt.hist(feat.flat[feat.flat>0],bins=100)
? ? # #顯示圖片的方法
? ? #plt.axis('off') # 不顯示坐標軸
? ? plt.show()?
? ? plt.savefig("fc6_zhifangtu.jpg")
? ? # 第七層輸出后的直方分布
? ? feat=net.blobs['fc7'].data[0]
? ? plt.subplot(2,1,1)
? ? plt.plot(feat.flat)
? ? plt.subplot(2,1,2)
? ? _=plt.hist(feat.flat[feat.flat>0],bins=100)
? ? plt.show()
? ? plt.savefig("fc7_zhifangtu.jpg")
? ? #看標簽
? ? #執(zhí)行測試?
? ? image_labels_filename=caffe_root+'data/ilsvrc12/synset_words.txt'
? ? #try:
? ? labels=np.loadtxt(image_labels_filename,str,delimiter='\t')
? ? top_k=net.blobs['prob'].data[0].flatten().argsort()[-1:-6:-1]
? ? #print labels[top_k]
? ? for i in np.arange(top_k.size):
? ? ? ? print top_k[i], labels[top_k[i]]
下面貼幾張檢測結(jié)果
圖3 原始檢測圖片
圖4 conv1參數(shù)可視化
圖5 conv1特征可視化
deep-visualization-toolbox
deep-visualization-toolbox是Jason Yosinsk出版在Computer Science上的一篇論文的源代碼到逊,改論文主要講述的是卷積神經(jīng)網(wǎng)絡(luò)的可視化铣口,感興趣的朋友可以看看這篇論文(論文地址)。B站上有個講怎么使用該工具的視頻觉壶,這里附上鏈接www.bilibili.com/video/av740…脑题。 該工具的源碼在github:github.com/yosinski/de…。該github下有完整的安裝配置步驟铜靶,還是以圖2中的馬為例叔遂,貼幾張檢測結(jié)果圖。
圖6 ToolBox conv1特征可視化
圖7 ToolBox conv2特征可視化
從檢測效果上看争剿,還是挺簡潔的已艰。圖片左側(cè)的一列圖片左上角是輸入圖片,中間部分是圖片經(jīng)過網(wǎng)絡(luò)前向傳播得到的特征圖可視化蚕苇,左下角是其特征可視化哩掺。
Loss可視化
網(wǎng)絡(luò)訓(xùn)練過程中Loss值的可視化可以幫助分析該網(wǎng)絡(luò)模型的參數(shù)是否合適。在使用Faster R-CNN網(wǎng)絡(luò)訓(xùn)練模型時涩笤,訓(xùn)練完成后的日志文件中保存了網(wǎng)絡(luò)訓(xùn)練各個階段的loss值嚼吞,如圖8所示。只用寫簡單的python程序蹬碧,讀取日志文件中的迭代次數(shù)舱禽,以及需要的損失值,再畫圖即可完成Loss的可視化恩沽。
圖8 模型的訓(xùn)練日志
在下面貼出Loss可視化的代碼:
#!/usr/bin/env python?
import os?
import sys?
import numpy as np?
import matplotlib.pyplot as plt?
import math?
import re?
import pylab?
from pylab import figure, show, legend?
from mpl_toolkits.axes_grid1 import host_subplot?
# 日志文件名
fp = open('faster_rcnn_end2end_ZF_.txt.2018-04-13_19-46-23', 'r',encoding='UTF-8')
train_iterations = []?
train_loss = []?
test_iterations = []?
#test_accuracy = []?
for ln in fp:?
? # get train_iterations and train_loss?
? if '] Iteration ' in ln and 'loss = ' in ln:?
? ? arr = re.findall(r'ion \b\d+\b,',ln)?
? ? train_iterations.append(int(arr[0].strip(',')[4:]))?
? ? train_loss.append(float(ln.strip().split(' = ')[-1]))?
fp.close()?
host = host_subplot(111)?
plt.subplots_adjust(right=0.8) # ajust the right boundary of the plot window?
#par1 = host.twinx()?
# set labels?
host.set_xlabel("iterations")?
host.set_ylabel("RPN loss")?
#par1.set_ylabel("validation accuracy")?
# plot curves?
p1, = host.plot(train_iterations, train_loss, label="train RPN loss")?
.?
host.legend(loc=1)?
# set label color?
host.axis["left"].label.set_color(p1.get_color())?
host.set_xlim([-1000, 60000])?
host.set_ylim([0., 3.5])?
plt.draw()?
plt.show()?
可視化效果如下圖所示
圖9 Loss可視化
畫PR圖
參考:https://github.com/rbgirshick/py-faster-rcnn/issues/670
在pascal_voc.py里添加幾行代碼即可:
1誊稚,文件頭部:
importmatplotlib.pyplot as plt
importpylab as pl
from sklearn.metricsimportprecision_recall_curve
from itertoolsimportcycle
2,_do_python_eval函數(shù):
def _do_python_eval(self, output_dir='output'):
? ? annopath = os.path.join(
self._devkit_path,
'VOC'+self._year,
'Annotations',
'{:s}.xml')
? ? imagesetfile = os.path.join(
self._devkit_path,
'VOC'+self._year,
'ImageSets',
'Main',
self._image_set +'.txt')
cachedir = os.path.join(self._devkit_path,'annotations_cache')
? ? aps = []
# The PASCAL VOC metric changed in 2010
use_07_metric =Trueifint(self._year) <2010elseFalse
print('VOC07 metric? '+ ('Yes'ifuse_07_metricelse'No'))
ifnot os.path.isdir(output_dir):
? ? ? os.mkdir(output_dir)
fori, cls in enumerate(self._classes):
ifcls =='__background__':
continue
filename =self._get_voc_results_file_template().format(cls)
? ? ? rec, prec, ap = voc_eval(
filename, annopath, imagesetfile, cls, cachedir, ovthresh=0.5,
? ? ? ? use_07_metric=use_07_metric)
? ? ? aps += [ap]
pl.plot(rec, prec, lw=2,
label='Precision-recall curve of class {} (area = {:.4f})'
''.format(cls, ap))
print(('AP for {} = {:.4f}'.format(cls, ap)))
with open(os.path.join(output_dir, cls +'_pr.pkl'),'wb')asf:
pickle.dump({'rec': rec,'prec': prec,'ap': ap}, f)
pl.xlabel('Recall')
pl.ylabel('Precision')
plt.grid(True)
pl.ylim([0.0,1.05])
pl.xlim([0.0,1.0])
pl.title('Precision-Recall')
pl.legend(loc="upper right")
? ? plt.show()
print(('Mean AP = {:.4f}'.format(np.mean(aps))))
print('~~~~~~~~')
print('Results:')
forap in aps:
print(('{:.3f}'.format(ap)))
print(('{:.3f}'.format(np.mean(aps))))
print('~~~~~~~~')
print('')
print('--------------------------------------------------------------')
print('Results computed with the **unofficial** Python eval code.')
print('Results should be very close to the official MATLAB eval code.')
print('Recompute with `./tools/reval.py --matlab ...` for your paper.')
print('-- Thanks, The Management')
print('--------------------------------------------------------------')
然后運行test_net.py,就可以得到如下圖的PR曲線里伯。如果想比較多條曲線绽昏,可以先把rec, prec數(shù)據(jù)存起來再畫圖。