YOLOv3损失函数个人见解

YOLOv3损失函数个人见解

网络上对YOLOv3的讲解各有不同,还有用YOLOv1损失函数来代表YOLOv3函数的,这里我发表一下个人对YOLOv3损失函数的见解。

损失函数

在这里本人认为损失函数就像YOLOv3原作者在论文所说的:

  • 边界框运用均方误差
  • 置信度运用交叉熵
  • 类别运用交叉熵

用公式表示如下:
在这里插入图片描述
源码中边框坐标也有乘以scale(2-wh)。对于scale,我的理解是:wh越小,就表示面积越小,面积越小,在和anchor做比较的时候,iou也就会越小,scale便是用来弱化边界框尺寸对损失值的影响。而对于坐标,本人认为没这个必要,所以上述公式坐标那一块并没有乘以scale。

函数求导讲解

均方误差求导:
在这里插入图片描述
交叉熵函数求导:
在这里插入图片描述
logistic函数求导:
在这里插入图片描述

源码解释

本人认为项目源码中并没有直接给出损失函数的表达,而是直接跳过了这一步进行反向传播求梯度
源码中有一个很重要的变量delta,本人认为这便是存放梯度的
Delta =

  1. 类别:
 delta[index + stride*n] = ((n == class_id) ? 1 : 0) - output[index + stride*n]; 
 if (n == class_id && avg_cat) *avg_cat += output[index + stride*n]; 
  1. 置信度:
 int obj_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 4); 
 avg_anyobj += l.output[obj_index]; 
 l.delta[obj_index] = 0 - l.output[obj_index]; 
 if (best_iou > l.ignore_thresh) { 
     l.delta[obj_index] = 0; 
 } 
 if (best_iou > l.truth_thresh) { 
     l.delta[obj_index] = 1 - l.output[obj_index]; 
  1. 边框:
 float tx = (truth.x*lw - i); 
 float ty = (truth.y*lh - j); 
 float tw = log(truth.w*w / biases[2*n]); 
 float th = log(truth.h*h / biases[2*n + 1]); 
  
 delta[index + 0*stride] = scale * (tx - x[index + 0*stride]); 
 delta[index + 1*stride] = scale * (ty - x[index + 1*stride]); 
 delta[index + 2*stride] = scale * (tw - x[index + 2*stride]); 
 delta[index + 3*stride] = scale * (th - x[index + 3*stride]); 

这里要注意,源码中对坐标,置信度,类别的输出都做了logistic函数处理回归。代码如下:

int index = entry_index(l, b, n*l.w*l.h, 0);
activate_array_gpu(l.output_gpu + index, 2*l.w*l.h, LOGISTIC);
index = entry_index(l, b, n*l.w*l.h, 4);
activate_array_gpu(l.output_gpu + index, (1+l.classes)*l.w*l.h, LOGISTIC);

源码里有如下这一行代码,本人认为这段代码只能作为损失标准,但不作为正式损失函数。损失函数是用来反向传播调节误差的,而代码中l.cost只是单纯地进行赋值后就没再调用过这个变量,也就是说并没有进入反向传播中,进行反向传播的应该是delta这块,所以只能从delta这里逆推求损失函数。

*(l.cost) = pow(mag_array(l.delta, l.outputs * l.batch), 2);

注释:

 float mag_array(float *a, int n) 
 { 
     int i; 
     float sum = 0; 
     for(i = 0; i < n; ++i){ 
         sum += a[i]*a[i];    
     } 
     return sqrt(sum); 

按类别和置信度运用交叉熵来推理,对于类别和置信度的反向求导如下(y(1-y)为logistic函数求导):
在这里插入图片描述
符合上述delta中类别和置信度的代码

而对于边框,大家可以先看下图注释:
在这里插入图片描述
按边框为均方误差同上面步骤来推理,delta本该为:

delta[index + 0*stride] = scale * (tx - x[index + 0*stride]) * (1 - x[index + 0*stride]) * x[index + 0*stride];
delta[index + 1*stride] = scale * (ty - x[index + 1*stride])* (1 - x[index + 1*stride]) * x[index + 1*stride];

但源码中却是下面代码(这里本人认为是原作者故意设置的,计算sigmoid(tx)之间而不是tx值之间的均方误差损失)。
x[index+0&stride]在源码中表示sigmoid(x),如果是按对sigmoid(x)逆推而不是x逆推,则损失函数可表示为:0.5*scale(tx-sigmoid(x))^2

 delta[index + 0*stride] = scale * (tx - x[index + 0*stride]); 
 delta[index + 1*stride] = scale * (ty - x[index + 1*stride]); 

以上是本人对YOLOv3中损失函数的理解,如有不对之处,望指出

发布了1 篇原创文章 · 获赞 4 · 访问量 47

猜你喜欢

转载自blog.csdn.net/weixin_39128022/article/details/105201250