faster-rcnn 中的RPN網絡的結構解析

本文轉載自:

http://blog.csdn.net/sloanqin/article/details/51545125


【說明】:歡迎加入:faster-rcnn 交流羣 238138700,我想很多人在看faster-rcnn的時候,都會被RPN的網絡結構和連接方式糾結,作者在文中說的不是很清晰,這裏給出解析;

【首先】:大家應該要了解卷積神經網絡的連接方式,卷積核的維度,反向傳播時是如何靈活的插入一層;這裏我推薦一份資料,真是寫的非常清晰,就是MatConvet的用戶手冊,這個框架底層借用的是caffe的算法,所以他們的數據結構,網絡層的連接方式都是一樣的;建議讀者看看,很快的;

下載鏈接:點擊打開鏈接

【前面5層】:作者RPN網絡前面的5層借用的是ZF網絡,這個網絡的結構圖我截個圖放在下面,並分析下爲什麼是這樣子的;


1、首先,輸入圖片大小是 224*224*3(這個3是三個通道,也就是RGB三種)

2、然後第一層的卷積核維度是 7*7*3*96 (所以大家要認識到卷積核都是4維的,在caffe的矩陣計算中都是這麼實現的);

3、所以conv1得到的結果是110*110*96 (這個110來自於 (224-7+pad)/2 +1 ,這個pad是我們常說的填充,也就是在圖片的周圍補充像素,這樣做的目的是爲了能夠整除,除以2是因爲2是圖中的stride, 這個計算方法在上面建議的文檔中有說明與推導的);

4、然後就是做一次池化,得到pool1, 池化的核的大小是3*3,所以池化後圖片的維度是55*55*96  (  (110-3+pad)/2 +1 =55 );

5、然後接着就是再一次卷積,這次的卷積核的維度是5*5*96*256 ,得到conv2:26*26*256;

6、後面就是類似的過程了,我就不詳細一步步算了,要注意有些地方除法除不盡,作者是做了填充了,在caffe的prototxt文件中,可以看到每一層的pad的大小;

7、最後作者取的是conv5的輸出,也就是13*13*256送給RPN網絡的;


【RPN部分】:然後,我們看看RPN部分的結構:


1、前面我們指出,這個conv feature map的維度是13*13*256的;

2、作者在文章中指出,sliding window的大小是3*3的,那麼如何得到這個256-d的向量呢? 這個很簡單了,我們只需要一個3*3*256*256這樣的一個4維的卷積核,就可以將每一個3*3的sliding window 卷積成一個256維的向量;

這裏讀者要注意啊,作者這裏畫的示意圖 僅僅是 針對一個sliding window的;在實際實現中,我們有很多個sliding window,所以得到的並不是一維的256-d向量,實際上還是一個3維的矩陣數據結構;可能寫成for循環做sliding window大家會比較清楚,當用矩陣運算的時候,會稍微繞些;

3、然後就是k=9,所以cls layer就是18個輸出節點了,那麼在256-d和cls layer之間使用一個1*1*256*18的卷積核,就可以得到cls layer,當然這個1*1*256*18的卷積核就是大家平常理解的全連接;所以全連接只是卷積操作的一種特殊情況(當卷積核的大小與圖片大小相同的時候,從卷積層連接到全連接層;當卷積核大小是1*1的時候,從全連接層連接到全連接層);

4、reg layer也是一樣了,reg layer的輸出是36個,所以對應的卷積核是1*1*256*36,這樣就可以得到reg layer的輸出了;

5、然後cls layer 和reg layer後面都會接到自己的損失函數上,給出損失函數的值,同時會根據求導的結果,給出反向傳播的數據,這個過程讀者還是參考上面給的文檔,寫的挺清楚的;


【作者關於RPN網絡的具體定義】:這個作者是放在./models/pascal_voc/ZF/faster_rcnn_alt_opt/stage1_rpn_train.pt 文件中的;

我把這個文件拿出來給註釋下:

