| @@ 214-311 (lines=98) @@ | ||
| 211 | assert got == expected |
|
| 212 | ||
| 213 | ||
| 214 | class TestJaccardIndex: |
|
| 215 | @pytest.mark.parametrize( |
|
| 216 | ("value", "smooth_nr", "smooth_dr", "expected"), |
|
| 217 | [ |
|
| 218 | (0, 1e-5, 1e-5, 1), |
|
| 219 | (0, 0, 1e-5, 0), |
|
| 220 | (0, 1e-5, 0, np.inf), |
|
| 221 | (0, 0, 0, np.nan), |
|
| 222 | (0, 1e-7, 1e-7, 1), |
|
| 223 | (1, 1e-5, 1e-5, 1), |
|
| 224 | (1, 0, 1e-5, 1), |
|
| 225 | (1, 1e-5, 0, 1), |
|
| 226 | (1, 0, 0, 1), |
|
| 227 | (1, 1e-7, 1e-7, 1), |
|
| 228 | ], |
|
| 229 | ) |
|
| 230 | def test_smooth( |
|
| 231 | self, |
|
| 232 | value: float, |
|
| 233 | smooth_nr: float, |
|
| 234 | smooth_dr: float, |
|
| 235 | expected: float, |
|
| 236 | ): |
|
| 237 | """ |
|
| 238 | Test values in extreme cases where numerator/denominator are all zero. |
|
| 239 | ||
| 240 | :param value: value for input. |
|
| 241 | :param smooth_nr: constant for numerator. |
|
| 242 | :param smooth_dr: constant for denominator. |
|
| 243 | :param expected: target value. |
|
| 244 | """ |
|
| 245 | shape = (1, 10) |
|
| 246 | y_true = tf.ones(shape=shape) * value |
|
| 247 | y_pred = tf.ones(shape=shape) * value |
|
| 248 | ||
| 249 | got = label.JaccardIndex(smooth_nr=smooth_nr, smooth_dr=smooth_dr)._call( |
|
| 250 | y_true, |
|
| 251 | y_pred, |
|
| 252 | ) |
|
| 253 | expected = tf.constant(expected) |
|
| 254 | assert is_equal_tf(got[0], expected) |
|
| 255 | ||
| 256 | @pytest.mark.parametrize("binary", [True, False]) |
|
| 257 | @pytest.mark.parametrize("background_weight", [0.0, 0.1, 0.5, 1.0]) |
|
| 258 | @pytest.mark.parametrize("shape", [(1,), (10,), (100,), (2, 3), (2, 3, 4)]) |
|
| 259 | def test_exact_value(self, binary: bool, background_weight: float, shape: Tuple): |
|
| 260 | """ |
|
| 261 | Test Jaccard index by comparing at ground truth values. |
|
| 262 | ||
| 263 | :param binary: if project labels to binary values. |
|
| 264 | :param background_weight: the weight of background class. |
|
| 265 | :param shape: shape of input. |
|
| 266 | """ |
|
| 267 | # init |
|
| 268 | shape = (1,) + shape # add batch axis |
|
| 269 | foreground_weight = 1 - background_weight |
|
| 270 | tf.random.set_seed(0) |
|
| 271 | y_true = tf.random.uniform(shape=shape) |
|
| 272 | y_pred = tf.random.uniform(shape=shape) |
|
| 273 | ||
| 274 | # obtained value |
|
| 275 | got = label.JaccardIndex( |
|
| 276 | binary=binary, |
|
| 277 | background_weight=background_weight, |
|
| 278 | ).call(y_true=y_true, y_pred=y_pred) |
|
| 279 | ||
| 280 | # expected value |
|
| 281 | flatten = tf.keras.layers.Flatten() |
|
| 282 | y_true = flatten(y_true) |
|
| 283 | y_pred = flatten(y_pred) |
|
| 284 | if binary: |
|
| 285 | y_true = tf.cast(y_true >= 0.5, dtype=y_true.dtype) |
|
| 286 | y_pred = tf.cast(y_pred >= 0.5, dtype=y_pred.dtype) |
|
| 287 | ||
| 288 | num = foreground_weight * tf.reduce_sum( |
|
| 289 | y_true * y_pred, axis=1 |
|
| 290 | ) + background_weight * tf.reduce_sum((1 - y_true) * (1 - y_pred), axis=1) |
|
| 291 | denom = foreground_weight * tf.reduce_sum( |
|
| 292 | y_true + y_pred, axis=1 |
|
| 293 | ) + background_weight * tf.reduce_sum((1 - y_true) + (1 - y_pred), axis=1) |
|
| 294 | denom = denom - num |
|
| 295 | expected = (num + EPS) / (denom + EPS) |
|
| 296 | ||
| 297 | assert is_equal_tf(got, expected) |
|
| 298 | ||
| 299 | def test_get_config(self): |
|
| 300 | got = label.JaccardIndex().get_config() |
|
| 301 | expected = dict( |
|
| 302 | binary=False, |
|
| 303 | background_weight=0.0, |
|
| 304 | smooth_nr=1e-5, |
|
| 305 | smooth_dr=1e-5, |
|
| 306 | scales=None, |
|
| 307 | kernel="gaussian", |
|
| 308 | reduction=tf.keras.losses.Reduction.SUM, |
|
| 309 | name="JaccardIndex", |
|
| 310 | ) |
|
| 311 | assert got == expected |
|
| 312 | ||
| 313 | ||
| 314 | def test_foreground_prop_binary(): |
|
| @@ 37-134 (lines=98) @@ | ||
| 34 | assert got == expected |
|
| 35 | ||
| 36 | ||
| 37 | class TestDiceScore: |
|
| 38 | @pytest.mark.parametrize( |
|
| 39 | ("value", "smooth_nr", "smooth_dr", "expected"), |
|
| 40 | [ |
|
| 41 | (0, 1e-5, 1e-5, 1), |
|
| 42 | (0, 0, 1e-5, 0), |
|
| 43 | (0, 1e-5, 0, np.inf), |
|
| 44 | (0, 0, 0, np.nan), |
|
| 45 | (0, 1e-7, 1e-7, 1), |
|
| 46 | (1, 1e-5, 1e-5, 1), |
|
| 47 | (1, 0, 1e-5, 1), |
|
| 48 | (1, 1e-5, 0, 1), |
|
| 49 | (1, 0, 0, 1), |
|
| 50 | (1, 1e-7, 1e-7, 1), |
|
| 51 | ], |
|
| 52 | ) |
|
| 53 | def test_smooth( |
|
| 54 | self, |
|
| 55 | value: float, |
|
| 56 | smooth_nr: float, |
|
| 57 | smooth_dr: float, |
|
| 58 | expected: float, |
|
| 59 | ): |
|
| 60 | """ |
|
| 61 | Test values in extreme cases where numerator/denominator are all zero. |
|
| 62 | ||
| 63 | :param value: value for input. |
|
| 64 | :param smooth_nr: constant for numerator. |
|
| 65 | :param smooth_dr: constant for denominator. |
|
| 66 | :param expected: target value. |
|
| 67 | """ |
|
| 68 | shape = (1, 10) |
|
| 69 | y_true = tf.ones(shape=shape) * value |
|
| 70 | y_pred = tf.ones(shape=shape) * value |
|
| 71 | ||
| 72 | got = label.DiceScore(smooth_nr=smooth_nr, smooth_dr=smooth_dr)._call( |
|
| 73 | y_true, |
|
| 74 | y_pred, |
|
| 75 | ) |
|
| 76 | expected = tf.constant(expected) |
|
| 77 | assert is_equal_tf(got[0], expected) |
|
| 78 | ||
| 79 | @pytest.mark.parametrize("binary", [True, False]) |
|
| 80 | @pytest.mark.parametrize("background_weight", [0.0, 0.1, 0.5, 1.0]) |
|
| 81 | @pytest.mark.parametrize("shape", [(1,), (10,), (100,), (2, 3), (2, 3, 4)]) |
|
| 82 | def test_exact_value(self, binary: bool, background_weight: float, shape: Tuple): |
|
| 83 | """ |
|
| 84 | Test dice score by comparing at ground truth values. |
|
| 85 | ||
| 86 | :param binary: if project labels to binary values. |
|
| 87 | :param background_weight: the weight of background class. |
|
| 88 | :param shape: shape of input. |
|
| 89 | """ |
|
| 90 | # init |
|
| 91 | shape = (1,) + shape # add batch axis |
|
| 92 | foreground_weight = 1 - background_weight |
|
| 93 | tf.random.set_seed(0) |
|
| 94 | y_true = tf.random.uniform(shape=shape) |
|
| 95 | y_pred = tf.random.uniform(shape=shape) |
|
| 96 | ||
| 97 | # obtained value |
|
| 98 | got = label.DiceScore( |
|
| 99 | binary=binary, |
|
| 100 | background_weight=background_weight, |
|
| 101 | ).call(y_true=y_true, y_pred=y_pred) |
|
| 102 | ||
| 103 | # expected value |
|
| 104 | flatten = tf.keras.layers.Flatten() |
|
| 105 | y_true = flatten(y_true) |
|
| 106 | y_pred = flatten(y_pred) |
|
| 107 | if binary: |
|
| 108 | y_true = tf.cast(y_true >= 0.5, dtype=y_true.dtype) |
|
| 109 | y_pred = tf.cast(y_pred >= 0.5, dtype=y_pred.dtype) |
|
| 110 | ||
| 111 | num = foreground_weight * tf.reduce_sum( |
|
| 112 | y_true * y_pred, axis=1 |
|
| 113 | ) + background_weight * tf.reduce_sum((1 - y_true) * (1 - y_pred), axis=1) |
|
| 114 | num *= 2 |
|
| 115 | denom = foreground_weight * tf.reduce_sum( |
|
| 116 | y_true + y_pred, axis=1 |
|
| 117 | ) + background_weight * tf.reduce_sum((1 - y_true) + (1 - y_pred), axis=1) |
|
| 118 | expected = (num + EPS) / (denom + EPS) |
|
| 119 | ||
| 120 | assert is_equal_tf(got, expected) |
|
| 121 | ||
| 122 | def test_get_config(self): |
|
| 123 | got = label.DiceScore().get_config() |
|
| 124 | expected = dict( |
|
| 125 | binary=False, |
|
| 126 | background_weight=0.0, |
|
| 127 | smooth_nr=1e-5, |
|
| 128 | smooth_dr=1e-5, |
|
| 129 | scales=None, |
|
| 130 | kernel="gaussian", |
|
| 131 | reduction=tf.keras.losses.Reduction.SUM, |
|
| 132 | name="DiceScore", |
|
| 133 | ) |
|
| 134 | assert got == expected |
|
| 135 | ||
| 136 | ||
| 137 | class TestCrossEntropy: |
|