SSD损失分为两部分,类别损失和回归框位置损失
其中,类别损失采用softmax损失,回顾框损失采用l1——smooth损失。
1. 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
SSD类别部分的网络输出维度为(batch_size, 8732, num_classes),并经过softmax激活函数,转化为概率。
softmax_loss = -tf.reduce_sum(y_true * tf.math.log(y_pred), axis=-1)
概率取-log,与对应真实值位置乘积求和, 维度为(batch_size, 8732)
2. l1_soomth 损失
def _l1_smooth_loss(self, y_true, y_pred): abs_loss = tf.abs(y_true - y_pred) sq_loss = 0.5 * (y_true - y_pred)**2 l1_loss = tf.where(tf.less(abs_loss, 1.0), sq_loss, abs_loss - 0.5) return tf.reduce_sum(l1_loss, -1)
SSD回归框偏差部份输出维度为(batch_size, 8732, 4),分别求l1_loss 和 l2_loss,将二者结合,
l1_loss = tf.where(tf.less(abs_loss, 1.0), sq_loss, abs_loss - 0.5)
return tf.reduce_sum(l1_loss, -1)
维度为(batch_size, 8732)。
3. 计算类别和回归框总损失
3.1 正样本:
正样本的损失分为类别损失和回归损失两种:
类别损失:(batch_size, )
pos_conf_loss = tf.reduce_sum(conf_loss * y_true[:, :, -8], axis=1)
回归损失:(batch_size, )
pos_loc_loss = tf.reduce_sum(loc_loss * y_true[:, :, -8],axis=1)
3.2 负样本
负样本的损失只有类别损失:
首先如何确定负样本,如果选取所有的负样本,则会引起类别不平衡的问题,所以选取部分负样本(正样本数量的3倍)
num_neg = tf.minimum(3 * num_pos, 8732 - num_pos)
# 求平均每个图片要取多少个负样本
num_neg_batch = tf.reduce_mean(tf.boolean_mask(num_neg,
tf.greater(num_neg, 0)))
num_neg_batch = tf.cast(num_neg_batch,tf.int32)
如果是选取部分负样本,选取哪些呢?选取不该有预测结果的框,求他们的最大置信度
max_confs = tf.reduce_max(y_pred[:, :, confs_start:confs_end], axis=2) # 5:25 (不包括背景的20个类别)
# 取top_k个置信度,作为负样本
_, indices = tf.nn.top_k(max_confs * (1 - y_true[:, :, -8]),
k=num_neg_batch)
抽取相应位置的conf_loss
neg_conf_loss = tf.gather(tf.reshape(conf_loss, [-1]),
full_indices)
neg_conf_loss = tf.reshape(neg_conf_loss,
[batch_size, num_neg_batch])
neg_conf_loss = tf.reduce_sum(neg_conf_loss, axis=1)
3.3 总损失
total_loss = tf.reduce_sum(pos_conf_loss) + tf.reduce_sum(neg_conf_loss)
total_loss /= tf.reduce_sum(num_pos)
total_loss += tf.reduce_sum(self.alpha * pos_loc_loss) / tf.reduce_sum(num_pos)