您的位置:首页 > 理论基础 > 计算机网络

深度网络中softmax_loss、Smooth L1 loss计算以及反向传播推导

2017-07-20 10:18 323 查看
SSD目标函数分为两个部分:对应默认框的位置loss(loc)和类别置信度loss(conf)。定义

 为第i个默认框和对应的第j个ground
truth box,相应的类别为p。目标函数定义为:



其中,N为匹配的默认框。如果N=0,loss为零。

为预测框

和ground
truth box 

的Smooth L1 loss,

值通过cross
validation设置为1。



定义如下:



其中,

为预测框,

为ground
truth。

为补偿(regress to offsets)后的默认框(

)的中心,

为默认框的宽和高。

传统欧式距离损失函数如下式所示:

 


                       (f1)

 

它在 Logistic Regression 里其到的作用是讲线性预测值转化为类别概率:假设

 (f2) 是第i 个类别的线性预测结果,带入Softmax 的结果其实就是先对每一个  取 exponential 变成非负,然后除以所有项之和进行归一化,现在每个

 (f3)就可以解释成观察到的数据x  属于类别i 的概率,或者称作似然 (Likelihood)。

然后 Logistic Regression 的目标函数是根据最大似然原则来建立的,假设数据 x 所对应的类别为y,则根据我们刚才的计算最大似然就是要最大化Oy  的值 (通常是使用 negative
log-likelihood 而不是 likelihood,也就是说最小化-log(oy)  的值,这两者结果在数学上是等价的。)而 Softmax-Loss 其实就是把两者结合到一起,只要把Oy  的定义展开即可

softmaxloss损失函数:

注意,这里的log代表ln的意思,即:=。

           

                      (f4)

其中,y为图像所属于的类别编号,是真实的标签值;z为图像所对应的数据,这个数据也是输入softmax层的数据,这个数据是深度神经网络输出的数据。比如如果我们要写一个 Logistic
Regression 的 solver,那么因为要处理的就是这个东西,比较自然地就可以将整个东西合在一起来考虑,或者甚至将

(f2)  的定义直接一起带进去然后对w  和b  进行求导来得到 Gradient
Descent 的update rule,例如我们之前介绍 Gradient Descent 的时候举的两类 Logistic
Regression 的例子就是这样做的。

反过来,如果是在设计 Deep Neural Networks 的库,则可能会倾向于将两者分开来看待:因为 Deep Learning 的模型都是一层一层叠起来的结构,一个计算库的主要工作是提供各种各样的 layer,然后让用户可以选择通过不同的方式来对各种 layer 组合得到一个网络层级结构就可以了。比如用户可能最终目的就是得到各个类别的概率似然值,这个时候就只需要一个 Softmax
Layer,而不一定要进行 Multinomial Logistic Loss 操作;或者是用户有通过其他什么方式已经得到了某种概率似然值,然后要做最大似然估计,此时则只需要后面的 Multinomial
Logistic Loss 而不需要前面的 Softmax 操作。因此提供两个不同的 Layer 结构比只提供一个合在一起的 Softmax-Loss
Layer 要灵活许多。从代码的角度来说也显得更加模块化。但是这里自然地就出现了一个问题:numerical stability。

让我们回到 Softmax-Loss 层,由于该层没有参数,我们只需要计算向后传递的导数就可以了,此外由于该层是最顶层,所以不用使用 chain rule 就可以直接计算对于最终输出(loss)的导数。回忆一下我们刚才的 notation,Softmax-Loss 层合在一起的时候我们用

(f5)  来表示,它有两个输入,一个是 true
label ,直接来自于最底部的数据层,并且我们不需要对数据层做任何的 gradient descent 参数更新,所以我们不需要像那个输入进行 back
propagation,但是另外一个输入z 则来自于下面的计算层,对于 Logistic Regression 或者普通的 DNNs 下面会是一个全连通的线性内积层,不过具体是什么我们也不需要关心,只要把

 (f6) 计算出来丢给下面让他们自己去算后面的就好了。根据普通的微积分知识,我们很容易算出:

 

     ; ; ;

  

   K=1,2,…..m 共m个类别; 

其中  是 Softmax-Loss 的中间步骤 Softmax 在 Forward
Pass 的计算结果,k=1,…..,m,表示softmax的输入端口编号。

 

求出之后,令,就求出了zk输出端口应该呈现出来的值。是应该进行调整的数量。

(f7)

                     

            (f8)
// CPU反向传导
template <typename Dtype>
void SoftmaxWithLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
if (propagate_down[1]) {
LOG(FATAL) << this->type()
<< " Lay
a3da
er cannot backpropagate to label inputs.";
}
if (propagate_down[0]) {
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
const Dtype* prob_data = prob_.cpu_data();
// 先将正向传导时计算的prob_数据(f(y_k))拷贝至偏导
caffe_copy(prob_.count(), prob_data, bottom_diff);
const Dtype* label = bottom[1]->cpu_data();
int dim = prob_.count() / outer_num_;
int count = 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]);
// 如果为忽略标签,则偏导为0
if (has_ignore_label_ && label_value == ignore_label_) {
for (int c = 0; c < bottom[0]->shape(softmax_axis_); ++c) {
bottom_diff[i * dim + c * inner_num_ + j] = 0;
}
} else {
// 计算偏导,预测正确的bottom_diff = f(y_k) - 1,其它不变
bottom_diff[i * dim + label_value * inner_num_ + j] -= 1;//使用label直接找到预测正确的Zj
++count;
}
}
}
// top[0]->cpu_diff()[0] = 1.0,已在SetLossWeights()函数中初始化
Dtype loss_weight = top[0]->cpu_diff()[0] /
get_normalizer(normalization_, count);
// 将bottom_diff归一化
caffe_scal(prob_.count(), loss_weight, bottom_diff);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: