深入理解YOLO v3实现细节系列文章,是本人根据自己对YOLO v3原理的理解,结合开源项目tensorflow-yolov3,写的学习笔记。如有不正确的地方,请大佬们指出,谢谢!


目录

第1篇 数据预处理

第2篇 backbone&network

第3篇 构建v3的 Loss_layer

1. 解读YOLOv3 的损失函数

在分析yolo v3的损失函数之前,先来回顾一下yolo v1的损失函数。

一般来说系统设计需要平衡边界框坐标损失置信度损失分类损失。论文中设计的损失函数如下:

对于 YOLOv3 的损失函数, Redmon J 在论文中并没有进行讲解。从darknet 源代码中的forward_yolo_layer函数中找到 l.cost ,通过解读,总结得到 YOLOv3 的损失函数如下:

与v1 Loss类似,主要分为三大部分: 边界框坐标损失, 分类损失置信度损失。

边界框坐标损失
表示置信度,判断网格内有无物体。

与yolo v1的边界框坐标损失类似,v3中使用误差平方损失函数分别计算(x, y, w, h)的Loss,然后加在一起。v1中作者对宽高(w, h)做了开根号处理,为了弱化边界框尺寸对损失值的影响。在v3中作者没有采取开根号的处理方式,而是增加1个与物体框大小有关的权重,权重=2 - 相对面积,取值范围(1~2)

分类损失
表示置信度,判断网格内有无物体。使用

误差平方损失函数计算类别class 的Loss。

置信度损失

使用误差平方损失函数计算置信度conf 的Loss。

YOLO v3网络输出

yolo v3三种不同尺度的输出,一共产生了(13*13*3+26*26*3+52*52*3)=10647个预测框。

这个10647就是这么来的。

最终Loss采用和的形式而不是平均Loss, 主要原因为预测的特殊机制, 造成正负样本比巨大, 尤其是置信度损失部分, 以一片包含一个目标为例, 置信度部分的正负样本比可以高达1:10646, 如果采用平均损失, 会使损失趋近于0, 网络预测变为全零, 失去预测能力。

参考上述原始的损失函数,加以优化。开始动手构建v3的Loss_layer!

2. 边界框损失

对IoU和GIoU不了解的读者可以看我写的另外一篇文章: 目标检测中的IOU&升级版GIOU

目前目标检测中主流的边界框优化采用的都是BBox的回归损失(MSE loss, L1-smooth loss等),这些方式计算损失值的方式都是检测框的“代理属性”—— 距离,而忽略了检测框本身最显著的性质——IoU。由于IOU有2个致命缺点,导致它不太适合作为损失函数。GIoU延续了IoU的优良特性,并消除了IoU的致命缺点。以下使用GIoU Loss作为边界框损失。

2.1 GIoU的计算

    def bbox_giou(self, boxes1, boxes2):# 将boxes[x,y,w,h]化为[x_min, y_min, x_max, y_max]的形式boxes1 = tf.concat([boxes1[..., :2] - boxes1[..., 2:] * 0.5,boxes1[..., :2] + boxes1[..., 2:] * 0.5], axis=-1)boxes2 = tf.concat([boxes2[..., :2] - boxes2[..., 2:] * 0.5,boxes2[..., :2] + boxes2[..., 2:] * 0.5], axis=-1)boxes1 = tf.concat([tf.minimum(boxes1[..., :2], boxes1[..., 2:]),tf.maximum(boxes1[..., :2], boxes1[..., 2:])], axis=-1)boxes2 = tf.concat([tf.minimum(boxes2[..., :2], boxes2[..., 2:]),tf.maximum(boxes2[..., :2], boxes2[..., 2:])], axis=-1)# 计算boxes1、boxes2的面积boxes1_area = (boxes1[..., 2] - boxes1[..., 0]) * (boxes1[..., 3] - boxes1[..., 1])boxes2_area = (boxes2[..., 2] - boxes2[..., 0]) * (boxes2[..., 3] - boxes2[..., 1])# 计算boxes1和boxes2交集的左上角坐标和右下角坐标left_up = tf.maximum(boxes1[..., :2], boxes2[..., :2])right_down = tf.minimum(boxes1[..., 2:], boxes2[..., 2:])# 计算交集区域的宽高,如果right_down - left_up < 0,没有交集,宽高设置为0inter_section = tf.maximum(right_down - left_up, 0.0)# 交集面积等于交集区域的宽 * 高inter_area = inter_section[..., 0] * inter_section[..., 1]# 计算并集面积union_area = boxes1_area + boxes2_area - inter_area# 计算IOUiou = inter_area / union_area# 计算最小闭合凸面 C 左上角和右下角的坐标enclose_left_up = tf.minimum(boxes1[..., :2], boxes2[..., :2])enclose_right_down = tf.maximum(boxes1[..., 2:], boxes2[..., 2:])# 计算最小闭合凸面 C的宽高enclose = tf.maximum(enclose_right_down - enclose_left_up, 0.0)# 计算最小闭合凸面 C的面积 = 宽 * 高enclose_area = enclose[..., 0] * enclose[..., 1]# 计算GIoUgiou = iou - 1.0 * (enclose_area - union_area) / enclose_areareturn giou

