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

TensorFlow 2.13下Mask R-CNN自定义损失函数引发KerasTensor TypeError问题求助

TensorFlow 2.13下Mask R-CNN自定义损失函数引发KerasTensor TypeError问题求助

大家好,我在使用TensorFlow 2.13版本运行Mask R-CNN模型时遇到了麻烦:模型在训练前的环节都正常,但启动训练时就触发了KerasTensor相关的TypeError,我推测问题出在自定义损失函数的写法上。

以下是我的代码细节,麻烦大家帮忙排查一下:

模型编译部分

def compile(self, learning_rate, momentum):
    """Compiles the Mask R-CNN model by adding losses and metrics."""
    # Check if the model is built before compiling
    if not hasattr(self, 'keras_model') or self.keras_model is None:
        print("⚠️ Model not built. Building now...")
        self.keras_model = self.build(mode="training", config=self.config)

    # Optimizer setup
    optimizer = tf.keras.optimizers.SGD(
        learning_rate=learning_rate, 
        momentum=momentum,
        clipnorm=self.config.GRADIENT_CLIP_NORM
    )
    
    loss_names = [
        "rpn_class_loss", "rpn_bbox_loss",
        "mrcnn_class_loss", "mrcnn_bbox_loss", "mrcnn_mask_loss"
    ]

    print(f"✅ Available layers: {[layer.name for layer in self.keras_model.layers]}")
    
    for loss_name in loss_names:
        try:
            loss_layer = self.keras_model.get_layer(loss_name)
            loss_value = tf.reduce_mean(loss_layer.output)
            print(f"✅ Loss layer {loss_name} found in model: {loss_value}")
            #loss_value = tf.convert_to_tensor(loss_value, dtype=tf.float32)
            @tf.function
            def compute_loss():
                return tf.reduce_mean(loss_layer.output)

            loss_value = compute_loss()
            print(f"✅ Loss layer {loss_name} converted from KerasTensor to normal tensor: {loss_value}")
            
            self.keras_model.add_loss(lambda: loss_value)
            print(f"✅ Added loss: {loss_name}")
        except ValueError:
            print(f"❌ Warning: Loss layer {loss_name} not found in model!")
    
    config_dict = {k: v for k, v in self.config.__dict__.items() if not k.startswith('__')}       
    # Compile the model
    self.keras_model.compile(
        optimizer=optimizer,
        loss=None,   # Mask R-CNN defines its own loss layers
        run_eagerly=True,
        experimental_run_tf_function=False
    )

    print("✅ Model compiled successfully!")

模型构建中添加损失的代码片段

rpn_class_loss = RPNClassLoss(name="rpn_class_loss")([input_rpn_match, rpn_class_logits])
self.add_loss(tf.reduce_mean(rpn_class_loss))
rpn_bbox_loss = RPNBBoxLoss(name="rpn_bbox_loss")([config, input_rpn_bbox, input_rpn_match, rpn_bbox])
self.add_loss(tf.reduce_mean(rpn_bbox_loss))
mrcnn_class_loss = MRCNNClassLoss(name="mrcnn_class_loss")([target_class_ids, mrcnn_class_logits, active_class_ids])
self.add_loss(tf.reduce_mean(mrcnn_class_loss))
mrcnn_bbox_loss = MRCNNBBoxLoss(name="mrcnn_bbox_loss")([target_bbox, target_class_ids, mrcnn_bbox])
self.add_loss(tf.reduce_mean(mrcnn_bbox_loss))
mrcnn_mask_loss = MRCNNMaskLoss(name="mrcnn_mask_loss")([target_mask, target_class_ids, mrcnn_mask])
self.add_loss(tf.reduce_mean(mrcnn_mask_loss))

自定义损失函数类实现

SmoothL1Loss

class SmoothL1Loss(Layer):
    @tf.function
    def call(self, inputs):
        """Computes Smooth-L1 loss."""
        y_true, y_pred = inputs  # Expecting two tensors: [batch, N, 4]

        diff = tf.abs(y_true - y_pred)
        less_than_one = tf.cast(tf.less(diff, 1.0), tf.float32)

        loss = (less_than_one * 0.5 * tf.square(diff)) + ((1.0 - less_than_one) * (diff - 0.5))
        return tf.reduce_mean(loss)  # ✅ Ensures numerical value

RPNClassLoss

class RPNClassLoss(Layer):
    @tf.function
    def call(self, inputs):
        rpn_match, rpn_class_logits = inputs  # Shapes: [batch, anchors, 1] and [batch, proposals, 2, 2]

        rpn_match = tf.squeeze(rpn_match, -1)  # Shape: [batch, anchors]

        # Convert -1/+1 labels to 0/1 format
        anchor_class = tf.where(rpn_match == 1, 1, 0)  # Shape: [batch, anchors]

        # ✅ Ensure indices do not exceed the available range
        max_proposals = tf.shape(rpn_class_logits)[1]  # Get the actual number of proposals
        valid_indices = tf.where(
            tf.logical_and(tf.not_equal(rpn_match, 0), tf.range(tf.shape(rpn_match)[1]) < max_proposals)
        )  # Shape: [num_valid, 2]

        # ✅ Gather only valid anchors
        rpn_class_logits = tf.gather_nd(rpn_class_logits, valid_indices)  # Shape: [num_valid, 2, 2]
        anchor_class = tf.gather_nd(anchor_class, valid_indices)  # Shape: [num_valid]

        #  **Fix: Ensure correct shape for labels**
        rpn_class_logits = tf.reshape(rpn_class_logits, [-1, tf.shape(rpn_class_logits)[-1]])  # [num_valid, num_classes]
        anchor_class = tf.reshape(anchor_class, [-1])  # [num_valid]

        # ✅ Ensure labels and logits match in shape
        min_size = tf.minimum(tf.shape(anchor_class)[0], tf.shape(rpn_class_logits)[0])
        anchor_class = anchor_class[:min_size]
        rpn_class_logits = rpn_class_logits[:min_size]

        # ✅ Compute loss
        loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=anchor_class, logits=rpn_class_logits)

        return tf.reduce_mean(loss)  # Ensure numerical loss

RPNBBoxLoss

class RPNBBoxLoss(Layer):
    @tf.function
    def call(self, inputs):
        config, target_bbox, rpn_match, rpn_bbox = inputs  # Expecting 4 inputs

        rpn_match = tf.squeeze(rpn_match, -1)  # Shape: [batch, anchors]
        indices = tf.where(tf.equal(rpn_match, 1))  # Select positive anchors

        # ✅ Convert indices[:, 1] to int32 to match max_indices
        indices = tf.cast(indices, tf.int32)

        max_indices = tf.shape(target_bbox)[1]  # Keep this as int32
        valid_mask = tf.less(indices[:, 1], max_indices)  # Ensure valid range
        valid_indices = tf.boolean_mask(indices, valid_mask)  # Remove invalid indices

        # Gather only the positive anchors' bbox values
        rpn_bbox = tf.gather_nd(rpn_bbox, valid_indices)  # Shape: [num_pos, 2, 4]
        target_bbox = tf.gather_nd(target_bbox, valid_indices)  # Shape: [num_pos, 4]
        
        # ✅ Fix shape mismatch by selecting first prediction (or averaging)
        rpn_bbox = tf.reduce_mean(rpn_bbox, axis=1)  # Shape: [num_pos, 4]

        # Compute loss safely using tf.cond to avoid NaNs
        num_samples = tf.shape(target_bbox)[0]
        loss = tf.cond(
            num_samples > 0,
            lambda: tf.reduce_mean(SmoothL1Loss()([target_bbox, rpn_bbox])),
            lambda: tf.constant(0.0, dtype=tf.float32)
        )
        
        return loss  # Ensure a valid numerical output

MRCNNClassLoss

class MRCNNClassLoss(Layer):
    @tf.function
    def call(self, inputs):
        target_class_ids, pred_class_logits, active_class_ids = inputs

        loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=target_class_ids, logits=pred_class_logits)
        return tf.reduce_mean(loss)

