caffe 顯示各類 accuracy(含 accuracy_layer 源碼修改)

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