使用PyCaffe进行特征图及学习曲线可视化

如何将卷基层以及池化层、学习曲线变化率可视化?

特征图可视化

设置常用参数

1
2
3
4
5
6
7
8
#网络结构描述文件
deploy_file = "/home/weijian/caffe/examples/mnist/lenet.prototxt"
#模型文件
model_file = "/home/weijian/caffe/examples/mnist/models/lenet_iter_10000.caffemodel"
#测试图片
test_data = "/home/weijian/caffe/examples/mnist/MNIST_data/0-9/8.bmp"
#特征图路径
feature_map_path = "/home/weijian/caffe/examples/mnist/draw_data/"

编写函数输出各层参数

归一化:减去一个最小值,再除以最大值,数据处于0到1之间。

data.shape[0]是show_data(feature.reshape(20,24,24),'conv1.jpg')中的20,np.sqrt(data.shape[0])对20开平方,得到一个4左右的数,经过np.ceil之后变成5,n=5。即输出的5*5的一个卷积层可视化结果。

padding操作(0, n ** 2 - data.shape[0]), n的平方减去data.shape[0],即25-20,就是(0, 5)。(0, padsize)padsize在参数表中默认是1,即(0, 1)。这样以来原来conv1输出的(20,24,24)就变成了(25,25,25),补(0,1)的目的在于使图片之间有一些间隔,而不是精密的贴合在一起。

numpy.pad简单介绍

形式:pad(array,pad_width,mode,**kwars)

参数说明: - array: 为要填补的数组(input)
- pad_width: 是在数组前后这两个维度的方向上想要填补的长度,如(2,3)是指在前面加2个后面加3个,如果直接输入一个整数,则说明在前后方向所填补的长度都一样。 - mode: 填补类型,即怎样去填补,有“constant”,“edge”等模式,如果为constant模式,就得指定填补的值。

mode选项:

1
2
3
4
5
6
7
8
9
10
constant    ---> 连续一样的值填充,有关于其填充值的参数。constant_values=(x, y)时前面用x填充,后面用y填充。缺参数是为0000
edge ---> 用边缘值填充
linear_ramp ---> 边缘递减的填充方式
maximum ---> 最大值填充
mean ---> 均值填充
median ---> 中位数填充
minimum ---> 最小值填充
reflect ---> 关于边缘对称
symmetric ---> 关于边缘外的空气对称
wrap ---> 用原数组后面的值填充前面,前面的值填充后面
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np
arr1D = np.array([1, 1, 2, 2, 3, 4])
'''不同的填充方法'''
print 'constant: ' + str(np.pad(arr1D, (2, 3), 'constant'))
print 'edge: ' + str(np.pad(arr1D, (2, 3), 'edge'))
print 'linear_ramp: ' + str(np.pad(arr1D, (2, 3), 'linear_ramp'))
print 'maximum: ' + str(np.pad(arr1D, (2, 3), 'maximum'))
print 'mean: ' + str(np.pad(arr1D, (2, 3), 'mean'))
print 'median: ' + str(np.pad(arr1D, (2, 3), 'median'))
print 'minimum: ' + str(np.pad(arr1D, (2, 3), 'minimum'))
print 'reflect: ' + str(np.pad(arr1D, (2, 3), 'reflect'))
print 'symmetric: ' + str(np.pad(arr1D, (2, 3), 'symmetric'))
print 'wrap: ' + str(np.pad(arr1D, (2, 3), 'wrap'))
输出结果:

参考链接

输出各层参数的原始代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#编写一个函数,用于显示各层的参数,padsize用于设置图片间隔空隙,padval用于调整亮度 
def show_data(data, name, padsize=1, padval=0):

#归一化
data -= data.min()
data /= data.max()

#根据data中图片数量data.shape[0],计算最后输出时每行每列图片数n
n = int(np.ceil(np.sqrt(data.shape[0])))

# 对于conv1,data.shape->(20,24,24)
# (前面填补0个,后面填补n ** 2 - data.shape[0]),(前面填补0个,后面填补padsize个),(前面填补0个,后面填补padsize个)
padding = ((0, n ** 2 - data.shape[0]), (0, padsize), (0, padsize))
data = np.pad(data, padding, mode='constant', constant_values=padval)# 常数值填充,填充padval=0

