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

Keras指标输出异常值:自定义IoU实现问题求助

Fixing Your Keras IoU Metric Implementation

Let’s walk through the problems in your IoU code and fix them properly—since you’re using this as a Keras metric, there are a few critical missteps breaking the calculation:

Key Issues in Your Current Code

  • Undefined num_classes: You reference num_classes in your loop but never define or pass it as a parameter. This will throw an immediate NameError.
  • Mixing NumPy and Keras/TensorFlow operations: Using np.argmax on tensor inputs breaks Keras’s symbolic computation graph. Metrics need to stay within Keras/TensorFlow’s symbolic API to work during training/evaluation.
  • Deprecated TF functions: tf.to_int64 and tf.to_float are obsolete in modern TensorFlow versions; use tf.cast instead.
  • Incorrect union calculation: You initialize union to 0 and never update it. The correct union for a class is (true positives) + (predicted positives) - (intersection).
  • Flawed aggregation: You only sum intersections but don’t compute per-class IoU values before averaging (the standard approach for multi-class IoU, or mIoU).

Corrected Mean IoU Metric (for Keras)

Here’s a fixed implementation that follows Keras’s metric best practices, handles multi-class cases correctly, and avoids common pitfalls:

import tensorflow as tf
from tensorflow.keras import backend as K

def mean_iou(y_true_batch, y_pred_batch, num_classes):
    # Convert one-hot encoded labels to class indices (symbolic operation)
    y_true = K.argmax(y_true_batch, axis=-1)
    y_pred = K.argmax(y_pred_batch, axis=-1)
    
    # Initialize tensors to track intersection and union per class
    intersection = K.zeros((num_classes,))
    union = K.zeros((num_classes,))
    
    for class_idx in range(num_classes):
        # Create boolean masks for the current class
        true_class_mask = K.equal(y_true, class_idx)
        pred_class_mask = K.equal(y_pred, class_idx)
        
        # Calculate intersection (true positives for this class)
        class_intersection = K.sum(K.cast(K.logical_and(true_class_mask, pred_class_mask), dtype="float32"))
        # Calculate total true samples and predicted samples for the class
        true_total = K.sum(K.cast(true_class_mask, dtype="float32"))
        pred_total = K.sum(K.cast(pred_class_mask, dtype="float32"))
        # Union = true positives + false positives + false negatives
        class_union = true_total + pred_total - class_intersection
        
        # Update the intersection and union tensors
        intersection = K.concatenate([intersection[:class_idx], [class_intersection], intersection[class_idx+1:]])
        union = K.concatenate([union[:class_idx], [class_union], union[class_idx+1:]])
    
    # Avoid division by zero for classes with no samples
    per_class_iou = K.where(union > 0, intersection / union, K.zeros_like(intersection))
    # Return the average IoU across all classes (mean IoU)
    return K.mean(per_class_iou)

How to Use This in Your Model

When compiling your Keras model, pass the metric with your class count:

# Replace 5 with your actual number of classes
model.compile(
    optimizer="adam",
    loss="categorical_crossentropy",
    metrics=[lambda y_true, y_pred: mean_iou(y_true, y_pred, num_classes=5)]
)

Bonus: Reusable Metric Class (Better for Training)

For long-running training jobs, using a Keras Metric class is more reliable—it tracks state across batches automatically:

class MeanIoU(tf.keras.metrics.Metric):
    def __init__(self, num_classes, name="mean_iou", **kwargs):
        super().__init__(name=name, **kwargs)
        self.num_classes = num_classes
        # Track cumulative intersection and union per class
        self.intersection = self.add_weight(name="intersection", shape=(num_classes,), initializer="zeros")
        self.union = self.add_weight(name="union", shape=(num_classes,), initializer="zeros")
    
    def update_state(self, y_true, y_pred, sample_weight=None):
        y_true = K.argmax(y_true, axis=-1)
        y_pred = K.argmax(y_pred, axis=-1)
        
        for class_idx in range(self.num_classes):
            true_mask = K.equal(y_true, class_idx)
            pred_mask = K.equal(y_pred, class_idx)
            
            inter = K.sum(K.cast(K.logical_and(true_mask, pred_mask), dtype="float32"))
            true_total = K.sum(K.cast(true_mask, dtype="float32"))
            pred_total = K.sum(K.cast(pred_mask, dtype="float32"))
            uni = true_total + pred_total - inter
            
            # Update cumulative values
            self.intersection.assign_add(K.concatenate([self.intersection[:class_idx], [inter], self.intersection[class_idx+1:]]))
            self.union.assign_add(K.concatenate([self.union[:class_idx], [uni], self.union[class_idx+1:]]))
    
    def result(self):
        # Calculate per-class IoU, handle zero-union cases
        per_class_iou = K.where(self.union > 0, self.intersection / self.union, K.zeros_like(self.intersection))
        return K.mean(per_class_iou)
    
    def reset_state(self):
        # Reset metrics at the start of each epoch
        K.batch_set_value([(v, tf.zeros((self.num_classes,))) for v in [self.intersection, self.union]])

Usage for the Class:

model.compile(
    optimizer="adam",
    loss="categorical_crossentropy",
    metrics=[MeanIoU(num_classes=5)]
)

Why This Works

  • Symbolic operations: We use K.argmax instead of np.argmax to keep everything within Keras’s computation graph, ensuring the metric works correctly during training and evaluation.
  • Per-class calculation: We compute IoU for each class individually, then average them to get the mean IoU (the standard metric for multi-class segmentation tasks).
  • Division by zero protection: We use K.where to set IoU to 0 for classes with no true or predicted samples, preventing NaN values from breaking training.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:21:17