You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RPN损失函数异常求助:低损失但预测框偏差极大

RPN损失值极低但预测结果严重偏离的问题

我的Region Proposal Network(RPN)损失函数的损失值很低,模型评估结果如下:

2/2 [==============================] - 1s 30ms/step - loss: 4.3797e-04

然而其预测结果却严重偏离,可视化置信度Top10的检测框时,所有测试图像的检测框完全偏离目标。

我怀疑问题出在RPN损失函数中,以下是我的损失函数代码:

class RPN_Loss(tf.keras.losses.Loss):
  def call(self, y_true, y_pred):
    # Calculate binary cross entropy
    bce = tf.keras.losses.BinaryCrossentropy()

    # Split true and pred tensors into objectness and transform tensors
    transform_true,objectness_true = tf.split(y_true, num_or_size_splits=[4,1],axis=-1)
    transform_pred,objectness_pred = tf.split(y_pred, num_or_size_splits=[4,1],axis=-1)

    # Calculate transform loss
    transform_loss = tf.where(tf.abs(tf.square(transform_pred - transform_true)) < 1, 0.5 * tf.square(transform_pred - transform_true), tf.abs(tf.square(transform_pred - transform_true)) - 0.5)
    transform_loss = tf.where(transform_true != [0,0,0,0], transform_loss * 1/256, 0)

    # Calculate classification loss
    cls_loss = bce(objectness_true, objectness_pred)
    cls_loss = tf.where(objectness_true == 1, (cls_loss) * 1/256, 0) + tf.where(objectness_true == 0, cls_loss * 1/256, 0)
    cls_loss = tf.abs(cls_loss)
    return tf.reduce_sum(tf.reduce_mean(transform_loss) + tf.reduce_mean(cls_loss))

我尤其认为最后一行存在错误,应该使用reduce_sum而非reduce_mean。但替换为reduce_sum后,损失值会变得极高(如537、nan或无穷大)。另外,我对分类损失取了绝对值,不确定是否正确。未取绝对值时损失会逐渐发散至负无穷,取绝对值后该问题得到解决。

以下是我的RPN网络结构代码供参考:

def get_rpn(feature_map_width,feature_map_height,k):
    num_anchors = feature_map_width*feature_map_height*k
    inputs = tf.keras.Input(shape=(feature_map_width, feature_map_height, 3), name="img")
    conv2d = tf.keras.layers.Conv2D(filters=512, kernel_size=3, padding="same", activation="relu",name='conv2d')(inputs)
    objectness_unshaped = tf.keras.layers.Conv2D(filters=k, kernel_size=1, padding="same", name='objectness_unshaped',activation='sigmoid')(conv2d)
     objectness = tf.keras.layers.Reshape((num_anchors,1),name='objectness')(objectness_unshaped)

    transform_unshaped = tf.keras.layers.Conv2D(filters=k*4, kernel_size=1, padding="same", name='transform_unshaped',activation="linear")(conv2d)
    transform = tf.keras.layers.Reshape((num_anchors,4),name='transform')(transform_unshaped)

    combined_output = tf.keras.layers.Concatenate(axis=-1,name='concatenate')([transform,objectness])
    combined_output = tf.keras.layers.Reshape((num_anchors,5),name='combined_output')(combined_output)
    model = tf.keras.Model(inputs=inputs, outputs=combined_output, name="region_proposal_network")
    tf.keras.utils.plot_model(model, "model.png", show_shapes=True)
    model.compile(optimizer=tf.keras.optimizers.experimental.SGD(learning_rate=LEARNING_RATE), loss=RPN_Loss())
    return model

顺便说明,我使用的是学习率为0.025的SGD优化器。作为TensorFlow、计算机视觉与机器学习新手,恳请各位提供帮助:我的损失函数代码是否存在错误?


问题分析与修复建议

1. 分类损失核心错误

你直接用批次平均的交叉熵标量做逐元素掩码操作,逻辑完全错误。tf.keras.losses.BinaryCrossentropy()默认返回的是整个批次的平均损失,不能和逐元素的tf.where结合。此外,对分类损失取绝对值完全没必要——损失发散到负无穷是因为错误的损失计算逻辑导致梯度混乱,而非交叉熵本身会产生负值。

2. 回归损失问题

  • 平滑L1损失实现错误:正确逻辑是对预测与真实值的差值绝对值做判断,而非差值平方的绝对值,应该写成tf.abs(transform_pred - transform_true) < 1。
  • 掩码逻辑错误:transform_true != [0,0,0,0]会对每个元素单独判断,导致部分元素被掩码、部分不被,应该用tf.reduce_all(transform_true == 0, axis=-1, keepdims=True)判断整个回归目标是否全为0。
  • 1/256的缩放过于激进,直接把损失压到极低,模型失去优化动力。

3. 损失聚合逻辑混乱

最后一行tf.reduce_sum(tf.reduce_mean(...) + tf.reduce_mean(...))完全多余:两个mean已经是标量,相加后再sum没有意义。正确做法是对有效损失(掩码后)计算平均,再给分类、回归损失分配合理权重(比如分类权重1,回归权重10,这是RPN的经典设置)。

4. 其他潜在问题

  • 学习率0.025过高,SGD下容易导致震荡或NaN,建议先降到0.001调试。
  • 未提及anchor设置,如果anchor的尺寸、比例与数据集不匹配,也会导致预测框完全偏离目标。

修正后的损失函数代码

class RPN_Loss(tf.keras.losses.Loss):
    def __init__(self, cls_weight=1.0, reg_weight=10.0):
        super().__init__()
        self.cls_weight = cls_weight
        self.reg_weight = reg_weight
        # 使用逐样本交叉熵,from_logits=False对应sigmoid激活
        self.bce = tf.keras.losses.BinaryCrossentropy(from_logits=False, reduction=tf.keras.losses.Reduction.NONE)

    def call(self, y_true, y_pred):
        # 拆分真实值与预测值
        transform_true, objectness_true = tf.split(y_true, num_or_size_splits=[4, 1], axis=-1)
        transform_pred, objectness_pred = tf.split(y_pred, num_or_size_splits=[4, 1], axis=-1)

        # 1. 计算平滑L1回归损失
        diff = transform_pred - transform_true
        abs_diff = tf.abs(diff)
        smooth_l1 = tf.where(abs_diff < 1.0, 0.5 * tf.square(abs_diff), abs_diff - 0.5)
        # 仅计算有目标的anchor的回归损失
        reg_mask = tf.reduce_all(transform_true != 0, axis=-1, keepdims=True)
        reg_loss = tf.reduce_mean(tf.boolean_mask(smooth_l1, reg_mask)) if tf.reduce_any(reg_mask) else 0.0

        # 2. 计算二分类交叉熵损失
        cls_loss = self.bce(objectness_true, objectness_pred)
        # 仅计算标注为0或1的anchor损失(忽略-1的难例标注)
        cls_mask = tf.logical_or(objectness_true == 0, objectness_true == 1)
        cls_loss = tf.reduce_mean(tf.boolean_mask(cls_loss, cls_mask)) if tf.reduce_any(cls_mask) else 0.0

        # 3. 总损失
        return self.cls_weight * cls_loss + self.reg_weight * reg_loss

额外建议

  • 检查数据集标注:RPN的回归目标应该是anchor到真实框的dx、dy、dw、dh偏移量,而非直接的框坐标,需确认是否做了正确的坐标转换。
  • 增加难例挖掘:RPN通常控制正负样本比例为1:3,避免负样本过多主导损失。

内容的提问来源于stack exchange,提问作者KoalaKomputer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 22:17:01