代码文件
from keras import backend as K
def xy_loss(object_mask, raw_pred, box_loss_scale, raw_true_xy, mf):
"""
object_mask: 真实置信度
raw_true_xy: 真实xy
raw_pred: 预测结果,包含坐标以及置信度和分类结果([x, y, w, h, c, a1, a2, a3...])
box_loss_scale: box比例
"""
#********* Begin *********#
xy_loss = object_mask*box_loss_scale*K.binary_crossentropy(raw_true_xy,raw_pred[...,0:2],from_logits=True)
#********* End *********#
return K.sum(xy_loss) / mf
def wh_loss(object_mask, raw_pred, box_loss_scale, raw_true_wh, mf):
"""
object_mask: 真实置信度
raw_true_xy: 真实wh
raw_pred: 预测结果,包含坐标以及置信度和分类结果([x, y, w, h, c, a1, a2, a3...])
box_loss_scale: box比例
"""
#********* Begin *********#
wh_loss = object_mask*box_loss_scale*0.5*K.square(raw_true_wh - raw_pred[...,2:4])
#********* End *********#
return K.sum(wh_loss) / mf
def confidence_loss(object_mask, raw_pred, ignore_mask, mf):
confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) + \
(1 - object_mask) * K.binary_crossentropy(object_mask, raw_pred[..., 4:5],
from_logits=True) * ignore_mask
return K.sum(confidence_loss) / mf
def class_loss(object_mask, raw_pred, true_class_probs, mf):
class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[..., 5:], from_logits=True)
return K.sum(class_loss) / mf
def loss_sum(loss, raw_pred, y_true, raw_true_xy, raw_true_wh, l, ignore_mask, mf):
# 真实物体置信度
object_mask = y_true[l][..., 4:5]
# 真实物体分类
true_class_probs = y_true[l][..., 5:]
box_loss_scale = 2 - y_true[l][..., 2:3] * y_true[l][..., 3:4] # 2-w*h
xy_l = xy_loss(object_mask, raw_pred, box_loss_scale, raw_true_xy, mf)
wh_l = wh_loss(object_mask, raw_pred, box_loss_scale, raw_true_wh, mf)
confidence_l = confidence_loss(object_mask, raw_pred, ignore_mask, mf)
class_l = class_loss(object_mask, raw_pred, true_class_probs, mf)
loss += xy_l + wh_l + confidence_l + class_l
return loss
题目描述
任务描述
本关任务:了解YOLO算法的损失函数概念,使用代码实现。
相关知识
YOLO v1损失函数
YOLO V1损失函数组成如下所示:
λcoord i=0∑S2j=0∑B1ijobj [(xi−x^i)2+(yi−y^i)2]
+λcoord i=0∑S2j=0∑B1ijobj [(wi−w^i)2+(hi−h^i)2]
+i=0∑S2j=0∑B1ijobj (Ci−C^i)2
+λnoobj i=0∑S2j=0∑B1ijnoobj (Ci−C^i)2
+i=0∑S21iobj c∈ classes ∑(pi(c)−p^i(c))2
1iobj 代表cell
中含有真实物体的中心, pr(object)=1。
损失函数分为三个部分:
-
① 坐标误差 为什么宽和高要带根号? 对不同大小的
bbox
预测中,相比于大bbox
预测偏一点,小box
预测偏一点更不能忍受。作者用了一个比较取巧的办法,就是将box
的width
和height
取平方根代替原本的height
和width
。(主要为了平衡小目标检测预测的偏移) -
② IoU误差(很多人不知道C^i代表什么)
其实这里的C^i分别表示1和0,
C^i=Pr( Object )∗1OUpred truth
-
③分类误差 这里就是激活函数的输出。
YOLO v2损失函数
YOLO V2损失函数组成如下所示:
losst=i=0∑Wj=0∑Hk=0∑A1MaxIOU< Thresh λnoobj ∗(−bijko)2+1t<12800λprior ∗r∈(x,y,w,h)∑( prior kr−bijkr)2+1ktruth ⎝⎜⎛λcoord ∗r∈(x,y,w,h)∑( truth r−bijkr)2+λobj∗(IOUtruth k−bijko)2∗λclass ∗(c=1∑c( truth c−bijkc)2))
首先W,H分别指的是特征图(3×13的宽与高,而A指的是先验框数(这里是5),各个λ各个loss
部分的权重系数,除了预测有对象的损失函数系数设置为5,其他都为1。
有无对象损失函数计算方法
1MaxIOU< Thresh λnoobj ∗(−bijko)2+λobj ∗(IOUtruth k−bijko)2
其中 λobj=5,λnobj=1∘1MaxIOU<Thresh表示最大的IOU都小于0.6时,取1。boijk表示0-confidence
YOLOv2中,总共有845个anchor_boxes
。在未与true_boxes匹配的anchor_boxes
中,与true_boxes的max IOU
小于0.6预测无对象,需要计算no_objects_loss
其损失。而objects_loss则是与true_boxes
匹配的anchor_boxes
的预测误差。与YOLOv1不同的是修正系数的改变,YOLO v1中no_objects_loss
和objects_loss
分别是0.5和1,而YOLO v2中则是1和5。其中 objects_loss = (object_scale * detectors_mask * K.square(best_ious - pred_confidence))
和公式表述相类似。
类别损失函数计算方法
+λclass ∗(c=1∑c( truth c−bijkc)2))
均方误差:(1-卷积层预测类别概率)的平方。
坐标损失函数计算方法
+1ktruth ⎝⎜⎛λcoord ∗r∈(x,y,w,h)∑( truth r−bijkr)2
和YOLO v1的改动较大:
-
(1)计算x,y的误差由相对于整个图像(416x416)的
offset
坐标误差的均方改变为相对于gird cell
的offset
坐标误差的均方。并且将YOLOv1
w,h取根号处理改为对YOLO v2
中长宽放缩因子取log
。 -
(2)并将修正系数由5改为了1 。
YOLO v3损失函数
YOLO V3损失函数组成如下所示:
loss ( object )=λcoord i=0∑K×Kj=0∑MIijobj (2−wi×hi)[(xi−x^i)2+(yi−y^i)2]+λcoord i=0∑K×Kj=0∑MIijobj (2−wi×hi)[(wi−w^i)2+(hi−h^)2]−i=0∑K×Kj=0∑MIijobj [C^ilog(Ci)+(1−C^i)log(1−Ci)]−λnoobj i=0∑K×Kj=0∑MIijnoobj [C^ilog(Ci)+(1−C^i)log(1−Ci)]−i=0∑K×KIijobj c∈ classes ∑[p^i(c)log(pi(c))+(1−p^i(c))log(1−pi(c))]
图片输入到神经网络后会被分成S*S
个网格,每个网格产生B个候选框,每个候选框会经过网络最终得到相应的bounding box
。最终得到S*S*B个bounding box
。那么就需要利用损失函数确定具体的bounding box
计算误差更新权重。
中心点坐标误差
λcoord i=0∑K×Kj=0∑MIijobj(2−wi×hi)[(xi−x^i)2+(yi−y^i)2]
Iijobj 是真实置信度,(2−wi×hi)是box比例,
[(xi−x^i)2+(yi−y^i)2]
是求中心点的二值交叉熵。
def xy_loss(true_p,true_xy, pred_xy,box_scale):
"""
true_p: 真实置信度
true_xy: 真实xy
raw_pred_xy: 预测xy
box_scale: box比例
"""
xy_loss = true_p*box_scale*K.binary_crossentropy(true_xy, pred_xy,from_logits=True)
网络输出是 tx 和 ty, 然后通过 σ(tx) 和 σ(ty), 再乘以步长,就映射到了 416 * 416
大小的图上的目标,所以在计算误差的时候,其实也是用的这一项 σ(tx)∗ stride
和 σ(ty)∗stride
和真实目标经过resize
到 416 * 416
上的目标的大小,去计算误差。 整个这一项表示的是:当第 i 个网格的第 j 个anchor box
负责某一个真实目标时,那么这个anchor box
所产生的bounding box
就应该去 和真实目标的box
去比较,计算得到中心坐标误差。
宽高坐标误差
λcoord i=0∑K×Kj=0∑MIijobj(2−wi×hi)[(wi−w^i)2+(hi−h^)2]
Iijobj 是真实置信度,(2−wi×hi)是box比例,
[(wi−w^i)2+(hi−h^)2]
是求宽高的均方误差。
def xy_loss(true_p,true_wh, pred_wh,box_scale):
"""
true_p: 真实置信度
true_xy: 真实wh
raw_pred_xy: 预测wh
box_scale: box比例
"""
xy_loss = true_p*box_scale*0.5*K.square(true_wh - pred_wh)
实际上,网络输出的应当是 (tw) 和 (th), 所以在计算误差的时候,其实也是用的这一项 (tw)∗ stride和 (th)∗ stride
和真实目标经过resize
之后的值, 去计算误差的。所以可以认为公式里的 wi 就是 (tw)∗ stride
等等。 整个这一项表示的是:当第 i 个网格的第j个anchor box
负责某一个真实目标时,那么这个anchor box
所产生的bounding box
就应该去 和真实目标的box去比较,计算得到宽高的误差。
置信度误差
i=0∑K×Kj=0∑MIijobj[C^ilog(Ci)+(1−C^i)log(1−Ci)]−λnoobj i=0∑K×Kj=0∑MIijnoobj[C^ilog(Ci)+(1−C^i)log(1−Ci)]
置信度误差使用交叉熵来表示。另外需要清楚不管anchor box
是否负责某个目标,都会计算置信度误差。因为置信度表示:框出的box内确实有物体的自信程度和框出的box将整个物体的所有特征都包括进来的自信程度。 损失函数分为两部分:有物体,没有物体,其中没有物体损失部分还增加了权重系数。
- 第一项是:存在对象的
bounding box
的置信度误差。带有Iijobj意味着只有"负责"(IOU比较大)预测的那个bounding box
的置信度才会计入误差。 - 第二项是:不存在对象的
bounding box
的置信度误差。因为不存在对象的bounding box
应该老老实实的说"我这里没有对象",也就是输出尽量低的置信度。如果它不恰当的输出较高的置信度,会与真正"负责"该对象预测的那个bounding box
产生混淆。其实就像对象分类一样,正确的对象概率最好是1,所有其它对象的概率最好是0。
分类误差
i=0∑S2Iijobjc∈ classes ∑([P^ijlog(Pij)+(1−P^ij)log(1−Pij)]
分类误差也是选择了交叉嫡作为损失函数。当第 i 个网格的第 j 个anchor box
负责某一个真实目标时,那么这个anchor box
所产生的bounding box
才会去计算分类损失函数。
编程要求
根据上述内容提示,在右侧编辑器补充损失函数部分代码。
测试说明
开始你的任务吧,祝你成功!