softmax层的实现

来源:互联网 发布:网络大神作家经典作品 编辑:程序博客网 时间:2024/05/21 12:50

转自:http://blog.csdn.net/l691899397/article/details/52291909

softmax简介

Softmax回归模型是logistic回归模型在多分类问题上的推广,在多分类问题中,待分类的类别数量大于2,且类别之间互斥。比如我们的网络要完成的功能是识别0-9这10个手写数字,若最后一层的输出为[0,1,0, 0, 0, 0, 0, 0, 0, 0],则表明我们网络的识别结果为数字1。

Softmax的公式为,可以直观看出如果某一个zj大过其他z,那这个映射的分量就逼近于1,其他就逼近于0,并且对所有输入数据进行归一化。


softmax层的推导

softmax层的前向计算

在我们的网络中,最后一层是softmax层。


softmax公式为,我们的softmax层有10个输入和10个输出,所以在这里n=10。前向过程只需要根据该函数进行计算即可。
Softmax还有另一种计算方法。假设zk为输入中的最大值,则softmax也可以写成这种形式,经化简2种形式是等价的。


softmax层的反向传播

设softmax的输出为a,输入为z,损失函数为loss。则, 。其中在caffe中是top_diff,a为caffe中的top_data,均为已知量。需要计算的是

直接求导可得
所以


softmax层的损失函数

通常情况下softmax会被用在网络中的最后一层,用来进行最后的分类和归一化。所以其实上边softmax层的反向传播一般不会用到。

Softmax的损失函数使用的是对数损失函数,其中k为该样本的label(即该样本对应的正确输出,比如我们要识别的图片是数字7,则k=7,选择softmax的第7个输出值来计算loss)。一般我们进行训练时一批图片有多张,比如batch size = 16,则

由于我们的输入为,则

若loss对每个输入z求导,则有


Caffe中softmax层的实现

在caffe中,关于softmax层有2种实现,一种是SoftmaxWithLoss,可以计算出loss;另一种为softmax,只计算出每个类别的概率似然值。这样在实际使用中比较灵活。若只是想得到每个类别的概率似然值,则只需使用softmax层即可,就不需调用SoftmaxWithLoss。

SoftmaxWithLoss的配置信息如下:

[cpp] view plain copy
print?
  1. layer {  
  2.   name: "loss"  
  3.   type: "SoftmaxWithLoss"  
  4.   bottom: "ip2"  
  5.   bottom: "label"  
  6.   top: "loss"  
  7. }  

该层的类型为SoftmaxWithLoss,可以计算给出每个样本对应的损失函数值。他有2个输入层,分别为全连接层2和标签值label,输出为求得的多个样本的loss之和除以样本数的值。

Softmax层的配置信息如下:

[cpp] view plain copy
print?
  1. layers {  
  2.   name: "prob"  
  3.   type: “Softmax"  
  4. bottom: " ip2"  
  5.   top: "prob"  
  6. }  

输出为每个类别的概率值。


Caffe中softmax层相关的GPU文件为\src\caffe\layers\ softmax_layer.cu

Caffe中SoftmaxWithLoss层相关的GPU文件为\src\caffe\layers\ softmax_loss_layer.cu

下面分别介绍

前向计算

1、softmax层前向计算过程代码及注释如下

[cpp] view plain copy
print?
  1. template <typename Dtype>  
  2. void SoftmaxLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,  
  3.     const vector<Blob<Dtype>*>& top) {  
  4.   const Dtype* bottom_data = bottom[0]->gpu_data();  
  5.   Dtype* top_data = top[0]->mutable_gpu_data();  
  6.   Dtype* scale_data = scale_.mutable_gpu_data();  
  7.   int count = bottom[0]->count();  
  8.   int channels = top[0]->shape(softmax_axis_);  
  9.   caffe_copy(count, bottom_data, top_data);  
  10.   // compute max  
  11.   //计算最大值max,scale_data为保存最大值的变量  
  12.   kernel_channel_max<Dtype><<<CAFFE_GET_BLOCKS(outer_num_ * inner_num_),  
  13.       CAFFE_CUDA_NUM_THREADS>>>(outer_num_, channels, inner_num_, top_data,  
  14.       scale_data);  
  15.   // subtract  
  16.   //每个输入Zi均减去最大值max  
  17.   kernel_channel_subtract<Dtype><<<CAFFE_GET_BLOCKS(count),  
  18.       CAFFE_CUDA_NUM_THREADS>>>(count, outer_num_, channels, inner_num_,  
  19.       scale_data, top_data);  
  20.   // exponentiate  
  21.   //求e^(Zi-max)  
  22.   kernel_exp<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(  
  23.       count, top_data, top_data);  
  24.   // sum after exp  
  25.   //求和,计算e^(Zi-max),i=[0,n]之和  
  26.   kernel_channel_sum<Dtype><<<CAFFE_GET_BLOCKS(outer_num_ * inner_num_),  
  27.       CAFFE_CUDA_NUM_THREADS>>>(outer_num_, channels, inner_num_, top_data,  
  28.       scale_data);  
  29.   // divide  
  30.   //每一个指数e^(Zi-max)除以上一步求得的最大值  
  31.   kernel_channel_div<Dtype><<<CAFFE_GET_BLOCKS(count),  
  32.       CAFFE_CUDA_NUM_THREADS>>>(count, outer_num_, channels, inner_num_,  
  33.       scale_data, top_data);  
  34. }  

2、SoftmaxWithLoss层前向计算过程代码及注释如下

[cpp] view plain copy
print?
  1. template <typename Dtype>  
  2. void SoftmaxWithLossLayer<Dtype>::Forward_gpu(  
  3.     const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {  
  4.   softmax_layer_->Forward(softmax_bottom_vec_, softmax_top_vec_);  
  5.   const Dtype* prob_data = prob_.gpu_data();  
  6.   const Dtype* label = bottom[1]->gpu_data();  
  7.   const int dim = prob_.count() / outer_num_;  
  8.   const int nthreads = outer_num_ * inner_num_;  
  9.   Dtype* loss_data = bottom[0]->mutable_gpu_diff();  
  10.   Dtype* counts = prob_.mutable_gpu_diff();  
  11.   //计算一个batch中每一个样本的loss  
  12.   SoftmaxLossForwardGPU<Dtype><<<CAFFE_GET_BLOCKS(nthreads),  
  13.       CAFFE_CUDA_NUM_THREADS>>>(nthreads, prob_data, label, loss_data,  
  14.       outer_num_, dim, inner_num_, has_ignore_label_, ignore_label_, counts);  
  15.   Dtype loss;  
  16.     
  17.   //对loss求和  
  18.   caffe_gpu_asum(nthreads, loss_data, &loss);  
  19.   Dtype valid_count = -1;  
  20.   // Only launch another CUDA kernel if we actually need the count of valid  
  21.   // outputs.  
  22.   if (normalization_ == LossParameter_NormalizationMode_VALID &&  
  23.       has_ignore_label_) {  
  24.     caffe_gpu_asum(nthreads, counts, &valid_count);  
  25.   }  
  26.     
  27.   //除以每一个batch中的样本数量  
  28.   top[0]->mutable_cpu_data()[0] = loss / get_normalizer(normalization_,  
  29.                                                         valid_count);  
  30.   if (top.size() == 2) {  
  31.     top[1]->ShareData(prob_);  
  32.   }  
  33. }  

反向传播

1、softmax层反向传播过程代码及注释如下

[cpp] view plain copy
print?
  1. template <typename Dtype>  
  2. void SoftmaxLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,  
  3.     const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {  
  4.   const Dtype* top_diff = top[0]->gpu_diff();  
  5.   const Dtype* top_data = top[0]->gpu_data();  
  6.   Dtype* bottom_diff = bottom[0]->mutable_gpu_diff();  
  7.   Dtype* scale_data = scale_.mutable_gpu_data();  
  8.   int count = top[0]->count();  
  9.   int channels = top[0]->shape(softmax_axis_);  
  10.   caffe_copy(count, top_diff, bottom_diff);  
  11.   // Compute inner1d(top_diff, top_data) and subtract them from the bottom diff.  
  12.   // NOLINT_NEXT_LINE(whitespace/operators)  
  13.   kernel_channel_dot<Dtype><<<CAFFE_GET_BLOCKS(outer_num_ * inner_num_),  
  14.       CAFFE_CUDA_NUM_THREADS>>>(outer_num_, channels, inner_num_,  
  15.       top_diff, top_data, scale_data);  
  16.   // NOLINT_NEXT_LINE(whitespace/operators)  
  17.   kernel_channel_subtract<Dtype><<<CAFFE_GET_BLOCKS(count),  
  18.       CAFFE_CUDA_NUM_THREADS>>>(count, outer_num_, channels, inner_num_,  
  19.       scale_data, bottom_diff);  
  20.   // elementwise multiplication  
  21.   caffe_gpu_mul<Dtype>(top[0]->count(), bottom_diff, top_data, bottom_diff);  
  22. }  

2、SoftmaxWithLoss层反向传播过程代码及注释如下

[cpp] view plain copy
print?
  1. template <typename Dtype>  
  2. void SoftmaxWithLossLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,  
  3.     const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {  
  4.   if (propagate_down[1]) {  
  5.     LOG(FATAL) << this->type()  
  6.                << " Layer cannot backpropagate to label inputs.";  
  7.   }  
  8.   if (propagate_down[0]) {  
  9.     Dtype* bottom_diff = bottom[0]->mutable_gpu_diff();  
  10.     const Dtype* prob_data = prob_.gpu_data();  
  11.     const Dtype* top_data = top[0]->gpu_data();  
  12.     //prob_data为softmax预测结果,复制到bottom_diff  
  13.     caffe_gpu_memcpy(prob_.count() * sizeof(Dtype), prob_data, bottom_diff);  
  14.     const Dtype* label = bottom[1]->gpu_data();  
  15.     const int dim = prob_.count() / outer_num_;  
  16.     const int nthreads = outer_num_ * inner_num_;  
  17.     // Since this memory is never used for anything else,  
  18.     // we use to to avoid allocating new GPU memory.  
  19.     Dtype* counts = prob_.mutable_gpu_diff();  
  20.     //SoftmaxLossBackwardGPU将bottom_diff中对应正确label的概率值-1,其他不变,在原内存计算  
  21.     SoftmaxLossBackwardGPU<Dtype><<<CAFFE_GET_BLOCKS(nthreads),  
  22.         CAFFE_CUDA_NUM_THREADS>>>(nthreads, top_data, label, bottom_diff,  
  23.         outer_num_, dim, inner_num_, has_ignore_label_, ignore_label_, counts);  
  24.   
  25.     Dtype valid_count = -1;  
  26.     // outputs.  
  27.     if (normalization_ == LossParameter_NormalizationMode_VALID &&  
  28.         has_ignore_label_) {  
  29.       caffe_gpu_asum(nthreads, counts, &valid_count);  
  30.     }  
  31.     //除以batch size  
  32.     const Dtype loss_weight = top[0]->cpu_diff()[0] /  
  33.                               get_normalizer(normalization_, valid_count);  
  34.     caffe_gpu_scal(prob_.count(), loss_weight , bottom_diff);  
  35.   }  
  36. }  





原创粉丝点击