[plain] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. name: "ZF"  
  2. layer {  
  3.   name: 'input-data' #這一層就是最開始數據輸入  
  4.   type: 'Python'  
  5.   top: 'data' # top表示該層的輸出,所以可以看到這一層輸出三組數據,data,真值框gt_boxes,和相關信息im_info  
  6.   top: 'im_info' # 這些都是存儲在矩陣中的  
  7.   top: 'gt_boxes'  
  8.   python_param {  
  9.     module: 'roi_data_layer.layer'  
  10.     layer: 'RoIDataLayer'  
  11.     param_str: "'num_classes': 21"  
  12.   }  
  13. }  
  14.   
  15. #========= conv1-conv5 ============  
  16.   
  17. layer {  
  18.     name: "conv1"  
  19.     type: "Convolution"  
  20.     bottom: "data" # 輸入data  
  21.     top: "conv1" # 輸出conv1,這裏conv1就代表了這一層輸出數據的名稱,存儲在對應的矩陣中  
  22.     param { lr_mult: 1.0 }  
  23.     param { lr_mult: 2.0 }  
  24.     convolution_param {  
  25.         num_output: 96  
  26.         kernel_size: 7  
  27.         pad: 3  # 這裏可以看到卷積1層 填充了3個像素  
  28.         stride: 2  
  29.     }  
  30. }  
  31. layer {  
  32.     name: "relu1"  
  33.     type: "ReLU"  
  34.     bottom: "conv1"  
  35.     top: "conv1"  
  36. }  
  37. layer {  
  38.     name: "norm1"  
  39.     type: "LRN"  
  40.     bottom: "conv1"  
  41.     top: "norm1" # 做歸一化操作,通俗點說就是做個除法  
  42.     lrn_param {  
  43.         local_size: 3  
  44.         alpha: 0.00005  
  45.         beta: 0.75  
  46.         norm_region: WITHIN_CHANNEL  
  47.     engine: CAFFE  
  48.     }  
  49. }  
  50. layer {  
  51.     name: "pool1"  
  52.     type: "Pooling"  
  53.     bottom: "norm1"  
  54.     top: "pool1"  
  55.     pooling_param {  
  56.         kernel_size: 3  
  57.         stride: 2  
  58.         pad: 1 # 池化的時候,又做了填充  
  59.         pool: MAX  
  60.     }  
  61. }  
  62. layer {  
  63.     name: "conv2"  
  64.     type: "Convolution"  
  65.     bottom: "pool1"  
  66.     top: "conv2"  
  67.     param { lr_mult: 1.0 }  
  68.     param { lr_mult: 2.0 }  
  69.     convolution_param {  
  70.         num_output: 256  
  71.         kernel_size: 5  
  72.         pad: 2  
  73.         stride: 2  
  74.     }  
  75. }  
  76. layer {  
  77.     name: "relu2"  
  78.     type: "ReLU"  
  79.     bottom: "conv2"  
  80.     top: "conv2"  
  81. }  
  82. layer {  
  83.     name: "norm2"  
  84.     type: "LRN"  
  85.     bottom: "conv2"  
  86.     top: "norm2"  
  87.     lrn_param {  
  88.         local_size: 3  
  89.         alpha: 0.00005  
  90.         beta: 0.75  
  91.         norm_region: WITHIN_CHANNEL  
  92.     engine: CAFFE  
  93.     }  
  94. }  
  95. layer {  
  96.     name: "pool2"  
  97.     type: "Pooling"  
  98.     bottom: "norm2"  
  99.     top: "pool2"  
  100.     pooling_param {  
  101.         kernel_size: 3  
  102.         stride: 2  
  103.         pad: 1  
  104.         pool: MAX  
  105.     }  
  106. }  
  107. layer {  
  108.     name: "conv3"  
  109.     type: "Convolution"  
  110.     bottom: "pool2"  
  111.     top: "conv3"  
  112.     param { lr_mult: 1.0 }  
  113.     param { lr_mult: 2.0 }  
  114.     convolution_param {  
  115.         num_output: 384  
  116.         kernel_size: 3  
  117.         pad: 1  
  118.         stride: 1  
  119.     }  
  120. }  
  121. layer {  
  122.     name: "relu3"  
  123.     type: "ReLU"  
  124.     bottom: "conv3"  
  125.     top: "conv3"  
  126. }  
  127. layer {  
  128.     name: "conv4"  
  129.     type: "Convolution"  
  130.     bottom: "conv3"  
  131.     top: "conv4"  
  132.     param { lr_mult: 1.0 }  
  133.     param { lr_mult: 2.0 }  
  134.     convolution_param {  
  135.         num_output: 384  
  136.         kernel_size: 3  
  137.         pad: 1  
  138.         stride: 1  
  139.     }  
  140. }  
  141. layer {  
  142.     name: "relu4"  
  143.     type: "ReLU"  
  144.     bottom: "conv4"  
  145.     top: "conv4"  
  146. }  
  147. layer {  
  148.     name: "conv5"  
  149.     type: "Convolution"  
  150.     bottom: "conv4"  
  151.     top: "conv5"  
  152.     param { lr_mult: 1.0 }  
  153.     param { lr_mult: 2.0 }  
  154.     convolution_param {  
  155.         num_output: 256  
  156.         kernel_size: 3  
  157.         pad: 1  
  158.         stride: 1  
  159.     }  
  160. }  
  161. layer {  
  162.     name: "relu5"  
  163.     type: "ReLU"  
  164.     bottom: "conv5"  
  165.     top: "conv5"  
  166. }  
  167.   
  168. #========= RPN ============  
  169. # 到我們的RPN網絡部分了,前面的都是共享的5層卷積層的部分  
  170. layer {  
  171.   name: "rpn_conv1"  
  172.   type: "Convolution"  
  173.   bottom: "conv5"  
  174.   top: "rpn_conv1"  
  175.   param { lr_mult: 1.0 }  
  176.   param { lr_mult: 2.0 }  
  177.   convolution_param {  
  178.     num_output: 256  
  179.     kernel_size: 3 pad: 1 stride: 1 #這裏作者把每個滑窗3*3,通過3*3*256*256的卷積核輸出256維,完整的輸出其實是12*12*256,  
  180.     weight_filler { type: "gaussian" std: 0.01 }  
  181.     bias_filler { type: "constant" value: 0 }  
  182.   }  
  183. }  
  184. layer {  
  185.   name: "rpn_relu1"  
  186.   type: "ReLU"  
  187.   bottom: "rpn_conv1"  
  188.   top: "rpn_conv1"  
  189. }  
  190. layer {  
  191.   name: "rpn_cls_score"  
  192.   type: "Convolution"  
  193.   bottom: "rpn_conv1"  
  194.   top: "rpn_cls_score"  
  195.   param { lr_mult: 1.0 }  
  196.   param { lr_mult: 2.0 }  
  197.   convolution_param {  
  198.     num_output: 18   # 2(bg/fg) * 9(anchors)  
  199.     kernel_size: 1 pad: 0 stride: 1 #這裏看的很清楚,作者通過1*1*256*18的卷積核,將前面的256維數據轉換成了18個輸出  
  200.     weight_filler { type: "gaussian" std: 0.01 }  
  201.     bias_filler { type: "constant" value: 0 }  
  202.   }  
  203. }  
  204. layer {  
  205.   name: "rpn_bbox_pred"  
  206.   type: "Convolution"  
  207.   bottom: "rpn_conv1"  
  208.   top: "rpn_bbox_pred"  
  209.   param { lr_mult: 1.0 }  
  210.   param { lr_mult: 2.0 }  
  211.   convolution_param {  
  212.     num_output: 36   # 4 * 9(anchors)  
  213.     kernel_size: 1 pad: 0 stride: 1 <span style="font-family: Arial, Helvetica, sans-serif;">#這裏看的很清楚,作者通過1*1*256*36的卷積核,將前面的256維數據轉換成了36個輸出</span>  
  214.     weight_filler { type: "gaussian" std: 0.01 }  
  215.     bias_filler { type: "constant" value: 0 }  
  216.   }  
  217. }  
  218. layer {  
  219.    bottom: "rpn_cls_score"  
  220.    top: "rpn_cls_score_reshape" # 我們之前說過,其實這一層是12*12*256的,所以後面我們要送給損失函數,需要將這個矩陣reshape一下,我們需要的是144個滑窗,每個對應的256的向量  
  221.    name: "rpn_cls_score_reshape"  
  222.    type: "Reshape"  
  223.    reshape_param { shape { dim: 0 dim: 2 dim: -1 dim: 0 } }  
  224. }  
  225. layer {  
  226.   name: 'rpn-data'  
  227.   type: 'Python'  
  228.   bottom: 'rpn_cls_score'  
  229.   bottom: 'gt_boxes'  
  230.   bottom: 'im_info'  
  231.   bottom: 'data'  
  232.   top: 'rpn_labels'  
  233.   top: 'rpn_bbox_targets'  
  234.   top: 'rpn_bbox_inside_weights'  
  235.   top: 'rpn_bbox_outside_weights'  
  236.   python_param {  
  237.     module: 'rpn.anchor_target_layer'  
  238.     layer: 'AnchorTargetLayer'  
  239.     param_str: "'feat_stride': 16"  
  240.   }  
  241. }  
  242. layer {  
  243.   name: "rpn_loss_cls"  
  244.   type: "SoftmaxWithLoss" # 很明顯這裏是計算softmax的損失,輸入labels和cls layer的18個輸出(中間reshape了一下),輸出損失函數的具體值  
  245.   bottom: "rpn_cls_score_reshape"  
  246.   bottom: "rpn_labels"  
  247.   propagate_down: 1  
  248.   propagate_down: 0  
  249.   top: "rpn_cls_loss"  
  250.   loss_weight: 1  
  251.   loss_param {  
  252.     ignore_label: -1  
  253.     normalize: true  
  254.   }  
  255. }  
  256. layer {  
  257.   name: "rpn_loss_bbox"  
  258.   type: "SmoothL1Loss" # 這裏計算的框迴歸損失函數具體的值  
  259.   bottom: "rpn_bbox_pred"  
  260.   bottom: "rpn_bbox_targets"  
  261.   bottom: "rpn_bbox_inside_weights"  
  262.   bottom: "rpn_bbox_outside_weights"  
  263.   top: "rpn_loss_bbox"  
  264.   loss_weight: 1  
  265.   smooth_l1_loss_param { sigma: 3.0 }  
  266. }  
  267.   
  268. #========= RCNN ============  
  269. # Dummy layers so that initial parameters are saved into the output net  
  270.   
  271. layer {  
  272.   name: "dummy_roi_pool_conv5"  
  273.   type: "DummyData"  
  274.   top: "dummy_roi_pool_conv5"  
  275.   dummy_data_param {  
  276.     shape { dim: 1 dim: 9216 }  
  277.     data_filler { type: "gaussian" std: 0.01 }  
  278.   }  
  279. }  
  280. layer {  
  281.   name: "fc6"  
  282.   type: "InnerProduct"  
  283.   bottom: "dummy_roi_pool_conv5"  
  284.   top: "fc6"  
  285.   param { lr_mult: 0 decay_mult: 0 }  
  286.   param { lr_mult: 0 decay_mult: 0 }  
  287.   inner_product_param {  
  288.     num_output: 4096  
  289.   }  
  290. }  
  291. layer {  
  292.   name: "relu6"  
  293.   type: "ReLU"  
  294.   bottom: "fc6"  
  295.   top: "fc6"  
  296. }  
  297. layer {  
  298.   name: "fc7"  
  299.   type: "InnerProduct"  
  300.   bottom: "fc6"  
  301.   top: "fc7"  
  302.   param { lr_mult: 0 decay_mult: 0 }  
  303.   param { lr_mult: 0 decay_mult: 0 }  
  304.   inner_product_param {  
  305.     num_output: 4096  
  306.   }  
  307. }  
  308. layer {  
  309.   name: "silence_fc7"  
  310.   type: "Silence"  
  311.   bottom: "fc7"  
  312. }  


ps:等源碼看完之後,我再註釋得更詳細些;

作者:香蕉麥樂迪--sloanqin-覃元元


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章