热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Tensorflow2.0YOLOV4tiny网络原理及代码解析(三)损失函数的构建

Tensorflow2.0—YOLOV4-tiny网络原理及代码解析(三)-损失函数的构建YOLOV4中的损失函数与V3还是有比较大的区别的ÿ
Tensorflow2.0—YOLO V4-tiny网络原理及代码解析(三)- 损失函数的构建

YOLO V4中的损失函数与V3还是有比较大的区别的,具体的可以看YOLOV4与YOLOV3的区别。
代码是在nets文件夹下面的loss.py文件中,在train.py中引用的是:

model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5, 'label_smoothing': label_smoothing})(loss_input)

先来看下yolo_loss的参数:

def yolo_loss(args, #是一个列表,其中包含了预测结果和进行编码之后的真实框的结果,# [, 预测结果1# , 预测结果2# , 真实框1# ] 真实框2anchors, #[[ 10. 14.]# [ 23. 27.]# [ 37. 58.]# [ 81. 82.]# [135. 169.]# [344. 319.]]num_classes, #['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'],共20个分类ignore_thresh=.5, #阈值label_smoothing=0.1, #标签平滑print_loss=False, #是否打印损失normalize=True): #是否做归一化

把预测的与真实的进行分割出来,
y_true是一个列表,包含两个特征层,shape分别为(m,13,13,75),(m,26,26,75)
yolo_outputs是一个列表,包含两个特征层,shape分别为(m,13,13,3,25),(m,26,26,3,25)

y_true = args[num_layers:]
yolo_outputs = args[:num_layers]

然后就开始以特征层数开始循环,这里就以(m,13,13,3,25)为例:
先获得真实框(m,13,13,3,25)的第5个位置的数据,如果在编码中该位置存在gt框,那么就设置为1,表示第(i,j)特征图中第k个锚点框包含物体,否则一切都设置为0

object_mask = y_true[l][..., 4:5]

然后,再获得真实框(m,13,13,3,25)的第6-26个位置的数据:

true_class_probs = y_true[l][..., 5:]

接着,进行标签平滑:

if label_smoothing:true_class_probs = _smooth_labels(true_class_probs, label_smoothing)

def _smooth_labels(y_true, label_smoothing):num_classes = tf.cast(K.shape(y_true)[-1], dtype=K.floatx())label_smoothing = K.constant(label_smoothing, dtype=K.floatx())return y_true * (1.0 - label_smoothing) + label_smoothing / num_classes


接着,就是yolo_head:

yolo_head(yolo_outputs[l], #yolo模型预测的结果,shape=(None,13,13,75)anchors[anchor_mask[l]], #预测结果所对应的anchor,这里为#[[ 81. ,82.],# [135. ,169.],# [344. ,319.]]num_classes, #voc数据集的class数量,为20input_shape, #(416,416)calc_loss=True) #是否计算损失函数,在模型预测阶段,该参数为False

下面这一段代码有点难理解,其实它的作用就是创建(13,13,1,2)的网格

grid_shape = K.shape(feats)[1:3] # height, width,为(13,13)
grid_y = K.tile(K.reshape(K.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]),[1, grid_shape[1], 1, 1])
grid_x = K.tile(K.reshape(K.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]),[grid_shape[0], 1, 1, 1])
grid = K.concatenate([grid_x, grid_y])
grid = K.cast(grid, K.dtype(feats))

举个例子:
在这里插入图片描述
在这幅图片中就可以很好的看到最终其实就是生成了(13,13,1,2)的网格。

feats = K.reshape(feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5])

将预测结果(m,13,13,75)分割成(m,13,13,3,25)。

box_xy = (K.sigmoid(feats[..., :2]) + grid) / K.cast(grid_shape[...,::-1], K.dtype(feats))
box_wh = K.exp(feats[..., 2:4]) * anchors_tensor / K.cast(input_shape[...,::-1], K.dtype(feats))
box_confidence = K.sigmoid(feats[..., 4:5])
box_class_probs = K.sigmoid(feats[..., 5:])

这四行代码的目的是:将预测值转换为真实值!
第一行:将预测结果的xy(None,13,13,3,2)先加上grid,然后除以(13,13),就得到了转化后的进行归一化的xy,shape=(None,13,13,3,2)。
第二行:将预测结果的wh(None,13,13,3,2)先乘anchor的尺寸,然后除以输入大小,最后再进行指数计算,得到转换后的wh,shape同xy。
第三行和第四行:将预测得到的confidence和class_prob进行sigmoid转化。

最后,就可以将预测结果进行decode为预测真实框的形式~

if calc_loss == True:return grid, feats, box_xy, box_whreturn box_xy, box_wh, box_confidence, box_class_probs

在计算loss的时候返回grid, feats, box_xy, box_wh
在预测的时候返回box_xy, box_wh, box_confidence, box_class_probs

