關於SSD訓練時預設框輸出層輸出通道num_output的計算, 關於SSD訓練時預設框輸出層輸出通道num_output的計算
關於SSD訓練時預設框輸出層輸出通道num_output的計算
2018年02月03日 21:00:40 xunan003 閱讀數:2155申明,本博文是為解決以下兩個問題而撰寫。
Check failed: num_priors_ * num_loc_classes_ * 4 == bottom[0]->channels() (264348 vs. 88116) Number of priors must match number of location predictions.
Check failed: num_priors_ * num_classes_ == bottom[1]->channels() (132174 vs. 99406) Number of priors must match number of confidence predictions.
我們針對小目標檢測時受到此方面的困擾,在閱讀原始碼後解決。特寫下此部落格,以供參考。具體原始碼,請仔細閱讀:src/caffe/layers/multibox_loss_layer.cpp
輸出通道主要涉及預設框產生層的置信輸出mbox_conf和位置輸出mbox_conf。對應prototxt檔案中的應該是以下部分,我們以conv4_3為例講解。
以上就是conv4_3層的在train.prototxt中的內容。我們主要講解num_output=16是怎麼計算來的,在原始SSD中conv4_3產生的每個特徵圖的中心點產生4個預設框,具體預設框的產生數量及方式請參看博主博文: 點選開啟連結layer { name: "conv4_3_norm_mbox_loc" type: "Convolution" bottom: "conv4_3_norm" top: "conv4_3_norm_mbox_loc" param { lr_mult: 1 decay_mult: 1 } param { lr_mult: 2 decay_mult: 0 } convolution_param { num_output: 16 pad: 1 kernel_size: 3 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" value: 0 } } }
這四個預設仍框分別對應x1,y1,x2,y2四個點,所以呢在產生位置損失時就會產生四個loc損失,所以一箇中心點的所產生的4個預設框就有4*4=16個位置資訊需要輸出,這就是16的來源。具體在multibox_loss_layer.cpp中的定義是:
其他5個預設框提取特徵層同樣的計算方法。template <typename Dtype> void MultiBoxLossLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { LossLayer<Dtype>::Reshape(bottom, top); num_ = bottom[0]->num(); num_priors_ = bottom[2]->height() / 4; num_gt_ = bottom[3]->height(); CHECK_EQ(bottom[0]->num(), bottom[1]->num()); CHECK_EQ(num_priors_ * loc_classes_ * 4, bottom[0]->channels()) //num_priors_就是指每個中心點產生的預設框個數,位置個數計算。 << "Number of priors must match number of location predictions."; CHECK_EQ(num_priors_ * num_classes_, bottom[1]->channels()) //置信個數計算。 << "Number of priors must match number of confidence predictions."; }
對於置信輸出通道,其在train.prototxt中的內容為:
layer {
name: "conv4_3_norm_mbox_conf"
type: "Convolution"
bottom: "conv4_3_norm"
top: "conv4_3_norm_mbox_conf"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 64
pad: 1
kernel_size: 3
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0
}
}
}
置信conf的輸出計算方法不同與位置loc,一箇中心點會產生一個預設框,但是這個預設框到底是正樣本還是負樣本,這就涉及到正負的兩個置信,如果是負的,那它就是背景。所以這需要看你的類別,在原始SSD檢測VOC時,類別數為21,所以這裡的num_output應該=(你的類別數+1)*中心點產生的預設框的個數,這裡為21*4=64。它在multibox_loss_layer.cpp中的定義也在上面我們複製出來的程式碼中。
其他5個提取層也是如此計算。
注意的一點是以上是針對conv4_3的計算,而fc7、conv6_2、conv7_2,原始的SSD框架中這三層的每個中心點產生了6個預設框,例如fc7,他在train.prototxt中的內容是:
layer {
name: "fc7_mbox_loc"
type: "Convolution"
bottom: "fc7"
top: "fc7_mbox_loc"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 24
pad: 1
kernel_size: 3
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0
}
}
}
這裡我們只列舉了位置loc,計算為4*6=24。置信輸出就應該是21*6=126了,大家應該知道怎麼計算了吧。
另外我們著重強調一點,關於flip引數的設定,這在我的博文關於ssd_pascal.py解讀中有講解:點選開啟連結
他是設定翻轉的,我們在關於預設框的產生一文中講到,ssd預設產生一大一小兩個正方形預設框,另外,每設定一個aspect_ratio則增加兩個長方形預設框,然而在prior_box_layer.cpp檔案中只有產生一個長方形預設框的計算公式如下:
// rest of priors
for (int r = 0; r < aspect_ratios_.size(); ++r) {
float ar = aspect_ratios_[r];
if (fabs(ar - 1.) < 1e-6) {
continue;
}
box_width = min_size_ * sqrt(ar);
box_height = min_size_ / sqrt(ar);
// xmin
top_data[idx++] = (center_x - box_width / 2.) / img_width;
// ymin
top_data[idx++] = (center_y - box_height / 2.) / img_height;
// xmax
top_data[idx++] = (center_x + box_width / 2.) / img_width;
// ymax
top_data[idx++] = (center_y + box_height / 2.) / img_height;
}
比如我們在conv4_3計算出的min_size=30,max_size=60,而該層的aspect_ratio=2,那麼產生四個預設框,兩個正方形,兩個長方形。這裡只計算了一個長方形框的大小,最後計算的縱橫比為1:2,那麼2:1的縱橫比長方形哪裡去了呢?就是靠我們的flip來計算了,當我們設定flip=True時一個aspect_ratio才會產生兩個預設框,如果不設定或者為Flase那麼就只產生一個長方形預設框。比如conv4_3產生的預設框在train.prototxt中的內容如下:
layer {
name: "conv4_3_norm_mbox_priorbox" //注意在卷積層conv4_3後有一個norm層做歸一化。
type: "PriorBox"
bottom: "conv4_3_norm"
bottom: "data"
top: "conv4_3_norm_mbox_priorbox"
prior_box_param {
min_size: 32
aspect_ratio: 2
flip: true //注意,如果沒有flip引數,則aspect_ratio=2只能產生一個縱橫比為1:2的預設框。
clip: false
variance: 0.1
variance: 0.1
variance: 0.2
variance: 0.2
step: 8
offset: 0.5
}
}
當然,這裡是我們專案中的設定,我們這裡只設置了min_size,因為我們只需要一個較小的正方形邊框就可以了,並不需要較大的正方形邊框,所以我們沒有設定max_size引數,故每個預設框產生特徵層只生成一個邊長為min_size的正方形預設框,剔除邊長為sqrt(min_size*max_size)的預設框。這裡我們設定了一個aspect_ratio=2,所以每個中心點產生3個預設框。這裡flip我吃了很大的虧。一直在報錯。
最後以上面的講解延伸到我們最近研究的小人臉檢測建構SFD,他最低層的預設框提取層是conv3_3,而並不是conv4_4,所以其在conv3_3後面也做了norm操作,另外他在conv3_3後面還添加了一個slice層,注意只在conv3_3後面新增,它在train.prototxt中的內容為:
layer {
name: "conv3_3_norm_mbox_conf"
type: "Convolution"
bottom: "conv3_3_norm"
top: "conv3_3_norm_mbox_conf"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 8 //這裡=類別數*該層每個中心點產生的預設框個數+2,這個2是由於以下新增的slice層的作用導致的。其他的如4_3、5_3、fc7、6_2、7_2這幾層沒有加slice層的則不需要+2。
pad: 1
kernel_size: 3
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "conv3_3_norm_mbox_conf_slice"
type: "Slice"
bottom: "conv3_3_norm_mbox_conf"
top: "conv3_3_norm_mbox_conf1"
top: "conv3_3_norm_mbox_conf2"
top: "conv3_3_norm_mbox_conf3"
top: "conv3_3_norm_mbox_conf4"
slice_param {
axis: 1
slice_point: 1
slice_point: 2
slice_point: 3
}
}
layer {
name: "conv3_3_norm_mbox_conf_maxout"
type: "Eltwise"
bottom: "conv3_3_norm_mbox_conf1"
bottom: "conv3_3_norm_mbox_conf2"
bottom: "conv3_3_norm_mbox_conf3"
top: "conv3_3_norm_mbox_conf_maxout"
eltwise_param {
operation: MAX
}
}
layer {
name: "conv3_3_norm_mbox_conf_out"
type: "Concat"
bottom: "conv3_3_norm_mbox_conf_maxout"
bottom: "conv3_3_norm_mbox_conf4"
top: "conv3_3_norm_mbox_conf_out"
concat_param {
axis: 1
}
}
該層的具體作用,博主還沒有做仔細的解讀,但是其中slice層導致了conv3_3的置信conf輸出發生了變化,要在我們前面講的基礎上加上2,即我們在以上程式碼中所註釋的部分。
最後給大家附上有置信conf和位置loc輸出通道設定錯誤引起的error:
1.由置信引起的:
Check failed: num_priors_ * num_classes_ == bottom[1]->channels() (132174 vs. 99406) Number of priors must match number of confidence predictions.
其中括號裡的數字不必去糾結,這和你的資料有關。
2.由位置引起的:
Check failed: num_priors_ * num_loc_classes_ * 4 == bottom[0]->channels() (264348 vs. 88116) Number of priors must match number of location predictions.
博主水平有限,如有錯誤請多多指正,轉載請註明地址。謝謝!