2.2 GIoU loss 的计算

还记得label是怎么来吗?在 第1篇 数据预处理 的4.3章节中有详细说明,这里不再多说。当你清楚了label是什么,表示什么,respond_bbox就容易理解了。

respond_bbox  = label[:, :, :, :, 4:5]  # 置信度,判断网格内有无物体
...
giou = tf.expand_dims(self.bbox_giou(pred_xywh, label_xywh), axis=-1)
# 2 - 相对面积
bbox_loss_scale = 2.0 - 1.0 * label_xywh[:, :, :, :, 2:3] * label_xywh[:, :, :, :, 3:4] / (input_size ** 2)giou_loss = respond_bbox * bbox_loss_scale * (1 - giou)

  • respond_bbox 的意思是如果网格单元中包含物体,那么就会计算边界框损失;
  • box_loss_scale = 2 - 相对面积,值的范围是(1~2),边界框的尺寸越小,bbox_loss_scale 的值就越大。box_loss_scale可以弱化边界框尺寸对损失值的影响;
  • 两个边界框之间的 GIoU 值越大,giou 的损失值就会越小, 因此网络会朝着预测框与真实框重叠度较高的方向去优化。

3. 置信度损失

3.1 引入Focal Loss

论文发现,密集检测器训练过程中,所遇到的极端前景背景类别不均衡(extreme foreground-background class imbalance)是核心原因。为了解决 one-stage 目标检测器在训练过程中出现的极端前景背景类不均衡的问题,引入Focal Loss。Focal Loss, 通过修改标准的交叉熵损失函数,降低对能够很好分类样本的权重(down-weights the loss assigned to well-classified examples),解决类别不均衡问题.

Focal Loss的计算公式:

是不同类别的分类概率, 权重因子
是个[0,1]间的小数,缩放因子
是个大于0的值。
都是固定值,不参与训练。
的最优值是相互影响的,所以在评估准确度时需要把两者组合起来调节。作者在论文中给出

= 0.25 (即正负样本比例为1:3)、

= 2 时性能最佳此外,实验结果表面在计算

时用sigmoid方法比softmax准确度更高。

讲了这么多,我们来看代码吧!

先定义权重因子

和缩放因子

target分为前景类和背景类,当target是前景类时为1,背景类时为0。

    def focal(self, target, actual, alpha=0.25, gamma=2):focal_loss = tf.abs(alpha + target - 1) * tf.pow(tf.abs(target - actual), gamma)return focal_loss

鉴于论文实验结果表明sigmoid方法比softmax准确度更高,下面选用sigmoid_cross_entropy交叉熵来计算置信度损失

3.2 计算置信度损失

conf_loss的计算公式:

计算置信度损失必须清楚conv_raw_confpred_conf是怎么来的。还记得上一篇文章提到的边界框预测decode函数吗?对,就是从那来的。

    def decode(self, conv_output, anchors, stride):"""return tensor of shape [batch_size, output_size, output_size, anchor_per_scale, 5 + num_classes]contains (x, y, w, h, score, probability)""" conv_shape       = tf.shape(conv_output)batch_size       = conv_shape[0]output_size      = conv_shape[1]anchor_per_scale = len(anchors) # 3# 将v3网络最终输出的255维特征向量的形状变为anchor_per_scale, x + y + w + h + score + num_classconv_output = tf.reshape(conv_output, (batch_size, output_size, output_size, anchor_per_scale, 5 + self.num_class))# v3网络输出的scoreconv_raw_conf = conv_output[:, :, :, :, 4:5]# 计算预测框里object的置信度pred_conf = tf.sigmoid(conv_raw_conf) 

