caffe 显示各类 accuracy(含 accuracy_layer 源码修改)
Tags: Deep_Learning
本文主要包含如下内容:
本篇博客旨在教会你在训练分类网络的时候,用一些简单的操作即可进一步显示具体每个类别的准确率,你可以根据这些信息进一步调整网络
方式一:修改 prototxt 文件
这里,我们需要编辑测试的 prototxt : deploy.prototxt,在其中添加一个top: “class”即可.
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
mean_file: "/home/kb539/YH/work/behavior_recognition/lmdb/imagenet_mean.binaryproto"
mirror: false
crop_size: 224
}
data_param {
source: "/home/kb539/YH/work/behavior_recognition/lmdb/test_lmdb"
batch_size: 128 # 注意batch_size的设置(跟验证集大小有关系)
backend: LMDB
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "fc8_score"
bottom: "label"
top: "accuracy@1"
top: "class" # 源码中有top[0]/top[1],其中top[1]对应每个类别的标签
include: { phase: TEST }
accuracy_param {
top_k: 1
}
}
接下来, 使用 caffe 测试即可, 测试代码显示如下:
#!/usr/bin/env sh
set -e
/home/kb539/YH/caffe-master/build/tools/caffe test --gpu=0 --model=/home/kb539/YH/work/behavior_recognition/vgg_16/deploy.prototxt --weights=/home/kb539/YH/work/behavior_recognition/vgg_16/output/case_two.caffemodel --iterations=21 # iterations*batch_size>=验证集数目
可以得到如下结果:(注意:我的类别为12类)
测试结果:
I0503 15:50:23.471802 12256 caffe.cpp:325] accuracy@1 = 0.857887
I0503 15:50:23.471859 12256 caffe.cpp:325] loss_fc8 = 0.603455 (* 1 = 0.603455 loss)
I0503 15:50:23.471871 12256 caffe.cpp:325] perclass = 0.845481
I0503 15:50:23.471881 12256 caffe.cpp:325] perclass = 0.847117
I0503 15:50:23.471891 12256 caffe.cpp:325] perclass = 0.786423
I0503 15:50:23.471900 12256 caffe.cpp:325] perclass = 0.782536
I0503 15:50:23.471909 12256 caffe.cpp:325] perclass = 0.85791
I0503 15:50:23.471920 12256 caffe.cpp:325] perclass = 0.944581
I0503 15:50:23.471928 12256 caffe.cpp:325] perclass = 0.891931
I0503 15:50:23.471938 12256 caffe.cpp:325] perclass = 0.926242
I0503 15:50:23.471947 12256 caffe.cpp:325] perclass = 0.919357
I0503 15:50:23.471956 12256 caffe.cpp:325] perclass = 0.909317
I0503 15:50:23.471966 12256 caffe.cpp:325] perclass = 0.912399
I0503 15:50:23.471976 12256 caffe.cpp:325] perclass = 0.704083
方式二:直接修改 accuracy_layer.cpp 源码
accuracy_layer.cpp 源码
首先,我们可以阅读源码 accuracy_layer.cpp : 源码的思路就是构造了top[0]/top[1]的 blob,其中,top[0]存储了验证集的准确率,top[1]存储了验证集中每个类别的准确率.
#include <functional>
#include <utility>
#include <vector>
#include "caffe/layers/accuracy_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template <typename Dtype>
void AccuracyLayer<Dtype>::LayerSetUp(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
top_k_ = this->layer_param_.accuracy_param().top_k();
has_ignore_label_ =
this->layer_param_.accuracy_param().has_ignore_label();
if (has_ignore_label_) {
ignore_label_ = this->layer_param_.accuracy_param().ignore_label();
}
}
template <typename Dtype>
void AccuracyLayer<Dtype>::Reshape(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
CHECK_LE(top_k_, bottom[0]->count() / bottom[1]->count())
<< "top_k must be less than or equal to the number of classes.";
label_axis_ =
bottom[0]->CanonicalAxisIndex(this->layer_param_.accuracy_param().axis());
outer_num_ = bottom[0]->count(0, label_axis_); // outer_num_为图像数量,100
inner_num_ = bottom[0]->count(label_axis_ + 1); // inner_num_为每个图像所对应的类别数,1
CHECK_EQ(outer_num_ * inner_num_, bottom[1]->count())
<< "Number of labels must match number of predictions; "
<< "e.g., if label axis == 1 and prediction shape is (N, C, H, W), "
<< "label count (number of labels) must be N*H*W, "
<< "with integer values in {0, 1, ..., C-1}.";
vector<int> top_shape(0); // Accuracy is a scalar; 0 axes. // 整体测试集的准确率
top[0]->Reshape(top_shape);
if (top.size() > 1) {
// Per-class accuracy is a vector; 1 axes.
vector<int> top_shape_per_class(1);
top_shape_per_class[0] = bottom[0]->shape(label_axis_);
top[1]->Reshape(top_shape_per_class); // 对应每个类别的准确率: 10维
nums_buffer_.Reshape(top_shape_per_class); // 对应每个类别的图像总数: 10维
}
}
template <typename Dtype>
void AccuracyLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
Dtype accuracy = 0; // 准确率初始化为0
const Dtype* bottom_data = bottom[0]->cpu_data(); // 输入图像100张,每一张对应10个输出类别 100*10
const Dtype* bottom_label = bottom[1]->cpu_data(); // 图像标签,每一张图像对应一个标签 100*1
const int dim = bottom[0]->count() / outer_num_; // dim = 10,outer_num_ = 100
const int num_labels = bottom[0]->shape(label_axis_); // 类别数目 = 10
vector<Dtype> maxval(top_k_+1);
vector<int> max_id(top_k_+1);
if (top.size() > 1) {
caffe_set(nums_buffer_.count(), Dtype(0), nums_buffer_.mutable_cpu_data());
caffe_set(top[1]->count(), Dtype(0), top[1]->mutable_cpu_data());
}
int count = 0;
for (int i = 0; i < outer_num_; ++i) {
for (int j = 0; j < inner_num_; ++j) { // inner_num_为每个图像所对应的类别数,所以=1
const int label_value =
static_cast<int>(bottom_label[i * inner_num_ + j]);
if (has_ignore_label_ && label_value == ignore_label_) {
continue;
}
if (top.size() > 1) ++nums_buffer_.mutable_cpu_data()[label_value]; // 记录每个类别的图像总数
DCHECK_GE(label_value, 0); // label_value(0~9)大于等于 0
DCHECK_LT(label_value, num_labels); // label_value(0~9)肯定小于 num_labels(10)
// Top-k accuracy // top_k为取前k个最高评分(的预测标签)
std::vector<std::pair<Dtype, int> > bottom_data_vector;
for (int k = 0; k < num_labels; ++k) {
bottom_data_vector.push_back(std::make_pair( // 记录预测结果:dim = 10;inner_num = 1,num_labels = 10
bottom_data[i * dim + k * inner_num_ + j], k));
}
std::partial_sort( // 按预测结果排序
bottom_data_vector.begin(), bottom_data_vector.begin() + top_k_,
bottom_data_vector.end(), std::greater<std::pair<Dtype, int> >());
// check if true label is in top k predictions
for (int k = 0; k < top_k_; k++) { // 只看前top_k个结果
if (bottom_data_vector[k].second == label_value) { // 如果存在标签,即准确值增加
++accuracy;
if (top.size() > 1) ++top[1]->mutable_cpu_data()[label_value]; // 对应每个类别准确率计数 + 1
break;
}
}
++count; // 总统计次数
}
}
// LOG(INFO) << "Accuracy: " << accuracy;
top[0]->mutable_cpu_data()[0] = accuracy / count; // 总的准确率
if (top.size() > 1) {
for (int i = 0; i < top[1]->count(); ++i) { // 对应每个类别的准确率
top[1]->mutable_cpu_data()[i] =
nums_buffer_.cpu_data()[i] == 0 ? 0
: top[1]->cpu_data()[i] / nums_buffer_.cpu_data()[i];
}
}
// Accuracy layer should not be used as a loss function.
}
INSTANTIATE_CLASS(AccuracyLayer);
REGISTER_LAYER_CLASS(Accuracy);
} // namespace caffe
accuracy_layer.cpp 源码修改
接下来:我们对源码进行修改: 即只构造了top[0]的 blob,其中,top[0]存储了验证集的准确率以及验证集中每个类别的准确率.
#include <functional>
#include <utility>
#include <vector>
#include "caffe/layers/accuracy_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template <typename Dtype>
void AccuracyLayer<Dtype>::LayerSetUp(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
top_k_ = this->layer_param_.accuracy_param().top_k();
has_ignore_label_ =
this->layer_param_.accuracy_param().has_ignore_label();
if (has_ignore_label_) {
ignore_label_ = this->layer_param_.accuracy_param().ignore_label();
}
}
template <typename Dtype>
void AccuracyLayer<Dtype>::Reshape(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
CHECK_LE(top_k_, bottom[0]->count() / bottom[1]->count())
<< "top_k must be less than or equal to the number of classes.";
label_axis_ =
bottom[0]->CanonicalAxisIndex(this->layer_param_.accuracy_param().axis());
outer_num_ = bottom[0]->count(0, label_axis_); // outer_num_为图像数量,100
inner_num_ = bottom[0]->count(label_axis_ + 1); // inner_num_为每个图像所对应的类别数,1
CHECK_EQ(outer_num_ * inner_num_, bottom[1]->count())
<< "Number of labels must match number of predictions; "
<< "e.g., if label axis == 1 and prediction shape is (N, C, H, W), "
<< "label count (number of labels) must be N*H*W, "
<< "with integer values in {0, 1, ..., C-1}.";
int dim = bottom[0]->count() / outer_num_; // dim = 10
top[0]->Reshape(1 + dim, 1, 1, 1);
}
template <typename Dtype>
void AccuracyLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
Dtype accuracy = 0; // 准确率初始化为0
const Dtype* bottom_data = bottom[0]->cpu_data(); // 输入图像100张,每一张对应10个输出类别 100*10
const Dtype* bottom_label = bottom[1]->cpu_data(); // 图像标签,每一张图像对应一个标签 100*1
int num = outer_num_; // 图像总数:100
const int dim = bottom[0]->count() / outer_num_; // dim = 10,outer_num_ = 100
vector<Dtype> maxval(top_k_+1);
vector<int> max_id(top_k_+1);
vector<Dtype> accuracies(dim, 0); // 记录每个类别的准确率
vector<Dtype> nums(dim, 0); // 记录每个类别图像的总数
for (int i = 0; i < outer_num_; ++i) {
const int label_value = static_cast<int>(bottom_label[i]); // 每张图像的标签
std::vector<std::pair<Dtype, int> > bottom_data_vector;
for (int k = 0; k < dim; ++k) {
bottom_data_vector.push_back(std::make_pair( // 记录预测结果:dim = 10;inner_num = 1,num_labels = 10
bottom_data[i * dim + k], k));
}
std::partial_sort( // 按预测结果排序
bottom_data_vector.begin(), bottom_data_vector.begin() + top_k_,
bottom_data_vector.end(), std::greater<std::pair<Dtype, int> >());
// check if true label is in top k predictions
for (int k = 0; k < top_k_; k++) { // 只看前top_k个结果
++nums[label_value];
if (bottom_data_vector[k].second == label_value) { // 如果存在标签,即准确值增加
++accuracy;
++accuracies[label_value]; // 对应每个类别准确率计数 + 1
break;
}
}
}
// LOG(INFO) << "Accuracy: " << accuracy;
top[0]->mutable_cpu_data()[0] = accuracy / num; // 总的准确率
for (int i = 0; i < dim; ++i) { // 对应每个类别的准确率
top[0]->mutable_cpu_data()[i + 1] = accuracies[i] / nums[i]; // 输出每个类别的准确率
}
// Accuracy layer should not be used as a loss function.
}
INSTANTIATE_CLASS(AccuracyLayer);
REGISTER_LAYER_CLASS(Accuracy);
} // namespace caffe
最后,在caffe的根目录make即可,你可以得到如下结果:(注意:我的类别为12类,获得了13个输出)
I0503 21:29:25.707322 14206 caffe.cpp:325] accuracy@1 = 0.857887
I0503 21:29:25.707332 14206 caffe.cpp:325] accuracy@1 = 0.845481
I0503 21:29:25.707340 14206 caffe.cpp:325] accuracy@1 = 0.847117
I0503 21:29:25.707346 14206 caffe.cpp:325] accuracy@1 = 0.786423
I0503 21:29:25.707353 14206 caffe.cpp:325] accuracy@1 = 0.782536
I0503 21:29:25.707361 14206 caffe.cpp:325] accuracy@1 = 0.85791
I0503 21:29:25.707370 14206 caffe.cpp:325] accuracy@1 = 0.944581
I0503 21:29:25.707378 14206 caffe.cpp:325] accuracy@1 = 0.891931
I0503 21:29:25.707386 14206 caffe.cpp:325] accuracy@1 = 0.926242
I0503 21:29:25.707392 14206 caffe.cpp:325] accuracy@1 = 0.919357
I0503 21:29:25.707399 14206 caffe.cpp:325] accuracy@1 = 0.909317
I0503 21:29:25.707406 14206 caffe.cpp:325] accuracy@1 = 0.912399
I0503 21:29:25.707414 14206 caffe.cpp:325] accuracy@1 = 0.704083
I0503 21:29:25.707427 14206 caffe.cpp:325] loss_fc8 = 0.603455 (* 1 = 0.603455 loss)