Caffe學習——Imagenet分類

Caffe學習——Imagenet分類

1. Caffe安裝

參考Alten Li的Caffe安裝[1]。

2. Imagenet分類

代碼來自Caffe的Notebook Examples[2]。在導入Caffe前,先在sys.path中插入Caffe的路徑:examples文件夾下有子文件夾pycaffe(猜是安裝Caffe時執行“make pycaffe”生成的文件夾),並沒有python文件夾;python文件夾屬於caffe文件夾的子文件夾。所以,如果把根文件夾設爲'/home/parallel/caffe/',導入caffe也是有效的。

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# Make sure that caffe is on the python path:
caffe_root = '/home/parallel/caffe/examples/'  # this file is expected to be in {caffe_root}/examples
import sys
sys.path.insert(0, caffe_root + 'python')

import caffe
figure.figsize爲圖像的大小(分別爲寬和高,單位爲英寸);image.interpolation爲'nearest'表示當屏幕像素和圖像像素不一致時,並沒有在像素間計算插入值(可以理解爲過渡值),所以如果屏幕像素過高,只是複製距這個像素點最近的像素值,所以每個像素值看起來像是個小方塊。默認imshow顯示採用的cmap爲'jet',這裏用'gray',所以顯示的圖像都是灰度圖像。

plt.rcParams['figure.figsize'] = (10, 10)
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
set_mode_cpu設置Caffe跑CPU模式。deploy.prototxt裏定義了網絡架構,後綴爲.caffemodel爲Caffe訓練後得到的模型,最後定義網絡的類型爲'TEST',這時網絡只有前向計算得到輸出結果,沒有反向計算調整caffe模型的參數。
caffe.set_mode_cpu()
net = caffe.Net(caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt',
                caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel',
                caffe.TEST)
