Tensorflow2.0---SSD网络原理及代码解析(五)- 损失函数的计算

40 篇文章 3 订阅
7 篇文章 2 订阅

Tensorflow2.0—SSD网络原理及代码解析(五)- 损失函数的计算

前面写了三篇关于SSD代码的讲解,还有最后一个关键代码—损失函数的计算,废话不多说,直接上干货~

model.compile(optimizer=Adam(lr=learning_rate_base),loss=MultiboxLoss(NUM_CLASSES, neg_pos_ratio=3.0).compute_loss)

这行代码是进行损失计算函数的调用。
在这里插入图片描述
损失函数被包装成一个MultiboxLoss类,最后一个compute_loss方法用于调用计算。
首先,先搞清楚y_true, y_pred分别是个啥shape的,其实二者的shape都是(2,8732,33)。2表示的是batch_size,8732表示的是每张图片的锚点框,33表示每个训练图片进行encode之后的结果。


#   分类的loss
        #   batch_size,8732,21 -> batch_size,8732
        # --------------------------------------------- #
        conf_loss = self._softmax_loss(y_true[:, :, 4:-8],
                                       y_pred[:, :, 4:-8])

先计算所有真实框(其实就是锚点框基于真实框进行encode之后)与预测框的分类的loss。这里用的是softmax。

    def _softmax_loss(self, y_true, y_pred):
        y_pred = tf.maximum(y_pred, 1e-7)
        softmax_loss = -tf.reduce_sum(y_true * tf.math.log(y_pred),
                                      axis=-1)
        return softmax_loss

输入的shape为(2,8732,21),输出的shape为(2,8732)。

然后,计算所有真实框(其实就是锚点框基于真实框进行encode之后)与预测框的坐标的loss,这里用的是l1平滑损失函数。
什么是L_1损失函数呢???
在这里插入图片描述

https://blog.csdn.net/weixin_41940752/article/details/93159710

代码实现:

    def _l1_smooth_loss(self, y_true, y_pred):
        abs_loss = tf.abs(y_true - y_pred)  # y_1 = |y_t - y_p|
        sq_loss = 0.5 * (y_true - y_pred)**2  #y_2 = 0.5 * (y_t - y_p)^2
        l1_loss = tf.where(tf.less(abs_loss, 1.0), sq_loss, abs_loss - 0.5)
        return tf.reduce_sum(l1_loss, -1)

输入的shape为(2,8732,4),输出的shape为(2,8732)。


接下来,计算所有正样本的先验框的loss:

#   获取所有的正标签的loss
        # --------------------------------------------- #
        pos_loc_loss = tf.reduce_sum(loc_loss * y_true[:, :, -8],
                                     axis=1)
        pos_conf_loss = tf.reduce_sum(conf_loss * y_true[:, :, -8],
                                      axis=1)

loc_loss和y_true[:, :, -8]的shape为(2,8732),按照最后一维进行先相乘,然后相加,最后得到了shape为(2,)的pos_loc_loss 和pos_conf_loss 。


现在,损失就是一个:负样本的conf的loss。

# --------------------------------------------- #
        #   每一张图的正样本的个数
        #   batch_size,
        # --------------------------------------------- #
        num_pos = tf.reduce_sum(y_true[:, :, -8], axis=-1)  #计算每个批次中每个图正样本的个数

        # --------------------------------------------- #
        #   每一张图的负样本的个数
        #   batch_size,
        # --------------------------------------------- #
        num_neg = tf.minimum(self.neg_pos_ratio * num_pos,
                             num_boxes - num_pos)
        # 找到了哪些值是大于0的
        pos_num_neg_mask = tf.greater(num_neg, 0)
        # --------------------------------------------- #
        #   如果有些图,它的正样本数量为0,
        #   默认负样本为100
        # --------------------------------------------- #
        has_min = tf.cast(tf.reduce_any(pos_num_neg_mask),tf.float32)
        num_neg = tf.concat(axis=0, values=[num_neg, [(1 - has_min) * self.negatives_for_hard]])

以上这么一大堆代码,其实任务很简单,就是在找负样本,如果有正样本,那么就取3倍的负样本。如果没有正样本,那么就筛选出100个负样本。

        # --------------------------------------------- #
        num_neg_batch = tf.reduce_sum(tf.boolean_mask(num_neg, tf.greater(num_neg, 0)))
        num_neg_batch = tf.cast(num_neg_batch,tf.int32)   #一个批次中所有的负样本的个数

        # --------------------------------------------- #
        #   对预测结果进行判断,如果该先验框没有包含物体
        #   那么它的不属于背景的预测概率过大的话
        #   就是难分类样本
        # --------------------------------------------- #
        confs_start = 4 + self.background_label_id + 1
        confs_end = confs_start + self.num_classes - 1

        # --------------------------------------------- #
        #   batch_size,8732
        #   把不是背景的概率求和,求和后的概率越大
        #   代表越难分类。
        # --------------------------------------------- #
        max_confs = tf.reduce_sum(y_pred[:, :, confs_start:confs_end], axis=2)

        # --------------------------------------------------- #
        #   只有没有包含物体的先验框才得到保留
        #   我们在整个batch里面选取最难分类的num_neg_batch个
        #   先验框作为负样本。
        # --------------------------------------------------- #
        max_confs = tf.reshape(max_confs * (1 - y_true[:, :, -8]), [-1])
        _, indices = tf.nn.top_k(max_confs, k=num_neg_batch)

        neg_conf_loss = tf.gather(tf.reshape(conf_loss, [-1]), indices)

这一步比较难理解,我个人理解为:先找到每个批次中所有负样本的数量,然后计算所有预测框的不是背景的概率进行求和,求和后的概率越大,代表越难分类。(我认为可以这么理解:除了背景的概率,其他概率相加越大,说明这个预测框就是属于那种越难分类的,比如说一只狗,预测出它为猫为0.3,狗为0.4,老虎为0.35,像这种的就很难区别出到底是哪个动物。)然后,我们在整个batch里面选取最难分类的num_neg_batch个先验框作为负样本。


最后,将三个损失进行相加,并归一化。

# 进行归一化
        num_pos     = tf.where(tf.not_equal(num_pos, 0), num_pos, tf.ones_like(num_pos))
        total_loss  = tf.reduce_sum(pos_conf_loss) + tf.reduce_sum(neg_conf_loss) + tf.reduce_sum(self.alpha * pos_loc_loss)
        total_loss /= tf.reduce_sum(num_pos)
        return total_loss
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进我的收藏吃灰吧~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值