在caffe中的lenet實作最後一層是softmax layer,輸出分類的結果,下面就簡單介紹一下softmax回歸。
1,首先,在caffe中,softmax layer輸出的是原始的輸入在每一個分類标簽上的機率,例如在lenet網絡中,輸出的分類共有0-9的10中分類,那麼softmax layer就會輸出含有10個元素的向量,每一個元素表示輸入在每一個分類上的機率。
那麼,用softmax的目的是讓輸入在正确的分來标簽上的機率最大,這就是我們優化的目标函數,普通的優化函數隻是比較輸出和标簽的內插補點,然後對內插補點進行優化,讓其最小,就可以得出網絡的參數值。但是在分類中,這種方法不太适用,因為輸出的結果不是連續的,而是離散的,是無法對其求梯度的,是以要從機率的角度進行考慮。
那麼問題來了,從機率的角度進行優化是可行的,但是輸入在每個标簽上的機率應該怎麼求呢,這是一個多元分布問題,而softmax就是解決多元分布問題的,在介紹具體的過程之前,我們先看softmax layer的輸入代表的含義,在《deeping learning》的181頁有這麼一句話:we can think of a as a vector of scores whose elements a(i) as associated with each category i, with larger relative scores yielding exponentially larger probabilities. 就是說softmax layer的輸入可以看作是輸入在每個标簽上的打分,分數越高,說明輸入越有可能屬于這個标簽上,那麼我們也可以利用這個分數求輸入相對于每個标簽的機率,分數越高,機率越大。
2, softmax回歸
網上有很多關于softmax回歸的文章,我的了解是softmax本質的作用就是計算softmax layer的輸入在每一個标簽上的機率,caffe中softmax_layer的過程如下:
(1)找出輸入的最大值;
(2)輸入的每一個變量都減去最大值;
(3)對(2)中結果求去指數函數;
(4)對(3)中結果歸一化,得出的結果就是輸入在每一個标簽上機率。
caffe中代碼是在softmax_layer.cpp Line 37
for (int i = 0; i < outer_num_; ++i) { // use softmax to calculate the hypothesis function
// initialize scale_data to the first plane
caffe_copy(inner_num_, bottom_data + i * dim, scale_data);
for (int j = 0; j < channels; j++) {
for (int k = 0; k < inner_num_; k++) {
scale_data[k] = std::max(scale_data[k],
bottom_data[i * dim + j * inner_num_ + k]);
}
}
// subtraction
caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, channels, inner_num_,
1, -1., sum_multiplier_.cpu_data(), scale_data, 1., top_data);
// exponentiation
caffe_exp<Dtype>(dim, top_data, top_data);
// sum after exp
caffe_cpu_gemv<Dtype>(CblasTrans, channels, inner_num_, 1.,
top_data, sum_multiplier_.cpu_data(), 0., scale_data);
// division
for (int j = 0; j < channels; j++) {
caffe_div(inner_num_, top_data, scale_data, top_data);
top_data += inner_num_;
}
}
下面最重要的是求去代價函數,代價函數本質上就是輸入在對應的正确的标簽上的機率,我們優化的目标就是使這個機率最大,一般我們都是對機率取個log運算,然後,由于網絡的訓練是以batch為機關的,假如一個batch裡有100個樣本,我們就要對這100個樣本的log(機率)進行求和,然後平均,最後我們就是利用這個代價函數求梯度,然後利用梯度更新權值。caffe中代碼是在softmax_loss_layer.cpp中Line 100
for (int i = 0; i < outer_num_; ++i){ // sample by sample
for (int j = 0; j < inner_num_; j++) {
const int label_value = static_cast<int>(label[i * inner_num_ + j]);
if (has_ignore_label_ && label_value == ignore_label_) {
continue;
}
DCHECK_GE(label_value, 0);
DCHECK_LT(label_value, prob_.shape(softmax_axis_));
loss -= log(std::max(prob_data[i * dim + label_value * inner_num_ + j],
Dtype(FLT_MIN))); // cost function
++count;
}
}
top[0]->mutable_cpu_data()[0] = loss / get_normalizer(normalization_, count);
關于softmax的具體原理,參考下面兩個部落格:
(1)http://ufldl.stanford.edu/wiki/index.php/Softmax%E5%9B%9E%E5%BD%92
(2)http://blog.csdn.net/acdreamers/article/details/44663305