# 1. 计算AP（Average Precision）的理论知识

$\bg_black \large Precision= \frac{TP}{TP+FP }$

$\bg_black \large Recall= \frac{TP}{TP+FN}$

$\bg_black \large Acc==\frac{TP + TN}{TP+TN+FP+FN}$

1） VOC2007的11Point方法

2） VOC2012以及ILSVRC的 MAXIntegral方法

3） Integral方法（Natural Integral方法）

$\bg_black \large \bg_black \large AP_{11point}=\frac{1}{11}\ast \left (\sum x \right ) \left ( x\in MaxPrecision \right )$

void ComputeAP(const vector<pair<float, int> >& tp, const int num_pos,
const vector<pair<float, int> >& fp, const string ap_version,
vector<float>* prec, vector<float>* rec, float* ap, vector<float> *temp) {
const float eps = 1e-6;
CHECK_EQ(tp.size(), fp.size()) << "tp must have same size as fp.";
const int num = tp.size();
// Make sure that tp and fp have complement value.
for (int i = 0; i < num; ++i) {
CHECK_LE(fabs(tp[i].first - fp[i].first), eps);
CHECK_EQ(tp[i].second, 1 - fp[i].second);
}
prec->clear();
rec->clear();
*ap = 0;
if (tp.size() == 0 || num_pos == 0) {
return;
}

// Compute cumsum of tp.
vector<int> tp_cumsum;
CumSum(tp, &tp_cumsum);
CHECK_EQ(tp_cumsum.size(), num);

// Compute cumsum of fp.
vector<int> fp_cumsum;
CumSum(fp, &fp_cumsum);
CHECK_EQ(fp_cumsum.size(), num);

// Compute precision.
for (int i = 0; i < num; ++i) {
prec->push_back(static_cast<float>(tp_cumsum[i]) /
(tp_cumsum[i] + fp_cumsum[i]));
}

// Compute recall.
for (int i = 0; i < num; ++i) {
CHECK_LE(tp_cumsum[i], num_pos);
rec->push_back(static_cast<float>(tp_cumsum[i]) / num_pos);
}

if (ap_version == "11point") {
// VOC2007 style for computing AP.
vector<float> max_precs(11, 0.);
int start_idx = num - 1;
for (int j = 10; j >= 0; --j) {
for (int i = start_idx; i >= 0 ; --i) {
if ((*rec)[i] < j / 10.) {
start_idx = i;
if (j > 0) {
max_precs[j-1] = max_precs[j];
}
break;
} else {
if (max_precs[j] < (*prec)[i]) {
max_precs[j] = (*prec)[i];
}
}
}
}
for (int j = 10; j >= 0; --j) {
*ap += max_precs[j] / 11;
temp->push_back(max_precs[j]); // save max_prec
}
} else if (ap_version == "MaxIntegral") {
// VOC2012 or ILSVRC style for computing AP.
float cur_rec = rec->back();
float cur_prec = prec->back();
for (int i = num - 2; i >= 0; --i) {
cur_prec = std::max<float>((*prec)[i], cur_prec);
if (fabs(cur_rec - (*rec)[i]) > eps) {
*ap += cur_prec * fabs(cur_rec - (*rec)[i]);
}
cur_rec = (*rec)[i];
}
*ap += cur_rec * cur_prec;
} else if (ap_version == "Integral") {
// Natural integral.
float prev_rec = 0.;
for (int i = 0; i < num; ++i) {
if (fabs((*rec)[i] - prev_rec) > eps) {
*ap += (*prec)[i] * fabs((*rec)[i] - prev_rec);
}
prev_rec = (*rec)[i];
}
} else {
LOG(FATAL) << "Unknown ap_version: " << ap_version;
}
}

# 2. 修改ssd(caffe)代码来输出AP和绘制P-R曲线

## 2.2 修改代码绘制每一类的P-R曲线

i)在bbox_util.cpp中修改的内容主要是，保存每一类在计算过程中的max_precision(11Point)。

修改代码为(主要有两部分，一个是函数定义时：加入了vector<float>* temp, 另一个是在11Point模式下，加入了保存max_precision的代码， temp->bushback(max_precs[j]))：