conv_raw_conf 对应计算公式中的

,

pred_conf 对应公式中的

nice,开始动手构建conf_loss!

iou = bbox_iou(pred_xywh[:, :, :, :, np.newaxis, :], bboxes[:, np.newaxis, np.newaxis, np.newaxis, :, :])
# 找出与真实框 iou 值最大的预测框
max_iou = tf.expand_dims(tf.reduce_max(iou, axis=-1), axis=-1)
# 如果最大的 iou 小于阈值,那么认为该预测框不包含物体,则为背景框
respond_bgd = (1.0 - respond_bbox) * tf.cast( max_iou < IOU_LOSS_THRESH, tf.float32 )conf_focal = self.focal(respond_bbox, pred_conf)
# 计算置信度的损失(我们希望假如该网格中包含物体,那么网络输出的预测框置信度为 1,无物体时则为 0)
conf_loss = conf_focal * (respond_bbox * tf.nn.sigmoid_cross_entropy_with_logits(labels=respond_bbox, logits=conv_raw_conf)+respond_bgd * tf.nn.sigmoid_cross_entropy_with_logits(labels=respond_bbox, logits=conv_raw_conf))

4.分类损失

这里分类损失采用的是二分类的交叉熵,即把所有类别的分类问题归结为是否属于这个类别,这样就把多分类看做是二分类问题。

prob_loss = respond_bbox * tf.nn.sigmoid_cross_entropy_with_logits(labels=label_prob, logits=conv_raw_prob)

5. 最终Loss计算

将各部分损失值的和,除以均值,累加,作为最终的图片损失值。

        giou_loss = tf.reduce_mean(tf.reduce_sum(giou_loss, axis=[1,2,3,4]))conf_loss = tf.reduce_mean(tf.reduce_sum(conf_loss, axis=[1,2,3,4]))prob_loss = tf.reduce_mean(tf.reduce_sum(prob_loss, axis=[1,2,3,4]))return giou_loss, conf_loss, prob_loss


补充

tf.pow函数具体使用方法:

TensorFlow幂值计算函数:tf.pow_w3cschool​www.w3cschool.cn

tf.nn.sigmoid_cross_entropy_with_logits函数使用方法:

TensorFlow函数教程:tf.nn.sigmoid_cross_entropy_with_logits_w3cschool​www.w3cschool.cn

tf.expand_dims函数使用方法:

TensorFlow函数教程:tf.keras.backend.expand_dims_w3cschool​www.w3cschool.cn

Focal Loss详细讲解和公式推导过程可以参考这篇文章:

Focal Loss 论文理解及公式推导​www.aiuai.cn

softmax_cross_entropy和sigmoid_cross_entropy之间的区别可以参考这篇文章:

损失函数softmax_cross_entropy、binary_cross_entropy、sigmoid_cross_entropy之间的区别与联系​www.jianshu.com

谢谢观看,觉得好就点个赞呗!

