申明,本博文是为解决以下两个问题而撰写。
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为例讲解。
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
}
}
}
以上就是conv4_3层的在train.prototxt中的内容。我们主要讲解num_output=16是怎么计算来的,在原始SSD中conv4_3产生的每个特征图的中心点产生4个默认框,具体默认框的产生数量及方式请参看博主博文:
点击打开链接
这四个默认仍框分别对应x1,y1,x2,y2四个点,所以呢在产生位置损失时就会产生四个loc损失,所以一个中心点的所产生的4个默认框就有4*4=16个位置信息需要输出,这就是16的来源。具体在multibox_loss_layer.cpp中的定义是:
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.";
}
其他5个默认框提取特征层同样的计算方法。
对于置信输出通道,其在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.
博主水平有限,如有错误请多多指正,转载请注明地址。谢谢!