softmax主要應用的地方是logistic模型對於多分類模型的推廣,將多個神經元的輸出問題映射到(0,1)區間中,它的公式爲
當某個類的預測值很大時該類的份量流趨向於1,其餘類的份量值就變得很小。html
在caffe中的softmax layer中它的前向傳播過程以下所示緩存
輸入的向量通過指數化和歸一化後進行輸出,這就是softmax layer的前向計算過程,主要代碼爲ide
template <typename Dtype>
void SoftmaxLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>>& bottom,const vector<Blob<Dtype>>& top) {
const Dtype* bottom_data = bottom[0]->cpu_data();
Dtype* top_data = top[0]->mutable_cpu_data();
Dtype* scale_data = scale_.mutable_cpu_data();
int channels = bottom[0]->shape(softmax_axis_); //channnel的深度
int dim = bottom[0]->count() / outer_num_; //總的類別的數目,也就是長高channnel
caffe_copy(bottom[0]->count(), bottom_data, top_data); //將底層的數據拷貝到頂層緩衝中
// 咱們須要先抽取最大值以防止在指數運算時超過了計算機能表達的最大值
for (int i = 0; i < outer_num_; ++i) {函數
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]);//找出了指定num的channnel通道的最大值 } } // 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 求1+exp caffe_cpu_gemv<Dtype>(CblasTrans, channels, inner_num_, 1., top_data, sum_multiplier_.cpu_data(), 0., scale_data); // division 求softmax的值 即exp/(1+exp) for (int j = 0; j < channels; j++) { caffe_div(inner_num_, top_data, scale_data, top_data); //最後的結果存儲在top中的 top_data += inner_num_; }
}
}
上述函數的做用就是計算softmax函數的結果而且保存在起初的位置,至於爲何要減去均值視爲了放置通過指數化後的值過大沒法在計算機中表示而出現上溢的問題,至於下溢的問題對結果無影響全部就不予考慮了。spa
如上圖所示,咱們設節點4的輸入Z4,輸出爲a4同理適用於節點5,6
則z4=w41a1+w42a2+w43*a3
z5=w51a1+w52a2+w53*a3
z6=w61a1+w62a2+w63*a3
a4=exp(z4)/(exp(z4)+exp(z5)+exp(z6))
同理可得a5,a6,咱們假設更新的是權值w41(對於位於前面的層的權值,咱們能夠經過梯度傳遞來進行計算)那麼要使用梯度降就必需要有一個損失函數,咱們定義損失函數.net
至於爲何採用該函數爲損失函數能夠觀看cs229課程中有講解,主要是由極大似然估計獲得的,其中batch是批數據中樣本的數量。
那麼咱們要計算的是loss對於w41的導數code
由以前的數據咱們能夠看到a4=exp(z4)/(exp(z4)+exp(z5)+exp(z6))就能夠就出a4對於z4的導數。更通常的當咱們更新的權值不是直接與softmax layer相連的怎麼辦?
咱們只須要計算loss對於輸入Zi的的偏導再進行鏈式法則傳播就能夠則通常而言htm
因爲其中f(Zk)就是ak,所以loss對於a的偏導數很好計算,其中f就是softmax函數那麼對於反向傳播的路徑本文中就有三條爲a4->節點4,a4->節點5,a4->節點6,也許你會疑惑爲何a4的值會和5節點有關係,緣由在於softmax函數的分母中包含了exp(z4),exp(z5)和exp(z6)即每一個節點的輸出值都與全部節點的輸入值存在關係,那麼blog
則ip
推導過程爲
最後一部分有書寫錯誤
其中ak和f(zk)是同樣的,依次累加ai便可獲得loss關於zk的偏導數,這就是他的基本原理
代碼爲
template <typename Dtype>
void SoftmaxLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>>& top,const vector<bool>& propagate_down,const vector<Blob<Dtype>>& bottom) {
const Dtype* top_diff = top[0]->cpu_diff(); //存儲的是loss對於a的導數
const Dtype* top_data = top[0]->cpu_data(); //存儲的是a(即softmax後的結果)
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
Dtype* scale_data = scale_.mutable_cpu_data();
int channels = top[0]->shape(softmax_axis_);
int dim = top[0]->count() / outer_num_;
caffe_copy(top[0]->count(), top_diff, bottom_diff);//將loss關於a的導數拷貝到底層diff
for (int i = 0; i < outer_num_; ++i) {
// compute dot(top_diff, top_data) and subtract them from the bottom diff for (int k = 0; k < inner_num_; ++k) { scale_data[k] = caffe_cpu_strided_dot<Dtype>(channels, bottom_diff + i * dim + k, inner_num_, top_data + i * dim + k, inner_num_); }//點積即top_diff*top_data // subtraction 減值 即top_diff-top_diff*top_data caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, channels, inner_num_, 1, -1., sum_multiplier_.cpu_data(), scale_data, 1., bottom_diff + i * dim);
}
// elementwise multiplication 乘法也就是最後一步將結果存儲在bottom_diff中
caffe_mul(top[0]->count(), bottom_diff, top_data, bottom_diff);
}
本文的主要內容來自於
http://blog.csdn.net/l6918993...
http://www.jianshu.com/p/ffa5...
http://www.cnblogs.com/zf-blo...感謝博主,本人能力有限,多多見諒