layer output 激活函数_深入理解YOLO v3实现细节 - 第3篇 构建v3的Loss_layer相关推荐

  1. layer output 激活函数_一文彻底搞懂BP算法:原理推导+数据演示+项目实战(下篇)...

    在"一文彻底搞懂BP算法:原理推导+数据演示+项目实战(上篇)"中我们详细介绍了BP算法的原理和推导过程,并且用实际的数据进行了计算演练.在下篇中,我们将自己实现BP算法(不使用第 ...

  2. 探索 YOLO v3 实现细节 - 第6篇 预测 (完结)

    YOLO,即You Only Look Once的缩写,是一个基于卷积神经网络(CNN)的物体检测算法.而YOLO v3是YOLO的第3个版本,即YOLO.YOLO 9000.YOLO v3,检测效果 ...

  3. 探索 YOLO v3 实现细节 - 第4篇 数据和y_true

    YOLO,即You Only Look Once的缩写,是一个基于卷积神经网络(CNN)的物体检测算法.而YOLO v3是YOLO的第3个版本(即YOLO.YOLO 9000.YOLO v3),检测效 ...

  4. activity 生命周期_如何理解安卓activity的生命周期(on-create篇)?

    个人认为用类比的方式来学习新事物比较容易接受.我这里用蝴蝶的一生来做比喻. OnCreate阶段就像是蝴蝶的幼虫刚出卵里孵化出来,蝴蝶的一生只可能出生一次,oncreate只能被创建一次.蝴蝶刚出生的 ...

  5. C#_深入理解Unity容器

    C#_深入理解Unity容器 一.背景 **DIP是依赖倒置原则:**一种软件架构设计的原则(抽象概念).依赖于抽象不依赖于细节 **IOC即为控制反转(Inversion of Control):* ...

  6. 深入理解计算机系统 -资料整理 高清中文版_在所不辞的博客-CSDN博客_深入理解计算机系统第四版pdf

    深入理解计算机系统 -资料整理 高清中文版_在所不辞的博客-CSDN博客_深入理解计算机系统第四版pdf

  7. 异步通信还要设置波特率?_深入理解同步/异步通信

    异步通信还要设置波特率?_深入理解同步/异步通信   上一篇我们解释了串口通信中同步通信和异步通信的区别,详见上篇链接.其中我们分析同步/异步通信最重要的不同点就是是否同步时钟,可能就有很多小伙伴不理 ...

  8. 深度学习之对象检测_深度学习时代您应该阅读的12篇文章,以了解对象检测

    深度学习之对象检测 前言 (Foreword) As the second article in the "Papers You Should Read" series, we a ...

  9. 理解卷积神经网络的利器:9篇重要的深度学习论文(上)

    摘要: 为了更好地帮助你理解卷积神经网络,在这里,我总结了计算机视觉和卷积神经网络领域内许多新的重要进步及有关论文. 手把手教你理解卷积神经网络(一) 手把手教你理解卷积神经网络(二) 本文将介绍过去 ...

最新文章

  1. 【Python自学】万文字,学习框架+思维整理,入门就是这么简单
  2. word顶部有一道线_为什么顶角线不会过时?文章内容告诉你
  3. BZOJ3996 [TJOI2015]线性代数 【最小割】
  4. c语言中有bool型变量吗?
  5. VTK:PolyData之CellPointNeighbors
  6. 20220208--CTF MISC-- BUUCTF--二维码 1--binwalk-john-(工具的使用:分析文件/暴力破解压缩包密码)
  7. MySQL数据库的权限表
  8. C语言 va_arg 宏 - C语言零基础入门教程
  9. H3C的DHCP中继配置命令
  10. Linux 下五个顶级的开源命令行 Shell
  11. 剑指offer(C++)-JZ33:二叉搜索树的后序遍历序列(数据结构-树)
  12. smobiler中实现页面切换_.Net语言Smobiler开发之如何在手机上实现表单设计
  13. 06正交实验法及场景法
  14. c语言反三角函数正切,arc(完整的反三角函数值表)
  15. #467 – 使用UniformGrid 均分行和列(Use a UniformGrid for Evenly Spaced Rows and Columns)
  16. 输出的字体全部变成繁体字
  17. 明星代言的商品一般都不便宜,为何还那么受青睐?
  18. Linux操作手机端
  19. 【高通SDM660平台 Android 10.0】(21) --- 高通Camera persist使用手册
  20. POST、GET请求及对应的参数获取

热门文章

  1. .NET中的Command(命令)模式
  2. jquery ui 自動完成對中文字搜尋Bug(firefox)
  3. 在内网服务器中获得真正的客户端ip的方法
  4. 服务器常用的端口及其用途
  5. gitlab远程提交
  6. 【Django】安装及配置
  7. Weblogic java.lang.OutOfMemoryError: PermGen space
  8. java 观察者模式
  9. C++ 20 是近十年来影响最大的一个版本,新的特性众多
  10. 谷歌量子霸权论文;13项NLP任务夺冠的小模型ALBERT