segmodels-keras 0.1.0.dev10__tar.gz → 0.1.0.dev11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/pyproject.toml +1 -1
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/__init__.py +5 -2
- segmodels_keras-0.1.0.dev11/segmodels_keras/_version.py +2 -0
- segmodels_keras-0.1.0.dev11/segmodels_keras/backbones/__init__.py +1 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/_common_blocks.py +6 -3
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/backbones_factory.py +37 -4
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/inception_resnet_v2.py +9 -3
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/inception_v3.py +9 -2
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/resnet_18_34.py +3 -1
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/base/__init__.py +2 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/base/functional.py +35 -10
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/base/objects.py +31 -18
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/losses.py +24 -22
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/metrics.py +22 -19
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/_common_blocks.py +1 -2
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/_utils.py +1 -1
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/fpn.py +26 -22
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/linknet.py +27 -23
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/pspnet.py +23 -22
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/unet.py +38 -35
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/utils.py +16 -13
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/setup.py +2 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/tests/test_backbones.py +2 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/tests/test_helper.py +2 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/tests/test_metrics.py +2 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/tests/test_models.py +3 -1
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/tests/test_utils.py +2 -0
- segmodels_keras-0.1.0.dev10/segmodels_keras/_version.py +0 -2
- segmodels_keras-0.1.0.dev10/segmodels_keras/backbones/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/LICENSE +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/MANIFEST.in +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/README.rst +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/_compat.py +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/_weights.py +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/resnet_common.py +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/model_factory.py +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/py.typed +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/SOURCES.txt +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/dependency_links.txt +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/requires.txt +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/top_level.txt +0 -0
- {segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/setup.cfg +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Segmentation Models for Keras."""
|
|
2
|
+
|
|
1
3
|
import functools # noqa: I001
|
|
2
4
|
import os
|
|
3
5
|
from typing import Any
|
|
@@ -43,12 +45,12 @@ def filter_kwargs(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
def framework() -> str | None:
|
|
46
|
-
"""Return name of Segmentation Models framework"""
|
|
48
|
+
"""Return name of Segmentation Models framework."""
|
|
47
49
|
return _KERAS_FRAMEWORK
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
def set_framework(name: str) -> None:
|
|
51
|
-
"""Set framework for Segmentation Models
|
|
53
|
+
"""Set framework for Segmentation Models.
|
|
52
54
|
|
|
53
55
|
Args:
|
|
54
56
|
name (str): one of ``keras``, ``tf.keras``, case insensitive.
|
|
@@ -118,6 +120,7 @@ get_available_backbone_names = Backbones.models_names
|
|
|
118
120
|
|
|
119
121
|
|
|
120
122
|
def get_preprocessing(name: str) -> Callable[[Any], Any]:
|
|
123
|
+
"""Get the preprocessing function for a backbone by name."""
|
|
121
124
|
return Backbones.get_preprocessing(name)
|
|
122
125
|
|
|
123
126
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Backbones package."""
|
|
@@ -44,6 +44,7 @@ def GroupConv2D(
|
|
|
44
44
|
If you don't specify anything, no activation is applied
|
|
45
45
|
(ie. "linear" activation: a(x) = x).
|
|
46
46
|
padding: one of "valid" or "same" (case-insensitive).
|
|
47
|
+
kwargs: additional keyword arguments (unused).
|
|
47
48
|
|
|
48
49
|
Input shape:
|
|
49
50
|
4D tensor with shape: (batch, rows, cols, channels) if data_format is
|
|
@@ -97,12 +98,14 @@ def expand_dims(x, channels_axis):
|
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
def ChannelSE(reduction=16, **kwargs): # noqa: ARG001
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
"""Squeeze and Excitation block.
|
|
102
|
+
|
|
103
|
+
Reimplementation inspired by
|
|
104
|
+
https://github.com/Cadene/pretrained-models.pytorch/blob/master/pretrainedmodels/models/senet.py.
|
|
103
105
|
|
|
104
106
|
Args:
|
|
105
107
|
reduction: channels squeeze factor
|
|
108
|
+
kwargs: additional keyword arguments (unused).
|
|
106
109
|
|
|
107
110
|
"""
|
|
108
111
|
channels_axis = 3 if backend.image_data_format() == "channels_last" else 1
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""Backbones factory module."""
|
|
2
|
+
|
|
3
|
+
import keras
|
|
1
4
|
import keras.applications as ka
|
|
2
5
|
|
|
3
6
|
from . import inception_resnet_v2 as irv2
|
|
@@ -249,27 +252,54 @@ class BackbonesFactory:
|
|
|
249
252
|
),
|
|
250
253
|
],
|
|
251
254
|
# EfficientNetV2
|
|
252
|
-
#
|
|
255
|
+
# Stage layout differs from EfficientNet-Bx.
|
|
256
|
+
# Skip layers selected to match decoder resolutions:
|
|
257
|
+
# x16 -> x8 -> x4 -> x2
|
|
258
|
+
"efficientnetv2s": (
|
|
259
|
+
ka.EfficientNetV2S,
|
|
260
|
+
ka.efficientnet_v2.preprocess_input,
|
|
261
|
+
(
|
|
262
|
+
"block6a_expand_conv", # 14x14
|
|
263
|
+
"block4a_expand_conv", # 28x28
|
|
264
|
+
"block2a_expand_activation", # 56x56
|
|
265
|
+
"stem_activation", # 112x112
|
|
266
|
+
),
|
|
267
|
+
),
|
|
253
268
|
"efficientnetv2m": (
|
|
254
269
|
ka.EfficientNetV2M,
|
|
255
270
|
ka.efficientnet_v2.preprocess_input,
|
|
256
271
|
(
|
|
257
272
|
"block6a_expand_conv",
|
|
258
273
|
"block4a_expand_conv",
|
|
259
|
-
"
|
|
260
|
-
"
|
|
274
|
+
"block2a_expand_activation",
|
|
275
|
+
"stem_activation",
|
|
276
|
+
),
|
|
277
|
+
),
|
|
278
|
+
"efficientnetv2l": (
|
|
279
|
+
ka.EfficientNetV2L,
|
|
280
|
+
ka.efficientnet_v2.preprocess_input,
|
|
281
|
+
(
|
|
282
|
+
"block6a_expand_conv",
|
|
283
|
+
"block4a_expand_conv",
|
|
284
|
+
"block2a_expand_activation",
|
|
285
|
+
"stem_activation",
|
|
261
286
|
),
|
|
262
287
|
),
|
|
263
288
|
}
|
|
264
289
|
|
|
265
290
|
@property
|
|
266
291
|
def models(self):
|
|
292
|
+
"""Get the dictionary of available backbones."""
|
|
267
293
|
return self._models
|
|
268
294
|
|
|
269
295
|
def models_names(self):
|
|
296
|
+
"""Get the list of available backbone names."""
|
|
270
297
|
return list(self.models.keys())
|
|
271
298
|
|
|
272
|
-
|
|
299
|
+
"""Get a backbone model by name."""
|
|
300
|
+
|
|
301
|
+
def get_backbone(self, name, *args, **kwargs) -> keras.Model:
|
|
302
|
+
"""Get a backbone model by name."""
|
|
273
303
|
backbone = self._models.get(name)
|
|
274
304
|
if backbone is None:
|
|
275
305
|
raise ValueError(f"Backbone with name '{name}' is not supported.")
|
|
@@ -279,12 +309,15 @@ class BackbonesFactory:
|
|
|
279
309
|
return model
|
|
280
310
|
|
|
281
311
|
def get_feature_layers(self, name, n=5):
|
|
312
|
+
"""Get the list of skip layers for a backbone by name."""
|
|
282
313
|
return self._models.get(name)[2][:n]
|
|
283
314
|
|
|
284
315
|
def get_preprocessing(self, name):
|
|
316
|
+
"""Get the preprocessing function for a backbone by name."""
|
|
285
317
|
return self._models.get(name)[1]
|
|
286
318
|
|
|
287
319
|
def get_custom_objects(self, name):
|
|
320
|
+
"""Get the custom objects for a backbone by name."""
|
|
288
321
|
if name == "inceptionresnetv2":
|
|
289
322
|
return irv2.get_custom_objects()
|
|
290
323
|
return {}
|
|
@@ -314,16 +314,18 @@ def conv2d_bn(
|
|
|
314
314
|
|
|
315
315
|
|
|
316
316
|
class CustomScaleLayer(layers.Layer):
|
|
317
|
-
|
|
317
|
+
"""Custom layer to scale the residuals before adding them to the shortcut branch."""
|
|
318
|
+
|
|
319
|
+
def __init__(self, scale, **kwargs): # noqa: D107
|
|
318
320
|
super().__init__(**kwargs)
|
|
319
321
|
self.scale = scale
|
|
320
322
|
|
|
321
|
-
def get_config(self):
|
|
323
|
+
def get_config(self): # noqa: D102
|
|
322
324
|
config = super().get_config()
|
|
323
325
|
config.update({"scale": self.scale})
|
|
324
326
|
return config
|
|
325
327
|
|
|
326
|
-
def call(self, inputs):
|
|
328
|
+
def call(self, inputs): # noqa: D102
|
|
327
329
|
return inputs[0] + inputs[1] * self.scale
|
|
328
330
|
|
|
329
331
|
|
|
@@ -416,6 +418,9 @@ def preprocess_input(x, data_format=None):
|
|
|
416
418
|
|
|
417
419
|
Args:
|
|
418
420
|
x: a 4D numpy array consists of RGB values within [0, 255].
|
|
421
|
+
data_format: image data format, either "channels_first" or
|
|
422
|
+
"channels_last". Defaults to the value found in
|
|
423
|
+
`keras.backend.image_data_format()`.
|
|
419
424
|
|
|
420
425
|
Returns:
|
|
421
426
|
Preprocessed array.
|
|
@@ -443,4 +448,5 @@ def decode_predictions(preds, top=5):
|
|
|
443
448
|
|
|
444
449
|
|
|
445
450
|
def get_custom_objects():
|
|
451
|
+
"""Returns a dictionary containing the custom objects used by the model."""
|
|
446
452
|
return {"CustomScaleLayer": CustomScaleLayer}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Inception V3 model for Keras.
|
|
2
|
+
|
|
2
3
|
Note that the input image format for this model is different than for
|
|
3
4
|
the VGG16 and ResNet models (299x299 instead of 224x224),
|
|
4
5
|
and that the input preprocessing function is also different (same as Xception).
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
References:
|
|
8
|
+
----------
|
|
6
9
|
- [Rethinking the Inception Architecture for Computer Vision](
|
|
7
|
-
http://arxiv.org/abs/1512.00567) (CVPR 2016)
|
|
10
|
+
http://arxiv.org/abs/1512.00567) (CVPR 2016).
|
|
8
11
|
"""
|
|
9
12
|
|
|
10
13
|
import os
|
|
@@ -30,6 +33,7 @@ WEIGHTS_PATH_NO_TOP = (
|
|
|
30
33
|
|
|
31
34
|
def conv2d_bn(x, filters, num_row, num_col, padding="same", strides=(1, 1), name=None):
|
|
32
35
|
"""Utility function to apply conv + BN.
|
|
36
|
+
|
|
33
37
|
# Arguments
|
|
34
38
|
x: input tensor.
|
|
35
39
|
filters: filters in `Conv2D`.
|
|
@@ -76,9 +80,11 @@ def InceptionV3(
|
|
|
76
80
|
**kwargs, # noqa: ARG001
|
|
77
81
|
):
|
|
78
82
|
"""Instantiates the Inception v3 architecture.
|
|
83
|
+
|
|
79
84
|
Optionally loads weights pre-trained on ImageNet.
|
|
80
85
|
Note that the data format convention used by the model is
|
|
81
86
|
the one specified in your Keras config at `~/.keras/keras.json`.
|
|
87
|
+
|
|
82
88
|
# Arguments
|
|
83
89
|
include_top: whether to include the fully-connected
|
|
84
90
|
layer at the top of the network.
|
|
@@ -372,6 +378,7 @@ def InceptionV3(
|
|
|
372
378
|
|
|
373
379
|
def preprocess_input(x, **kwargs):
|
|
374
380
|
"""Preprocesses a numpy array encoding a batch of images.
|
|
381
|
+
|
|
375
382
|
# Arguments
|
|
376
383
|
x: a 4D numpy array consists of RGB values within [0, 255].
|
|
377
384
|
# Returns
|
|
@@ -109,7 +109,6 @@ def ResNet(
|
|
|
109
109
|
Returns:
|
|
110
110
|
A ``keras.Model`` instance.
|
|
111
111
|
"""
|
|
112
|
-
|
|
113
112
|
# Determine proper input shape
|
|
114
113
|
input_shape = _obtain_input_shape(
|
|
115
114
|
input_shape,
|
|
@@ -358,6 +357,9 @@ def preprocess_input(x, data_format=None):
|
|
|
358
357
|
|
|
359
358
|
Args:
|
|
360
359
|
x: a 4D numpy array consists of RGB values within [0, 255].
|
|
360
|
+
data_format: image data format, either "channels_first" or
|
|
361
|
+
"channels_last". Defaults to the value found in
|
|
362
|
+
`keras.backend.image_data_format()`.
|
|
361
363
|
|
|
362
364
|
Returns:
|
|
363
365
|
Preprocessed array.
|
{segmodels_keras-0.1.0.dev10 → segmodels_keras-0.1.0.dev11}/segmodels_keras/base/functional.py
RENAMED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Functional utilities for segmentation models."""
|
|
2
|
+
|
|
1
3
|
from typing import Any
|
|
2
4
|
|
|
3
5
|
import keras
|
|
@@ -33,6 +35,7 @@ def _gather_channels(x: Any, indexes: Any, **kwargs: Any) -> Any:
|
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def get_reduce_axes(per_image: bool, **kwargs: Any) -> list[int]:
|
|
38
|
+
"""Get axes for reduction operations."""
|
|
36
39
|
backend = kwargs["backend"]
|
|
37
40
|
|
|
38
41
|
axes = [1, 2] if backend.image_data_format() == "channels_last" else [2, 3]
|
|
@@ -44,7 +47,7 @@ def get_reduce_axes(per_image: bool, **kwargs: Any) -> list[int]:
|
|
|
44
47
|
def gather_channels(
|
|
45
48
|
*xs: Any, indexes: int | list[int] | None = None, **kwargs: Any
|
|
46
49
|
) -> tuple[Any, ...] | tuple[Any]:
|
|
47
|
-
"""Slice tensors along channels axis by given indexes"""
|
|
50
|
+
"""Slice tensors along channels axis by given indexes."""
|
|
48
51
|
if indexes is None:
|
|
49
52
|
return xs
|
|
50
53
|
elif isinstance(indexes, (int)):
|
|
@@ -54,6 +57,7 @@ def gather_channels(
|
|
|
54
57
|
|
|
55
58
|
|
|
56
59
|
def round_if_needed(x, threshold, **kwargs):
|
|
60
|
+
"""Round predictions if threshold is not None."""
|
|
57
61
|
if threshold is not None:
|
|
58
62
|
x = ops.greater(x, threshold)
|
|
59
63
|
if not KERAS_GTE_3:
|
|
@@ -65,6 +69,10 @@ def round_if_needed(x, threshold, **kwargs):
|
|
|
65
69
|
|
|
66
70
|
|
|
67
71
|
def average(x, per_image=False, class_weights=None, **kwargs): # noqa: ARG001
|
|
72
|
+
"""Calculate the average of a tensor.
|
|
73
|
+
|
|
74
|
+
Optionally per image and with class weights.
|
|
75
|
+
"""
|
|
68
76
|
if per_image:
|
|
69
77
|
x = ops.mean(x, axis=0)
|
|
70
78
|
if class_weights is not None:
|
|
@@ -87,12 +95,14 @@ def iou_score(
|
|
|
87
95
|
threshold=None,
|
|
88
96
|
**kwargs,
|
|
89
97
|
):
|
|
90
|
-
r"""The `Jaccard index`_
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
r"""The `Jaccard index`_.
|
|
99
|
+
|
|
100
|
+
Also known as Intersection over Union and the Jaccard similarity coefficient
|
|
101
|
+
(originally coined coefficient de communauté by Paul Jaccard),
|
|
102
|
+
is a statistic used for comparing the similarity and diversity of sample
|
|
93
103
|
sets. The Jaccard coefficient measures similarity between finite sample sets,
|
|
94
104
|
and is defined as the size of the intersection divided by the size of the union of
|
|
95
|
-
the sample sets
|
|
105
|
+
the sample sets.
|
|
96
106
|
|
|
97
107
|
.. math:: J(A, B) = \frac{A \cap B}{A \cup B}
|
|
98
108
|
|
|
@@ -107,6 +117,8 @@ def iou_score(
|
|
|
107
117
|
else over whole batch
|
|
108
118
|
threshold: value to round predictions (use ``>`` comparison), if ``None``
|
|
109
119
|
prediction will not be round
|
|
120
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
121
|
+
operations
|
|
110
122
|
|
|
111
123
|
Returns:
|
|
112
124
|
IoU/Jaccard score in range [0, 1]
|
|
@@ -139,9 +151,10 @@ def f_score(
|
|
|
139
151
|
threshold=None,
|
|
140
152
|
**kwargs,
|
|
141
153
|
):
|
|
142
|
-
r"""The F-score (Dice coefficient)
|
|
143
|
-
|
|
144
|
-
|
|
154
|
+
r"""The F-score (Dice coefficient).
|
|
155
|
+
|
|
156
|
+
Can be interpreted as a weighted average of the precision and recall, where an
|
|
157
|
+
F-score reaches its best value at 1 and worst score at 0.
|
|
145
158
|
The relative contribution of ``precision`` and ``recall`` to the F1-score are equal.
|
|
146
159
|
|
|
147
160
|
The formula for the F score is:
|
|
@@ -171,6 +184,8 @@ def f_score(
|
|
|
171
184
|
else over whole batch
|
|
172
185
|
threshold: value to round predictions (use ``>`` comparison), if ``None``
|
|
173
186
|
prediction will not be round
|
|
187
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
188
|
+
operations
|
|
174
189
|
|
|
175
190
|
Returns:
|
|
176
191
|
F-score in range [0, 1]
|
|
@@ -224,6 +239,8 @@ def precision(
|
|
|
224
239
|
threshold: Float value to round predictions (use ``>`` comparison), if ``None``
|
|
225
240
|
prediction will not be round.
|
|
226
241
|
name: Optional string, if ``None`` default ``precision`` name is used.
|
|
242
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
243
|
+
operations
|
|
227
244
|
|
|
228
245
|
Returns:
|
|
229
246
|
float: precision score
|
|
@@ -273,6 +290,8 @@ def recall(
|
|
|
273
290
|
threshold: Float value to round predictions (use ``>`` comparison), if ``None``
|
|
274
291
|
prediction will not be round.
|
|
275
292
|
name: Optional string, if ``None`` default ``precision`` name is used.
|
|
293
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
294
|
+
operations
|
|
276
295
|
|
|
277
296
|
Returns:
|
|
278
297
|
float: recall score
|
|
@@ -296,6 +315,7 @@ def recall(
|
|
|
296
315
|
|
|
297
316
|
|
|
298
317
|
def categorical_crossentropy(gt, pr, class_weights=1.0, class_indexes=None, **kwargs):
|
|
318
|
+
"""Calculate categorical crossentropy loss between gt and pr tensors."""
|
|
299
319
|
backend = kwargs["backend"]
|
|
300
320
|
|
|
301
321
|
gt, pr = gather_channels(gt, pr, indexes=class_indexes, **kwargs)
|
|
@@ -313,11 +333,12 @@ def categorical_crossentropy(gt, pr, class_weights=1.0, class_indexes=None, **kw
|
|
|
313
333
|
|
|
314
334
|
|
|
315
335
|
def binary_crossentropy(gt, pr, **kwargs): # noqa: ARG001
|
|
336
|
+
"""Calculate binary crossentropy loss between gt and pr tensors."""
|
|
316
337
|
return ops.mean(ops.binary_crossentropy(gt, pr))
|
|
317
338
|
|
|
318
339
|
|
|
319
340
|
def categorical_focal_loss(gt, pr, gamma=2.0, alpha=0.25, class_indexes=None, **kwargs):
|
|
320
|
-
r"""Implementation of Focal Loss from the paper in multiclass classification
|
|
341
|
+
r"""Implementation of Focal Loss from the paper in multiclass classification.
|
|
321
342
|
|
|
322
343
|
Formula:
|
|
323
344
|
loss = - gt * alpha * ((1 - pr)^gamma) * log(pr)
|
|
@@ -329,6 +350,8 @@ def categorical_focal_loss(gt, pr, gamma=2.0, alpha=0.25, class_indexes=None, **
|
|
|
329
350
|
gamma: focusing parameter for modulating factor (1-p), default 2.0
|
|
330
351
|
class_indexes: Optional integer or list of integers, classes to consider, if
|
|
331
352
|
``None`` all classes are used.
|
|
353
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
354
|
+
operations
|
|
332
355
|
|
|
333
356
|
"""
|
|
334
357
|
gt, pr = gather_channels(gt, pr, indexes=class_indexes, **kwargs)
|
|
@@ -343,7 +366,7 @@ def categorical_focal_loss(gt, pr, gamma=2.0, alpha=0.25, class_indexes=None, **
|
|
|
343
366
|
|
|
344
367
|
|
|
345
368
|
def binary_focal_loss(gt, pr, gamma=2.0, alpha=0.25, **kwargs): # noqa: ARG001
|
|
346
|
-
r"""Implementation of Focal Loss from the paper in binary classification
|
|
369
|
+
r"""Implementation of Focal Loss from the paper in binary classification.
|
|
347
370
|
|
|
348
371
|
Formula:
|
|
349
372
|
loss = - gt * alpha * ((1 - pr)^gamma) * log(pr) \
|
|
@@ -354,6 +377,8 @@ def binary_focal_loss(gt, pr, gamma=2.0, alpha=0.25, **kwargs): # noqa: ARG001
|
|
|
354
377
|
pr: prediction 4D keras tensor (B, H, W, C) or (B, C, H, W)
|
|
355
378
|
alpha: the same as weighting factor in balanced cross entropy, default 0.25
|
|
356
379
|
gamma: focusing parameter for modulating factor (1-p), default 2.0
|
|
380
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
381
|
+
operations
|
|
357
382
|
|
|
358
383
|
"""
|
|
359
384
|
# clip to prevent NaN's and Inf's
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
"""Base objects for segmodels_keras."""
|
|
2
|
+
|
|
1
3
|
from typing import Any
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class KerasObject:
|
|
7
|
+
"""Base class for all objects in segmodels_keras."""
|
|
8
|
+
|
|
5
9
|
_backend: Any = None
|
|
6
10
|
_models: Any = None
|
|
7
11
|
_layers: Any = None
|
|
8
12
|
_utils: Any = None
|
|
9
13
|
|
|
10
|
-
def __init__(self, name: str | None = None) -> None:
|
|
14
|
+
def __init__(self, name: str | None = None) -> None: # noqa: D107
|
|
11
15
|
if (
|
|
12
16
|
self.backend is None
|
|
13
17
|
or self.utils is None
|
|
@@ -19,13 +23,13 @@ class KerasObject:
|
|
|
19
23
|
self._name = name
|
|
20
24
|
|
|
21
25
|
@property
|
|
22
|
-
def __name__(self) -> str:
|
|
26
|
+
def __name__(self) -> str: # noqa: D105
|
|
23
27
|
if self._name is None:
|
|
24
28
|
return self.__class__.__name__ # type: ignore[return-value]
|
|
25
29
|
return self._name
|
|
26
30
|
|
|
27
31
|
@property
|
|
28
|
-
def name(self) -> str:
|
|
32
|
+
def name(self) -> str: # noqa: D102
|
|
29
33
|
return self.__name__
|
|
30
34
|
|
|
31
35
|
@name.setter
|
|
@@ -33,14 +37,14 @@ class KerasObject:
|
|
|
33
37
|
self._name = name
|
|
34
38
|
|
|
35
39
|
@classmethod
|
|
36
|
-
def set_submodules(cls, backend: Any, layers: Any, models: Any, utils: Any) -> None:
|
|
40
|
+
def set_submodules(cls, backend: Any, layers: Any, models: Any, utils: Any) -> None: # noqa: D102
|
|
37
41
|
cls._backend = backend
|
|
38
42
|
cls._layers = layers
|
|
39
43
|
cls._models = models
|
|
40
44
|
cls._utils = utils
|
|
41
45
|
|
|
42
46
|
@property
|
|
43
|
-
def submodules(self) -> dict[str, Any]:
|
|
47
|
+
def submodules(self) -> dict[str, Any]: # noqa: D102
|
|
44
48
|
return {
|
|
45
49
|
"backend": self.backend,
|
|
46
50
|
"layers": self.layers,
|
|
@@ -49,48 +53,52 @@ class KerasObject:
|
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
@property
|
|
52
|
-
def backend(self) -> Any:
|
|
56
|
+
def backend(self) -> Any: # noqa: D102
|
|
53
57
|
return self._backend
|
|
54
58
|
|
|
55
59
|
@property
|
|
56
|
-
def layers(self) -> Any:
|
|
60
|
+
def layers(self) -> Any: # noqa: D102
|
|
57
61
|
return self._layers
|
|
58
62
|
|
|
59
63
|
@property
|
|
60
|
-
def models(self) -> Any:
|
|
64
|
+
def models(self) -> Any: # noqa: D102
|
|
61
65
|
return self._models
|
|
62
66
|
|
|
63
67
|
@property
|
|
64
|
-
def utils(self) -> Any:
|
|
68
|
+
def utils(self) -> Any: # noqa: D102
|
|
65
69
|
return self._utils
|
|
66
70
|
|
|
67
71
|
|
|
68
72
|
class Metric(KerasObject):
|
|
69
|
-
|
|
73
|
+
"""Base class for all metrics."""
|
|
70
74
|
|
|
71
75
|
|
|
72
76
|
class Loss(KerasObject):
|
|
73
|
-
|
|
77
|
+
"""Base class for all loss functions."""
|
|
78
|
+
|
|
79
|
+
def __add__(self, other: Any) -> Any: # noqa: D105
|
|
74
80
|
if isinstance(other, Loss):
|
|
75
81
|
return SumOfLosses(self, other)
|
|
76
82
|
else:
|
|
77
83
|
raise ValueError("Loss should be inherited from `Loss` class")
|
|
78
84
|
|
|
79
|
-
def __radd__(self, other: Any) -> Any:
|
|
85
|
+
def __radd__(self, other: Any) -> Any: # noqa: D105
|
|
80
86
|
return self.__add__(other)
|
|
81
87
|
|
|
82
|
-
def __mul__(self, value: int | float) -> Any:
|
|
88
|
+
def __mul__(self, value: int | float) -> Any: # noqa: D105
|
|
83
89
|
if isinstance(value, (int, float)):
|
|
84
90
|
return MultipliedLoss(self, value)
|
|
85
91
|
else:
|
|
86
92
|
raise ValueError("Loss should be inherited from `BaseLoss` class")
|
|
87
93
|
|
|
88
|
-
def __rmul__(self, other: Any) -> Any:
|
|
94
|
+
def __rmul__(self, other: Any) -> Any: # noqa: D105
|
|
89
95
|
return self.__mul__(other)
|
|
90
96
|
|
|
91
97
|
|
|
92
98
|
class MultipliedLoss(Loss):
|
|
93
|
-
|
|
99
|
+
"""Class for multiplying loss by a constant value."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, loss: Any, multiplier: int | float) -> None: # noqa: D107
|
|
94
102
|
|
|
95
103
|
# resolve name
|
|
96
104
|
if len(loss.__name__.split("+")) > 1:
|
|
@@ -101,16 +109,21 @@ class MultipliedLoss(Loss):
|
|
|
101
109
|
self.loss = loss
|
|
102
110
|
self.multiplier = multiplier
|
|
103
111
|
|
|
104
|
-
def __call__(self, gt: Any, pr: Any) -> Any:
|
|
112
|
+
def __call__(self, gt: Any, pr: Any) -> Any: # noqa: D102
|
|
105
113
|
return self.multiplier * self.loss(gt, pr)
|
|
106
114
|
|
|
107
115
|
|
|
108
116
|
class SumOfLosses(Loss):
|
|
109
|
-
|
|
117
|
+
"""Class for summing two losses.
|
|
118
|
+
|
|
119
|
+
For example, you can sum DiceLoss and BinaryCELoss.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
def __init__(self, l1: Any, l2: Any) -> None: # noqa: D107
|
|
110
123
|
name = f"{l1.__name__}_plus_{l2.__name__}"
|
|
111
124
|
super().__init__(name=name)
|
|
112
125
|
self.l1 = l1
|
|
113
126
|
self.l2 = l2
|
|
114
127
|
|
|
115
|
-
def __call__(self, gt: Any, pr: Any) -> Any:
|
|
128
|
+
def __call__(self, gt: Any, pr: Any) -> Any: # noqa: D102
|
|
116
129
|
return self.l1(gt, pr) + self.l2(gt, pr)
|