1. 程式人生 > >分類任務中label取值的注意事項(caffe框架下),從0開始,連續整數,

分類任務中label取值的注意事項(caffe框架下),從0開始,連續整數,

最近在做一個分類任務的實驗的時候,對標籤的取值產生了一些疑惑,所以看了一點對應的原始碼,順利解決了疑惑,在這裡和大家分享,如果有什麼理解錯誤還請大家指出。
之前做分類任務的時候總有聽說,標籤(label)的取值需要從0開始,按照個人的習慣,我在之前的分類實驗中,標籤的取值一直也都是從0開始(假如有5類,那麼label的取值就是0,1,2,3,4),沒有作死取其他值,所以也一直一帆風順。而在最近的實驗中,偷懶了一下,雖然label的取值還是從0開始,但是出現了間斷(還是假如有5類,那麼label的取值就是類似0,1,2,4,5),訓練的過程就出現了異常,這也是這一次探索label取值的起因。
先把結論寫下來,在不改寫原始碼的情況下,label的取值有下面兩點需要注意:

  1. label的取值必須從0開始;
  2. label必須是連續的整數(不能間斷);
    總結起來就是:label的取值必須是從0開始的連續整數。 (假如有5類,那麼label的取值1,2,3,4,5或者0,1,2,4,5都是不行的)。

下面來看看相應的原始碼就比較清晰了。caffe中,單一標籤的分類任務中一般在AccuracySoftmaxWithLoss這兩個層中會用到label值,而這兩個層對應的程式碼檔案分別為accuracy_layer.cppsoftmax_loss_layer.cpp

首先看一看accuracy_layer.cpp(關於accuracy_layer.cpp

的詳細解讀可以參考博文:link )。
accuracy_layer.cpp 中的Forward_cpu 函式,有下面這段程式碼:

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(bottom_data[i * dim + k * inner_num_ + j], k));
}
  
  • 1
  • 2
  • 3
  • 4

假如我們是5分類問題,那麼這裡的num_labels 就等於5,上面的for迴圈的目的在於將一個樣本的最後一個全連線層(5個神經元)的值和對應的標籤序號配對(這裡是通過make_pair 的方式)。因為迴圈變數k是從0開始的,所以這裡k的取值為0,1,2,3,4。是不是已經看出一點端倪了?嗯,繼續往下看。

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++) {
  if (bottom_data_vector[k].second == label_value) {
    ++accuracy;
    if (top.size() > 1) ++top[1]->mutable_cpu_data()[label_value];
    break;
  }
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

這裡的partial_sort 函式是對bottom_data_vector 中的元素根據其第一項的值(也就是全連線層神經元的值)進行降序排列(這一步的目的是將值越大也就是響應越大的神經元放在越前面)。top_k_ 指的是top幾的準確率,比如在之前的ILSVRC中會有top_1或者top_5的的準確率,我們這裡簡單起見取top_1,也就是top_k_ 為1。
下面的for迴圈對top_k_ 進行,我們這裡top_k_為1,僅僅進行一次迴圈。if語句是,判斷bottom_data_vector 的第1個元素的第二項(也就是響應最大的神經元的序號)和label_value 是否相等,如果相等,那麼計數器accuracy 就加1。這裡的label_value 就是我們在txt檔案中定義的label值。如果label_value 不從0開始且連續的話,就會出現混亂。舉個例子:
假如我們在txt中的label取值(即label_value)為0,1,2,4,5,有一個樣本A,它的label為5。當A樣本作為網路的輸入之後,最後一個神經元的響應最強烈,到這裡一切都是完美和正確的!但是,經過上面的make_pair 所在的for迴圈,程式認為最後一個神經元的序號為4。所以在下面判斷是否分類正確的時候(if (bottom_data_vector[k].second == label_value)),因為4不等於5,所以程式認為這個樣本分類錯誤了!

同樣等混亂會出現在softmax_loss_layer.cpp 中,在Forward_cpu 函式中有下面這句程式碼:

loss -= log(std::max(prob_data[i * dim + label_value * inner_num_ + j], Dtype(FLT_MIN)));
  
  • 1

這句程式碼是在計算loss,而label_value 在這裡是作為一個構成指標偏移量的項(構成指標偏移量的其它項的具體含義可以參見上面關於accuracy_layer.cpp的參考博文),所以如果label_value 不是從0開始的連續整數,會使索引出現混亂,出現loss爆炸的情況。

以上就是對caffe中分類任務的label取值的一些理解。


如果對上面的一些變數不太理解,可以直接加一句程式碼,把變數cout出來看一看。

挺久沒有寫部落格了,感覺還不錯哦~

最近在做一個分類任務的實驗的時候,對標籤的取值產生了一些疑惑,所以看了一點對應的原始碼,順利解決了疑惑,在這裡和大家分享,如果有什麼理解錯誤還請大家指出。
之前做分類任務的時候總有聽說,標籤(label)的取值需要從0開始,按照個人的習慣,我在之前的分類實驗中,標籤的取值一直也都是從0開始(假如有5類,那麼label的取值就是0,1,2,3,4),沒有作死取其他值,所以也一直一帆風順。而在最近的實驗中,偷懶了一下,雖然label的取值還是從0開始,但是出現了間斷(還是假如有5類,那麼label的取值就是類似0,1,2,4,5),訓練的過程就出現了異常,這也是這一次探索label取值的起因。
先把結論寫下來,在不改寫原始碼的情況下,label的取值有下面兩點需要注意: