从交叉熵角度理解caffe中softmax_loss层

来源:互联网 发布:流燕抢票软件下载 编辑:程序博客网 时间:2024/05/21 15:41

一、Caffe中的多分类损失函数采用SoftmaxWithLoss层来计算; 损失函数采用的是交叉熵:

            (1)

其中,k为真是的标签,ak表示每个标签的值,而经过softmax()之后,则返回每一个标签的概率,N表示一个批量的大小,

          若去掉批量的概念,即 批量数量为1, 可以理解为只输入一张图片, 来确认它的分类, 则损失函数变为:

                             (2)

那这个交叉熵到底需要怎么理解呢? 他为什么就能作为多分类的损失函数呢? 他在caffe中是怎么实现的呢?

要理解这几个问题, 首先需要了解下面的预备知识:

二、

        2.1信息量:

          一个信号包含信息的多少, 通常这样的理解或要求:

       1)一个非常有可能发生的事情发生了, 信息量较少;

       2) 一个不太可能发生的事情发生了, 信息量较大,更有价值;

         因此,定义的自信息公式为:

              I(x)= - log(P(x))

         其中,P(x)为事件x发生的概率, 由公式可知: 当px较大时, Ix较小; 当px较小时, Ix较大。

          这个公式能较好的反应Px所携有的信息量。


          2.2 熵

             熵是信息量的期望, 表示随机变量的确定性的度量, 既然是期望,则很容易写出公式:

          H(x)= - Ep*log(px),

                对于二分类问题, 即X要么取0,要么取1;

          则熵H(x)=-[ P(X=0)*log(P(X=0)) +(1-P(X=1))*log(1-P(X=1)) ]


         2.3 交叉熵

                 表示两个分布 距离的度量。

           H(x)= - Ep*log(qx),                           (3)

                   其中p和q 分别表示两个分布;  若这两个分布完全相同, p=x的位置, q也非常确定=x(概率qx=1), 则-log(qx)=0,两者的Hx维0;

若两个分布差距很大,即在p=x处, q有很小的概率=x, 即 qx很小, 则-log(qx)会很大, 从而导致两者的交叉熵很大。

            因此, 交叉熵是衡量两个分布的工具;

   

           2.4 交叉熵的应用:

          回归到机器学习上, 其实也是两个分布的弥合过程, 一个是带有标签的训练数据的分布, 另一个神经网络参数的预测数据分布, 怎么利用交叉熵呢?

        比如,我们要预测一张图片上有没有人脸,  我们输入x ----> 通过某种映射f(x)来得到0和1, 1表示有人脸,0表示没有人脸, 但这个映射到底是什么呢???

        不过幸运的是,大数据的到来,让我们有了很多这样的图片, 同时我们也知道它有没有人脸, 即 我们有了 x------>y, 即我们有x(像素)和y(标签), 但我们还是不知道映射关系f(x)到底是什么, 但我们可以通过学习来拟合这个映射f(x), 这里有两个分布:一个是带有标签的训练数据的分布,这个分布是真是存才,并且输出概率是确定的, 另一个神经网络参数的预测数据分布,这个是不断学习的。 我们的目标就是通过不断地学习, 让神经网络的模型分布来接近真是的x--》y的分布, 用什么来衡量这两个分布呢,当然是交叉熵了。


           但你可能会发现交叉熵的公式3, 与caffe中使用的公式2 不相同, 其实那是因为caffe的softmax分类过程中的结果是唯一确定的,比如 是(0,0,1,0,0,)表示一个5分类问题, 其中这个图片是属于第三类的,  也就是公式3中的 Ep,表示带标签的分布,他是唯一确定取第三类,即 Ep 只有在第三个维1, 在其余4个都为0,, 因此只剩下了-log(); 表示在真是标签上的大小。

            比如, 这个预测的结果也是分类到第三类,且概率为1,即qx=(0,0,1,0,0,), 则    损失函数=-log(1)=0;    (正确分类)

                     如另一个预测结果也是分类到第二类,且概率为1,即qx=(0,1,0,0,0,),则    损失函数=-log(0)很大, (错误分类),当然为了数值稳定, 不可能真的取到0, 而是很接近0;


          所以 利用公式2中的交叉熵结果, 如果这个结果较大, 则表示两个分布差别较大(真是分布和预测分布), 表示机器学习的结果不能很好的预测训练集, 跟别说测试集了;当交叉熵的结果(损失函数) 不断减小(梯度下降方法, 随后总结), 趋于很小时, 表示这两个分布近似,  这个模型可以较好预测这个训练分布的数据。 再用来测试训练集。


三、Caffe中的代码实现

     

template <typename Dtype>void SoftmaxWithLossLayer<Dtype>::Forward_cpu(    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {  // The forward pass computes the softmax prob values.  softmax_layer_->Forward(softmax_bottom_vec_, softmax_top_vec_);   //调用softmax层来计算分类的概率, 其中softmax_top_vec和prob_data指向同一地址  const Dtype* prob_data = prob_.cpu_data();  const Dtype* label = bottom[1]->cpu_data();  int dim = prob_.count() / outer_num_;  int count = 0;  Dtype loss = 0;  for (int i = 0; i < outer_num_; ++i) {    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_));    //prob_data是从softmax层输出的概率结果,      loss -= log(std::max(prob_data[i * dim + label_value * inner_num_ + j],                           Dtype(FLT_MIN)));     //仅取出对应label的概率,进行取log操作, 来计算损失函数。      ++count;    }  }  top[0]->mutable_cpu_data()[0] = loss / get_normalizer(normalization_, count);  if (top.size() == 2) {    top[1]->ShareData(prob_);  }}
补充: 关于softmax层:

            其实是计算从全连接层进来的分类的概率。

          计算步骤如下:

归一化概率

之所以减掉最大值, 是为了数值稳定。 防止取e操作时,出现无穷大,  或 当x很小时, 由于计算机的误差 将分母取为0的错误。

caffe实现如下:

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_);  int dim = bottom[0]->count() / outer_num_;  caffe_copy(bottom[0]->count(), bottom_data, top_data);  // We need to subtract the max to avoid numerical issues, compute the exp,  // and then normalize.  for (int i = 0; i < outer_num_; ++i) {    // 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_;    }  }}





            


 

         

原创粉丝点击