# 对于conv1,padding后data.shape->(25,25,25)
# 对于conv1,将(25,25,25)reshape->(5,5,25,25)再transpose->(5,25,5,25)
data = data.reshape((n, n) + data.shape[1:]).transpose((0, 2, 1, 3))

# 再将(n, W, n, H)变换成(n*w, n*H)
data = data.reshape((n * data.shape[1], n * data.shape[3]))


image_path = os.path.join(feature_map_path,name)#特征图路径
plt.set_cmap('gray')#设置为灰度图
plt.imsave(image_path,data)#保存生成的图片
plt.axis('off')#不显示坐标

print name
#显示图片
img=Image.open(image_path)
plt.imshow(img)
plt.show()
## 数据与处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#----------------------------数据预处理---------------------------------
#初始化caffe
net = caffe.Net(deploy_file, #网络结构描述文件
model_file, #训练好的模型
caffe.TEST) #使用测试模式

#输出网络每一层的参数
print [(k, v[0].data.shape) for k, v in net.params.items()]

transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})

# python读取的图片文件格式为H×W×K(高度,宽度,通道数),需转化为K×H×W(通道数,高度,宽度)
transformer.set_transpose('data', (2, 0, 1))

# python中将图片存储为[0-1]
# 如果模型输入用的是0~255的原始格式,则需要做以下转换
# transformer.set_raw_scale('data', 255)

# caffe中图片是BGR格式,而原始格式是RGB,所以要转化
#transformer.set_channel_swap('data', (2, 1, 0))

数据运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#----------------------------数据运算---------------------------------
#读取图片
#参数color: True(default)是彩色图,False是灰度图
img = caffe.io.load_image(test_data,color=False)

# 数据输入、预处理
net.blobs['data'].data[...] = transformer.preprocess('data', img)

# 将输入图片格式转化为合适格式(与deploy文件相同)
net.blobs['data'].reshape(1, 1, 28, 28)

# 前向迭代,即分类。保存输出
out = net.forward()

# 输出结果为各个可能分类的概率分布
print "Prob:"
print out['prob']

#最可能分类
predict = out['prob'].argmax()
print "Result:" + str(predict)

输出特征图

show_data(feature.reshape(20,24,24),'conv1.jpg')  20(拥有20个卷积核,也就是20个特征平面),24,24(输入的图片是28*28,经过5*5的卷积核采样,之后的特征平面就是24*24)

1
2
3
4
5
6
7
8
9
10
11
12
13
#----------------------------输出特征图---------------------------------
#第一个卷积层输出的特征图
feature = net.blobs['conv1'].data
show_data(feature.reshape(20,24,24),'conv1.jpg') # 20(拥有20个卷积核,也就是20个特征平面),2424(输入的图片是28*28,经过5*5的卷积核采样,之后的特征平面就是24*24)
#第一个池化层输出的特征图
feature = net.blobs['pool1'].data
show_data(feature.reshape(20,12,12),'pool1.jpg')
#第二个卷积层输出的特征图
feature = net.blobs['conv2'].data
show_data(feature.reshape(50,8,8),'conv2.jpg')
#第二个池化层输出的特征图
feature = net.blobs['pool2'].data
show_data(feature.reshape(50,4,4),'pool2.jpg')

conv1的特征输出

conv2的特征输出

从conv1.jpg到conv2.jpg的变化中,以肉眼已经越来越难以分辨6这个数字了,提取出来的是更高维度的关于6这个数字的特征。

学习曲线可视化

使用plot_training_log.py文件生成对应的可视化曲线 路径:/home/weijian/caffe/tools/extra 使用:

1
2
3
4
5
6
7
8
9
10
11
12
python plot_training_log.py [0-7] 可视化曲线路径图片保存路径 log文件路径
Supported chart types:
0: Test accuracy vs. Iters
1: Test accuracy vs. Seconds
2: Test loss vs. Iters
3: Test loss vs. Seconds
4: Train learning rate vs. Iters
5: Train learning rate vs. Seconds
6: Train loss vs. Iters
7: Train loss vs. Seconds
example:
python plot_training_log.py 1 /home/weijian/save.png /home/weijian/caffe/mnist.log
> 可以写一个sh脚本,每间隔1秒执行一次,时时刻刻监控测试输出。 watch -n 1 sh visualization_accuracy.sh