本想著簡單記錄下過程,沒想到大家這麼喜歡這篇博文,萬分榮幸。可能我記錄的不夠詳細,有人實際操作中出現了一些問題。在此給出工程代碼(由於自己下東西積分時常不夠,所以給資源加了積分。如果沒積分想下載可以私信我郵箱)。 介紹
論文《Visualizing and Understanding Convolutional Networks》通過對卷積網路的逆向操作,將指定卷積層的啟用值反向投影到輸入像素空間,從而揭示輸入映像的不同部分對該層啟用值的貢獻大小。
論文總結了:對於每個層基本上有以下四個操作組成, Conv ReLU MaxPooling [optionally] Norm [optionally]
對應的反向操作為, unpool rectify(ReLU) filter(Deconv)
由於Pooling操作為無法復原的,作為近似的逆操作unpool需要知道pooling操作時pooling region中取得最大值的位置,用Switches來儲存。
Figure 1. Top: A deconvnet layer (left) attached to a con-vnet layer (right). The deconvnet will reconstruct an approximate version of the convnet features from the layer beneath. Bottom: An illustration of the unpooling operation in the deconvnet, using switches which record the location of the local max in each pooling region (colored zones) during pooling in the convnet. 向caffe中加入新層
2017年4月20日更新:
如何在caffe中增加新的layer
新版的caffe中增加新的layer,變得輕鬆多了,概括說來,分四步:
在./src/caffe/proto/caffe.proto 中增加 對應layer的paramter message;
在./include/caffe/layers目錄下增加該layer的類的聲明
在./src/caffe/layers/目錄下建立.cpp和.cu檔案,進行類實現。
在./src/caffe/gtest/中增加layer的測試代碼,對所寫的layer前傳和反傳進行測試,測試還包括速度。
最後一步很多人省了,或者沒意識到,但是為保證代碼正確,建議還是嚴格進行測試,磨刀不誤砍柴功。
摘自:如何在caffe中增加layer以及caffe中triplet loss layer的實現
GitHub - piergiaj/caffe-deconvnet: A deconvolutional network in caffe中給出了這篇文章的一個開源實現。可以看到源碼中只給出了新增的三個層: PoolingSwitches:替代原Pooling層,輸出Pool後的結果和最大值的索引位置Switches。 SliceHalf:對PoolingSwitches層的結果進行分割,是特殊的Slice層。 InvPooling:Pooling的反向操作。
但是由於caffe 版本的原因不能直接將新層加入到caffe工程中,需要做些改動。 改動1:三個層的標頭檔寫在common_layers.hpp和vision_layers.hpp中,這是老版本caffe的寫法,新版中需要獨立建立對應的標頭檔,從原檔案中拷貝出對應的內容,cpp檔案也需要參照新版寫法稍微改動下。 改動2:layer_factory.hpp中加入新層的工廠函數(不能粘貼替換。)。 改動3:修改caffe.proto檔案中V1LayerParameter。
最後重新編譯caffe。 測試
直接使用了github上下載測試案例。convnet使用的是修改後的bvlc_reference_caffenet,將其中的Pooling層換成了PoolingSwitches層。deconvnet是前者的反向操作。二者共用參數。
ConvNet:
DeConvNet:
測試代碼如下:
import numpy as npimport matplotlib.pyplot as pltimport osimport syssys.path.append('./python')import caffecaffe.set_mode_cpu()net = caffe.Net('python-demo/deploy.prototxt', 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel', caffe.TEST)invnet = caffe.Net('python-demo/invdeploy.prototxt',caffe.TEST)# 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('python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1)) # mean pixeltransformer.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 RGBdef norm(x, s=1.0): x -= x.min() x /= x.max() return x*s#對特徵圖進行可視化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)) # tile the filters into an image 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:]) plt.axis('off') plt.imshow(data)net.blobs['data'].data[...] = transformer.preprocess('data', caffe.io.load_image('test/butterfly.jpg'))out = net.forward()#參數共用for b in invnet.params: invnet.params[b][0].data[...] = net.params[b][0].data.reshape(invnet.params[b][0].data.shape)feat = net.blobs['pool5'].datafeat[0][feat[0] < 0] = 0vis_square(feat[0], padval=1)plt.show()#相關賦值操作invnet.blobs['pooled'].data[...] = featinvnet.blobs['switches5'].data[...] = net.blobs['switches5'].datainvnet.blobs['switches2'].data[...] = net.blobs['switches2'].datainvnet.blobs['switches1'].data[...] = net.blobs['switches1'].datainvnet.forward()plt.clf()feat = norm(invnet.blobs['conv1'].data[0],255.0)gci=plt.imshow(transformer.deprocess('data', feat))plt.colorbar(gci);plt.show()
輸入映像:
從Conv5層的啟用值開始,反卷積結果: