天天看点

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

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

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

这行代码是进行损失计算函数的调用。

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

损失函数被包装成一个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损失函数呢???

Tensorflow2.0---SSD网络原理及代码解析(五)- 损失函数的计算Tensorflow2.0—SSD网络原理及代码解析(五)- 损失函数的计算
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
           

继续阅读