深度炼丹 | 这次我站边:数学很重要

经常会看到各路大牛在给深度学习小白铺路的时候会把高等数学,线性代数列为入门级必看读物,当然这个观点褒贬不一,我对这个观点最开始持中立的态度,毕竟很多时候掉包就能应付百分之70的工作。但这次,我站边:学深度学习一定要会点高数。

1.抛出问题

本文从如下知乎问题(也是我这次遇到的问题)展开。问题描述如下:

该问题描述到:如下图所示,在训练的过程中整体loss在下降,但总在几个固定的batch,loss会突然变大,请问要怎么解决?多分类网络在训练过程中loss周期性变大

想要解决问题,我们就必须先分析问题产生的原因。而对于多分类任务,softmax激活和交叉熵损失函数,是你不得不深入了解的两个家伙。在讲这两个家伙之前,请允许花点口舌讲一讲分类回归模型。

2.广义线性模型


如上述公式所示,被称之为广义线性模型。之所以称之为线性模型,因为其参数w和特征x是线性的。之所以称为广义,因为映射g可以是各种类型的映射:线性,非线性,回归,分类。当映射g不同时,我们的模型也会不同。举例如下(本文展开讨论下softmax):

3.softmax和交叉熵

1.softmax
在多分类任务中,softmax和交叉熵代价函数的组合是时下最流行的。上图中多分类标签下的公式就是softmax函数的公式。而如下图所示(来自网络),是softmax函数的计算策略示意图,从图中我们可以看到softmax函数会对每个类别计算概率值。

softmax直白来说就是将原来输出是3,1,-3通过softmax函数一作用,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们就可以将它理解成概率,在最后选取输出结点的时候,我们就可以选取概率最大(也就是值对应最大的)结点,作为我们的预测目标!https://zhuanlan.zhihu.com/p/25723112

2.交叉熵
对于多分类任务和二分类任务,交叉熵损失函数的公式如下所示,因此对于softmax函数而言,其对应的损失为公式一,本文不展开讲交叉熵损失的原理。

交叉熵损失的数学原理
为什么交叉熵损失比均分差损失更适合作损失函数

4.分析问题

根据上述介绍的softmax和其对应的交叉熵损失函数,通过softmax的输出结合one-hot形式的标签,我们可以利用多分类下的交叉熵损失函数计算loss值,计算方式如下图所示。

从图中我们可以看出,若当前softmax输出最大概率的类别和标签是一致的,那么当前样本(或batch)的损失比较小,而当标签对应的类别的softmax激活值比较小时,loss就会很大。经过上述的分析,最终初步定位认为是在训练集中存在一部分数据很难被分类准确。个人认为造成现象的因素有:训练集中标签有问题?最终发现一个3分类的网络中有部分标签被设定为了3(正常的标签为0,1,2),因此此类数据不论怎么样都会被分错而且通过上图的计算,不论网络训练到什么时候,都会在有规律的那个batch导致loss很大。

5.解决问题

如果遇到的问题与上述分析的一致,那解决方案就很简单,直接重新制作训练数据即可。接下来我介绍一种当问题很难定位时,暴力解决上述问题的方法(该方法是基于caffe修改的,利用其他框架时也可以参考,因为思想很简单),只需要在计算loss的时候,当迭代次数大于一定值(可自行设定)后,若当前batch的平均loss还是大于1(可自行设定),就将该loss强行置为零,这样这个错误的loss就不会让参数往错误的方向更新了。
原始的计算前向的函数(softmax_loss_layer.cu):

template <typename Dtype>
void SoftmaxWithLossLayer<Dtype>::Forward_gpu() 
{
…………
…………
…………
  Dtype loss;
  caffe_gpu_asum(nthreads, loss_data, &loss);
  Dtype valid_count = -1;
  if (normalization_ == LossParameter_NormalizationMode_VALID &&
      has_ignore_label_) {
    caffe_gpu_asum(nthreads, counts, &valid_count);
  }
  top[0]->mutable_cpu_data()[0] = loss / get_normalizer(normalization_,
                                                        valid_count);
  …………
  …………
}

更新后的计算前向的函数(softmax_loss_layer.cu):

template <typename Dtype>
void SoftmaxWithLossLayer<Dtype>::Forward_gpu() 
{
…………
…………
…………
  Dtype loss;
  caffe_gpu_asum(nthreads, loss_data, &loss);
  Dtype valid_count = -1;
  if (normalization_ == LossParameter_NormalizationMode_VALID &&
      has_ignore_label_) {
    caffe_gpu_asum(nthreads, counts, &valid_count);
  }
//-------------------------修改部分-----------------------------
static long int batch_num = 0;
batch_num = batch_num + 1;
if (batch_num > 5000  && (loss / get_normalizer(normalization_,valid_count)) > 1.0)
{
 top[0]->mutable_cpu_data()[0] = 0;
}
else
{
 top[0]->mutable_cpu_data()[0] = loss / get_normalizer(normalization_,
                                                        valid_count);
}
 
  …………
  …………
}

所以说,这次真心觉得在炼丹,调包的同时,对原理公式的理解的深入程度,对问题的解决速度会起到决定性的作用。

发布了233 篇原创文章 · 获赞 187 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/qiu931110/article/details/90739350