-
-
Save Kautenja/69d306c587ccdf464c45d28c1545e580 to your computer and use it in GitHub Desktop.
"""An implementation of the Intersection over Union (IoU) metric for Keras.""" | |
from keras import backend as K | |
def iou(y_true, y_pred, label: int): | |
""" | |
Return the Intersection over Union (IoU) for a given label. | |
Args: | |
y_true: the expected y values as a one-hot | |
y_pred: the predicted y values as a one-hot or softmax output | |
label: the label to return the IoU for | |
Returns: | |
the IoU for the given label | |
""" | |
# extract the label values using the argmax operator then | |
# calculate equality of the predictions and truths to the label | |
y_true = K.cast(K.equal(K.argmax(y_true), label), K.floatx()) | |
y_pred = K.cast(K.equal(K.argmax(y_pred), label), K.floatx()) | |
# calculate the |intersection| (AND) of the labels | |
intersection = K.sum(y_true * y_pred) | |
# calculate the |union| (OR) of the labels | |
union = K.sum(y_true) + K.sum(y_pred) - intersection | |
# avoid divide by zero - if the union is zero, return 1 | |
# otherwise, return the intersection over union | |
return K.switch(K.equal(union, 0), 1.0, intersection / union) | |
def build_iou_for(label: int, name: str=None): | |
""" | |
Build an Intersection over Union (IoU) metric for a label. | |
Args: | |
label: the label to build the IoU metric for | |
name: an optional name for debugging the built method | |
Returns: | |
a keras metric to evaluate IoU for the given label | |
Note: | |
label and name support list inputs for multiple labels | |
""" | |
# handle recursive inputs (e.g. a list of labels and names) | |
if isinstance(label, list): | |
if isinstance(name, list): | |
return [build_iou_for(l, n) for (l, n) in zip(label, name)] | |
return [build_iou_for(l) for l in label] | |
# build the method for returning the IoU of the given label | |
def label_iou(y_true, y_pred): | |
""" | |
Return the Intersection over Union (IoU) score for {0}. | |
Args: | |
y_true: the expected y values as a one-hot | |
y_pred: the predicted y values as a one-hot or softmax output | |
Returns: | |
the scalar IoU value for the given label ({0}) | |
""".format(label) | |
return iou(y_true, y_pred, label) | |
# if no name is provided, us the label | |
if name is None: | |
name = label | |
# change the name of the method for debugging | |
label_iou.__name__ = 'iou_{}'.format(name) | |
return label_iou | |
def mean_iou(y_true, y_pred): | |
""" | |
Return the Intersection over Union (IoU) score. | |
Args: | |
y_true: the expected y values as a one-hot | |
y_pred: the predicted y values as a one-hot or softmax output | |
Returns: | |
the scalar IoU value (mean over all labels) | |
""" | |
# get number of labels to calculate IoU for | |
num_labels = K.int_shape(y_pred)[-1] | |
# initialize a variable to store total IoU in | |
total_iou = K.variable(0) | |
# iterate over labels to calculate IoU for | |
for label in range(num_labels): | |
total_iou = total_iou + iou(y_true, y_pred, label) | |
# divide total IoU by number of labels to get mean IoU | |
return total_iou / num_labels | |
# explicitly define the outward facing API of this module | |
__all__ = [build_iou_for.__name__, mean_iou.__name__] |
I try to use this IoU loss for a U-Net model but this error comes up. What's the problem? thank you.
model.compile(optimizer = Adam(lr = 1e-4), loss = mean_iou, metrics = [mean_iou])
ValueError: No gradients provided for any variable: ['conv2d/kernel:0', 'conv2d/bias:0', 'conv2d_1/kernel:0', 'conv2d_1/bias:0', 'conv2d_2/kernel:0', 'conv2d_2/bias:0', 'conv2d_3/kernel:0', 'conv2d_3/bias:0', 'conv2d_4/kernel:0', 'conv2d_4/bias:0', 'conv2d_5/kernel:0', 'conv2d_5/bias:0', 'conv2d_6/kernel:0', 'conv2d_6/bias:0', 'conv2d_7/kernel:0', 'conv2d_7/bias:0', 'conv2d_8/kernel:0', 'conv2d_8/bias:0', 'conv2d_9/kernel:0', 'conv2d_9/bias:0', 'conv2d_10/kernel:0', 'conv2d_10/bias:0', 'conv2d_11/kernel:0', 'conv2d_11/bias:0', 'conv2d_12/kernel:0', 'conv2d_12/bias:0', 'conv2d_13/kernel:0', 'conv2d_13/bias:0', 'conv2d_14/kernel:0', 'conv2d_14/bias:0', 'conv2d_15/kernel:0', 'conv2d_15/bias:0', 'conv2d_16/kernel:0', 'conv2d_16/bias:0', 'conv2d_17/kernel:0', 'conv2d_17/bias:0', 'conv2d_18/kernel:0', 'conv2d_18/bias:0', 'conv2d_19/kernel:0', 'conv2d_19/bias:0', 'conv2d_20/kernel:0', 'conv2d_20/bias:0', 'conv2d_21/kernel:0', 'conv2d_21/bias:0', 'conv2d_22/kernel:0', 'conv2d_22/bias:0', 'conv2d_23/kernel:0', 'conv2d_23/bias:0'].
In keras gradient doesnt exist for argmax. this loss doesnt work.
it's a metric, not a loss. So yes, as a loss, it does not work, but as a metric (the intended design), it works fine.
Yeap. As a metric it works.
how to use this?