pred_box = K.concatenate([pred_xy, pred_wh])

将上述box_xy, box_wh进行合并,shape=(None,13,13,3,4)


下面,还有一个loop_body函数,来看看它是干嘛的。

#-----------------------------------------------------------## 对每一张图片计算ignore_mask#-----------------------------------------------------------#def loop_body(b, ignore_mask):#-----------------------------------------------------------## 取出n个真实框&#xff1a;n,4#-----------------------------------------------------------#true_box &#61; tf.boolean_mask(y_true[l][b,...,0:4], object_mask_bool[b,...,0])#-----------------------------------------------------------## 计算预测框与真实框的iou# pred_box 13,13,3,4 预测框的坐标# true_box n,4 真实框的坐标# iou 13,13,3,n 预测框和真实框的iou#-----------------------------------------------------------#iou &#61; box_iou(pred_box[b], true_box)#-----------------------------------------------------------## best_iou 13,13,3 每个特征点与真实框的最大重合程度#-----------------------------------------------------------#best_iou &#61; K.max(iou, axis&#61;-1)#-----------------------------------------------------------## 判断预测框和真实框的最大iou小于ignore_thresh# 则认为该预测框没有与之对应的真实框# 该操作的目的是&#xff1a;# 忽略预测结果与真实框非常对应特征点&#xff0c;因为这些框已经比较准了# 不适合当作负样本&#xff0c;所以忽略掉。#-----------------------------------------------------------#ignore_mask &#61; ignore_mask.write(b, K.cast(best_iou<ignore_thresh, K.dtype(true_box)))return b&#43;1, ignore_mask#-----------------------------------------------------------## 在这个地方进行一个循环、循环是对每一张图片进行的#-----------------------------------------------------------#_, ignore_mask &#61; tf.while_loop(lambda b,*args: b<m, loop_body, [0, ignore_mask])

步骤解释&#xff1a;
1.先取出真实框存在物体的框的xywh
2.与预测框进行iou计算
3.找到对应每个真实框最大的iou的预测框&#xff0c;best_iou &#61; &#xff08;13&#xff0c;13&#xff0c;3&#xff09;
4.判断预测框和真实框的最大iou小于ignore_thresh&#xff0c;则认为该预测框没有与之对应的真实框&#xff0c;这么做的目的是&#xff1a;忽略预测结果与真实框非常对应特征点&#xff0c;因为这些框已经比较准了&#xff0c;不适合当作负样本&#xff0c;所以忽略掉
5.把这些框放入ignore_mask中

#-----------------------------------------------------------## 真实框越大&#xff0c;比重越小&#xff0c;小框的比重更大。#-----------------------------------------------------------#box_loss_scale &#61; 2 - y_true[l][...,2:3]*y_true[l][...,3:4]#-----------------------------------------------------------## 计算Ciou loss#-----------------------------------------------------------#raw_true_box &#61; y_true[l][...,0:4]ciou &#61; box_ciou(pred_box, raw_true_box)ciou_loss &#61; object_mask * box_loss_scale * (1 - ciou)

这一部分是进行预测与真实的ciou损失&#xff01;但是代码实现中与论文中还是有一点区别的&#xff0c;在代码中还考虑到了真实框大小的因素&#xff1a;真实框越大&#xff0c;比重越小&#xff0c;小框的比重更大。&#xff08;这里我就不写ciou的代码了&#xff0c;有机会单独写一个blog~&#xff09;&#xff0c;得到的ciou_loss &#61; &#xff08;None,13,13,3,1&#xff09;。

最后&#xff0c;就是置信度损失和类别损失的计算了&#xff1a;

confidence_loss &#61; object_mask * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits&#61;True)&#43; \(1-object_mask) * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits&#61;True) * ignore_maskclass_loss &#61; object_mask * K.binary_crossentropy(true_class_probs, raw_pred[...,5:], from_logits&#61;True)

第一行&#xff1a;先计算真实框存在的confidence_loss&#xff0c;加上不存在真实框的confidence_loss&#xff08;这里要忽略那些ignore_mask里面的框&#xff09;。
第二行&#xff1a;直接计算预测和真实的类别损失。

num_pos &#43;&#61; tf.maximum(K.sum(K.cast(object_mask, tf.float32)), 1)loss &#43;&#61; location_loss &#43; confidence_loss &#43; class_lossloss &#61; K.expand_dims(loss, axis&#61;-1)if normalize:loss &#61; loss / num_poselse:loss &#61; loss / mfreturn loss

最后就是进行损失求和&#xff0c;若进行归一化&#xff0c;就将总损失除以正样本&#xff0c;要是不进行归一化&#xff0c;那就除以批次大小。最终得到最后的loss~


推荐阅读
author-avatar
zackcoolgirl_497
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有