void ComputeAP(const vector<pair<float, int> >& tp, const int num_pos,
const vector<pair<float, int> >& fp, const string ap_version,
vector<float>* prec, vector<float>* rec, float* ap, vector<float> *temp) {
const float eps = 1e-6;
CHECK_EQ(tp.size(), fp.size()) << "tp must have same size as fp.";
const int num = tp.size();
// Make sure that tp and fp have complement value.
for (int i = 0; i < num; ++i) {
CHECK_LE(fabs(tp[i].first - fp[i].first), eps);
CHECK_EQ(tp[i].second, 1 - fp[i].second);
}
prec->clear();
rec->clear();
*ap = 0;
if (tp.size() == 0 || num_pos == 0) {
return;
}

// Compute cumsum of tp.
vector<int> tp_cumsum;
CumSum(tp, &tp_cumsum);
CHECK_EQ(tp_cumsum.size(), num);

// Compute cumsum of fp.
vector<int> fp_cumsum;
CumSum(fp, &fp_cumsum);
CHECK_EQ(fp_cumsum.size(), num);

// Compute precision.
for (int i = 0; i < num; ++i) {
prec->push_back(static_cast<float>(tp_cumsum[i]) /
(tp_cumsum[i] + fp_cumsum[i]));
}

// Compute recall.
for (int i = 0; i < num; ++i) {
CHECK_LE(tp_cumsum[i], num_pos);
rec->push_back(static_cast<float>(tp_cumsum[i]) / num_pos);
}

if (ap_version == "11point") {
// VOC2007 style for computing AP.
vector<float> max_precs(11, 0.);
int start_idx = num - 1;
for (int j = 10; j >= 0; --j) {
for (int i = start_idx; i >= 0 ; --i) {
if ((*rec)[i] < j / 10.) {
start_idx = i;
if (j > 0) {
max_precs[j-1] = max_precs[j];
}
break;
} else {
if (max_precs[j] < (*prec)[i]) {
max_precs[j] = (*prec)[i];
}
}
}
}
for (int j = 10; j >= 0; --j) {
*ap += max_precs[j] / 11;
temp->push_back(max_precs[j]); // save max_prec
}
} else if (ap_version == "MaxIntegral") {
// VOC2012 or ILSVRC style for computing AP.
float cur_rec = rec->back();
float cur_prec = prec->back();
for (int i = num - 2; i >= 0; --i) {
cur_prec = std::max<float>((*prec)[i], cur_prec);
if (fabs(cur_rec - (*rec)[i]) > eps) {
*ap += cur_prec * fabs(cur_rec - (*rec)[i]);
}
cur_rec = (*rec)[i];
}
*ap += cur_rec * cur_prec;
} else if (ap_version == "Integral") {
// Natural integral.
float prev_rec = 0.;
for (int i = 0; i < num; ++i) {
if (fabs((*rec)[i] - prev_rec) > eps) {
*ap += (*prec)[i] * fabs((*rec)[i] - prev_rec);
}
prev_rec = (*rec)[i];
}
} else {
LOG(FATAL) << "Unknown ap_version: " << ap_version;
}
}

ii) 在bbox_util.hpp中修改的主要是ComputeAP函数的声明参数。

iii) 在solver.cpp 中声明的主要是：打印输出max_precison的情况

vector<float> prec, rec, p_r; // add p_r vector
ComputeAP(label_true_pos, label_num_pos, label_false_pos,
param_.ap_version(), &prec, &rec, &(APs[label]), &p_r); // add &p_r parameter
mAP += APs[label];
if (param_.show_per_class_result()) {
LOG(INFO) << "class" << label << ": " << APs[label];
if(param_.show_pr_value()){ //  add output MAX_Precision if given a Recall value
for(int i=0; i < p_r.size(); i++){
LOG(INFO)<<"for plot P-R for each class: "<<p_r[i];
// LOG(INFO)<<"for plot P-R for each class Precision: "<<prec[i];  // add output line
// LOG(INFO)<<"for plot P-R for each class Recall: "<<rec[i];
}
}
}

iv)在caffe.proto 修改的是: 添加一个控制是否输出每一类的max_precision（11Point）的变量。

// If true, display pr value of per class
optional bool show_pr_value = 45 [default = false];  // add line to show MAX_precision given a Recall value

v) 在caffe/examples/ssd/score_ssd_pascal.py中修改配置文件，选择是否输出 max_precision（11Point）

solver_param = {
# Train parameters
'base_lr': base_lr,
'weight_decay': 0.0005,
'lr_policy': "multistep",
'stepvalue': [80000, 100000, 120000],
'gamma': 0.1,
'momentum': 0.9,
'iter_size': iter_size,
'max_iter': 0,
'snapshot': 0,
'display': 10,
'average_loss': 10,
'type': "SGD",
'solver_mode': solver_mode,
'device_id': device_id,
'debug_info': False,
'snapshot_after_train': False,
# Test parameters
'test_iter': [test_iter],
'test_interval': 10000,
'eval_type': "detection",
'show_per_class_result': True,  ## whether output AP for each class
'show_pr_value': True,    ## whether output max_precision for each class
'ap_version': "11point",  ## the way of computing AP
'test_initialization': True,
}

# [Reference]

[1] ssd（caffe）代码：https://github.com/weiliu89/caffe/tree/ssd

[2] 修改输出AP的代码：https://blog.csdn.net/xunan003/article/details/79252162

[4] 11Point AP计算的理解：https://github.com/Cartucho/mAP

[5] ssd(caffe)源码解读：https://blog.csdn.net/xunan003/article/details/79089280