MRCNNBBoxLoss

class MRCNNBBoxLoss(Layer):
    @tf.function
    def call(self, inputs):
        target_bbox, target_class_ids, pred_bbox = inputs

        # Reshape for gathering
        target_class_ids = tf.reshape(target_class_ids, [-1])  # Shape: [batch * num_rois]
        target_bbox = tf.reshape(target_bbox, [-1, 4])  # Shape: [batch * num_rois, 4]
        pred_bbox = tf.reshape(pred_bbox, [-1, tf.shape(pred_bbox)[2], 4])  # Shape: [batch * num_rois, num_classes, 4]

        # Get positive ROIs
        positive_roi_ix = tf.where(target_class_ids > 0)

        # ✅ Ensure positive_roi_ix is not empty
        has_valid_rois = tf.greater(tf.shape(positive_roi_ix)[0], 0)

        def compute_loss():
            """Inner function for computing loss, wrapped for TensorFlow execution."""
            positive_roi_ix_flat = tf.reshape(positive_roi_ix, [-1])  # Flatten indices
            positive_class_ids = tf.gather(target_class_ids, positive_roi_ix_flat)
            
            positive_roi_ix_flat = tf.cast(positive_roi_ix_flat, tf.int32)
            positive_class_ids = tf.cast(positive_class_ids, tf.int32)

            indices = tf.stack([positive_roi_ix_flat, positive_class_ids], axis=1)
            pred_bbox_selected = tf.gather_nd(pred_bbox, indices)

            loss = tf.reduce_mean(SmoothL1Loss()([target_bbox, pred_bbox_selected]))  
            return tf.cast(loss, tf.float32)  # ✅ Ensure TensorFlow tensor output

        # Use `tf.cond` to handle empty cases
        loss = tf.cond(
            has_valid_rois,
            compute_loss,  # Call compute_loss if has_valid_rois is True
            lambda: tf.constant(0.0, dtype=tf.float32)  # Return 0.0 if has_valid_rois is False
        )

        return loss

MRCNNMaskLoss

class MRCNNMaskLoss(Layer):
    @tf.function
    def call(self, inputs):
        target_masks, target_class_ids, pred_masks = inputs

        # Flatten target_class_ids to be 1D: (batch * num_rois,)
        target_class_ids = tf.reshape(target_class_ids, [-1])
        
        # Flatten target_masks
        mask_shape = tf.shape(target_masks)
        target_masks = tf.reshape(target_masks, [-1, mask_shape[2], mask_shape[3]])
        
        # Reshape pred_masks
        pred_shape = tf.shape(pred_masks)
        pred_masks = tf.reshape(pred_masks, [-1, pred_shape[2], pred_shape[3], pred_shape[4]])
        
        # Permute predicted masks to [N, num_classes, height, width]
        pred_masks = tf.transpose(pred_masks, [0, 3, 1, 2])
        
        # Get indices of positive ROIs
        positive_ix = tf.where(target_class_ids > 0)[:, 0]
        
        # Ensure dtype consistency
        positive_ix = tf.cast(positive_ix, tf.int32)
        positive_class_ids = tf.cast(tf.gather(target_class_ids, positive_ix), tf.int32)
        
        # Stack indices for pred_masks gathering
        indices = tf.stack([positive_ix, positive_class_ids], axis=1)  # ✅ Fix dtype issue
        
        # Gather the masks (predicted and true) that contribute to loss
        y_true = tf.gather(target_masks, positive_ix)
        y_pred = tf.gather_nd(pred_masks, indices)
        
        # Compute binary cross entropy loss
        num_samples = tf.shape(y_true)[0]
        loss = tf.cond(
            tf.greater(num_samples, 0),  # Check if num_samples > 0
            lambda: tf.reduce_mean(tf.keras.losses.binary_crossentropy(y_true, y_pred)),
            lambda: tf.constant(0.0, dtype=tf.float32)
        )
        return loss

备注:内容来源于stack exchange,提问作者Naimur Hassan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:43:13