io爲Caffe的數據輸入模塊,Transformer初始化時將輸入數據的索引設爲'data',同時設置了輸入數據(這裏爲圖像)的大小。set_transpose負責將索引爲'data'的輸入數據做維度上的交換:如果原始圖像爲RGB圖像,那麼0對應R通道,1對應G通道,2對應B通道,(2,0,1)transpose後'data'變爲BGR。mean.npy爲像素的平均值,這個文件caffe可以自己生成(?:以後用到再說)。set_raw_scale將輸入數據縮放爲原來的255倍。(2,1,0)轉置又將BGR圖像轉換爲RGB圖像。
# input preprocessing: 'data' is the name of the input blob == net.inputs[0]
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2,0,1))
transformer.set_mean('data', np.load('/home/parallel/caffe/python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1)) # mean pixel
transformer.set_raw_scale('data', 255)  # the reference model operates on images in [0,255] range instead of [0,1]
transformer.set_channel_swap('data', (2,1,0))  # the reference model has channels in BGR order instead of RGB
設置塊大小爲50,通道數爲3,圖像大小爲227*227。然後用上面設置的transformer對加載的圖像'cat.jpg'做預處理。

# set net to batch size of 50
net.blobs['data'].reshape(50,3,227,227)
net.blobs['data'].data[...] = transformer.preprocess('data', caffe.io.load_image(caffe_root + 'images/cat.jpg'))
前向計算輸出結果給out。打印out中概率('prob')最大的索引,輸入爲50個圖像爲一組,這裏應該是把'cat.jpg'複製了50張(從data[0]到data[49])。所以'prob'標籤後面的[0]可以修改爲49及以內的任何正整數。

out = net.forward()
print("Predicted class is #{}.".format(out['prob'][0].argmax()))
顯示圖像。imshow設置的'cmap'應該爲'gray',不清楚爲什麼這裏會顯示彩色圖像。

plt.imshow(transformer.deprocess('data', net.blobs['data'].data[0]))
加載groundtruch標籤,存放在synset_words.txt裏面。如果沒有這個文件加載給labels,會執行get_ilsvrc_aux.sh批處理文件獲取該文件。

# load labels
imagenet_labels_filename = '/home/parallel/caffe/data/ilsvrc12/synset_words.txt'
try:
    labels = np.loadtxt(imagenet_labels_filename, str, delimiter='\t')
except:
    !../data/ilsvrc12/get_ilsvrc_aux.sh
    labels = np.loadtxt(imagenet_labels_filename, str, delimiter='\t')
獲取'prob'最大的6個預測索引,這裏最大的概率對應的索引應該爲281,對應的標籤爲'n02123045 tabby, tabby cat'。

# sort top k predictions from softmax output
top_k = net.blobs['prob'].data[0].flatten().argsort()[-1:-6:-1]
print labels[top_k]
設置爲CPU模式,輸出結果爲最好的3次,每次都是1個前向計算(感覺3次輸入1樣,所以基本上沒什麼差異)。CPU每次前向計算花費的時間爲6.45秒;
# CPU mode
net.forward()  # call once for allocation
%timeit net.forward()

1 loops, best of 3: 6.45 s per loop
設置爲GPU模式,同理得到每次前向計算花費的時間爲0.233秒。

# GPU mode
caffe.set_device(0)
caffe.set_mode_gpu()
net.forward()  # call once for allocation
%timeit net.forward()

1 loops, best of 3: 223 ms per loop


網絡架構的繪製使用graphviz[3]。


net.blobs裏包含網絡每塊特徵地圖的字典索引k和對應內容v的大小,net.params訓練後的網絡參數[4]。
net.params爲事先設置的參數尺寸(output channels, input channels, filter height filter width)。
根據net.params中的filter size來計算net.blobs的(batch size, output channels, height, width)。
data: 50張圖片爲1組輸入,通道數爲3(RGB),圖像寬度和高度爲227。
conv1:(227-11)/4+1=55
pool1 :(55-3)/2+1=27
conv2:(27-5+4)/1+1=27
pool2 :(27-3)/2+1=13
conv3:(13-3+2)/1+1=13
conv4:(13-3+2)/1+1=13
conv5:13
pool5 :(13-3)/2+1=6 

[(k, v.data.shape) for k, v in net.blobs.items()]

[('data', (50, 3, 227, 227)),
 ('conv1', (50, 96, 55, 55)),
 ('pool1', (50, 96, 27, 27)),
 ('norm1', (50, 96, 27, 27)),
 ('conv2', (50, 256, 27, 27)),
 ('pool2', (50, 256, 13, 13)),
 ('norm2', (50, 256, 13, 13)),
 ('conv3', (50, 384, 13, 13)),
 ('conv4', (50, 384, 13, 13)),
 ('conv5', (50, 256, 13, 13)),
 ('pool5', (50, 256, 6, 6)),
 ('fc6', (50, 4096)),
 ('fc7', (50, 4096)),
 ('fc8', (50, 1000)),
 ('prob', (50, 1000))]

[(k, v[0].data.shape) for k, v in net.params.items()]

[('conv1', (96, 3, 11, 11)),
 ('conv2', (256, 48, 5, 5)),
 ('conv3', (384, 256, 3, 3)),
 ('conv4', (384, 192, 3, 3)),
 ('conv5', (256, 192, 3, 3)),
 ('fc6', (4096, 9216)),
 ('fc7', (4096, 4096)),
 ('fc8', (1000, 4096))]

vis_square是把data歸一化,然後把n個data放在同一張圖片裏顯示出來。

params存儲網絡中間層的網絡參數,conv1層的參數尺寸爲(96, 3, 11, 11),params['conv1'][0].data爲conv1的權重參數,filters轉置後的尺寸爲(96, 11, 11, 3),符合vis_square函數中data的定義(n, height, width, channels)。

filters = net.params['conv1'][0].data
vis_square(filters.transpose(0, 2, 3, 1))

conv2層的參數尺寸爲(256,48,5,5),params['conv2'][0].data爲conv2的權重參數,這裏只顯示其中的48個。注意到conv1的output channels爲96,conv2的input channels爲48,說明剩下的48個output channels都被丟棄了。

filters = net.params['conv2'][0].data
vis_square(filters[:48].reshape(48**2, 5, 5))


blobs存儲網絡中間層的輸出數據,顯示conv1層前36個特徵圖。根據blobs的定義,data[0, :36]應該是第1張圖片的前36個output channels。

feat = net.blobs['conv1'].data[0, :36]
vis_square(feat, padval=1)

顯示conv2層前36個特徵圖。

feat = net.blobs['conv2'].data[0, :36]
vis_square(feat, padval=1)
顯示conv3層所有的特徵圖。
feat = net.blobs['conv3'].data[0]
vis_square(feat, padval=0.5)
顯示conv4層所有的特徵圖。

feat = net.blobs['conv4'].data[0]
vis_square(feat, padval=0.5)
顯示conv5層所有的特徵圖。
feat = net.blobs['conv5'].data[0]
vis_square(feat, padval=0.5)
顯示pool5層所有的特徵圖。

feat = net.blobs['pool5'].data[0]
vis_square(feat, padval=1)


全連接層的params的結構爲(output channels, input channels),所以fc6和fc7的直方圖橫軸都爲4096。上面的圖爲全連接層4096個神經元的輸出值,下面的圖爲對這些神經元的響應值的強度爲直方圖的輸入,可知fc6大多數神經元的響應值都在40以下,和fc7相比,直方圖的斜率的絕對值應該是偏小。也就是說全連接層前後相連,越往後神經元的響應值越不會均勻分佈化,而通過神經元間的競爭所以最後一層全連接層'prob'產生的尖峯即爲優勝的神經元,這個神經元對應的索引決定了對象屬於哪一類。

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)
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)
feat = net.blobs['prob'].data[0]
plt.plot(feat.flat)




3. 參考鏈接

發佈了54 篇原創文章 · 獲贊 48 · 訪問量 31萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章