segmodels-keras 0.1.0.dev6__tar.gz → 0.1.0.dev7__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.dev6 → segmodels_keras-0.1.0.dev7}/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/__init__.py +4 -4
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/_version.py +1 -1
- segmodels_keras-0.1.0.dev7/segmodels_keras/backbones/_common_blocks.py +131 -0
- segmodels_keras-0.1.0.dev7/segmodels_keras/backbones/_weights.py +363 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/backbones/backbones_factory.py +26 -21
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/backbones/inception_v3.py +1 -0
- segmodels_keras-0.1.0.dev7/segmodels_keras/backbones/resnet.py +494 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/base/functional.py +3 -3
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras.egg-info/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras.egg-info/SOURCES.txt +3 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/tests/test_models.py +8 -8
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/LICENSE +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/MANIFEST.in +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/README.rst +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/pyproject.toml +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/_compat.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/backbones/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/backbones/inception_resnet_v2.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/base/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/base/objects.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/losses.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/metrics.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/_common_blocks.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/_utils.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/fpn.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/linknet.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/pspnet.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/unet.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/utils.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras.egg-info/dependency_links.txt +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras.egg-info/requires.txt +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras.egg-info/top_level.txt +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/setup.cfg +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/setup.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/tests/test_backbones.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/tests/test_metrics.py +0 -0
- {segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/tests/test_utils.py +0 -0
|
@@ -59,17 +59,17 @@ def set_framework(name):
|
|
|
59
59
|
name = name.lower()
|
|
60
60
|
|
|
61
61
|
if name == _KERAS_FRAMEWORK_NAME:
|
|
62
|
-
import keras
|
|
62
|
+
import keras # noqa: PLC0415
|
|
63
63
|
elif name == _TF_KERAS_FRAMEWORK_NAME:
|
|
64
|
-
from tensorflow import keras
|
|
64
|
+
from tensorflow import keras # noqa: PLC0415
|
|
65
65
|
else:
|
|
66
66
|
raise ValueError(
|
|
67
67
|
f"Not correct module name `{name}`, use `{_KERAS_FRAMEWORK_NAME}` or "
|
|
68
68
|
f"`{_TF_KERAS_FRAMEWORK_NAME}`"
|
|
69
69
|
)
|
|
70
70
|
|
|
71
|
-
global _KERAS_BACKEND, _KERAS_LAYERS, _KERAS_MODELS
|
|
72
|
-
global _KERAS_UTILS, _KERAS_LOSSES, _KERAS_FRAMEWORK
|
|
71
|
+
global _KERAS_BACKEND, _KERAS_LAYERS, _KERAS_MODELS # noqa: PLW0603
|
|
72
|
+
global _KERAS_UTILS, _KERAS_LOSSES, _KERAS_FRAMEWORK # noqa: PLW0603
|
|
73
73
|
|
|
74
74
|
_KERAS_FRAMEWORK = name
|
|
75
75
|
_KERAS_BACKEND = keras.backend
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__versioninfo__ = (0, 1, 0, "
|
|
1
|
+
__versioninfo__ = (0, 1, 0, "dev7")
|
|
2
2
|
__version__ = ".".join(map(str, __versioninfo__))
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Common blocks for backbone models.
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/qubvel/classification_models
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from keras import backend, layers
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def slice_tensor(x, start, stop, axis):
|
|
10
|
+
if axis == 3:
|
|
11
|
+
return x[:, :, :, start:stop]
|
|
12
|
+
elif axis == 1:
|
|
13
|
+
return x[:, start:stop, :, :]
|
|
14
|
+
else:
|
|
15
|
+
raise ValueError(f"Slice axis should be in (1, 3), got {axis}.")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def GroupConv2D(
|
|
19
|
+
filters,
|
|
20
|
+
kernel_size,
|
|
21
|
+
strides=(1, 1),
|
|
22
|
+
groups=32,
|
|
23
|
+
kernel_initializer="he_uniform",
|
|
24
|
+
use_bias=True,
|
|
25
|
+
activation="linear",
|
|
26
|
+
padding="valid",
|
|
27
|
+
**kwargs, # noqa: ARG001
|
|
28
|
+
):
|
|
29
|
+
"""Grouped Convolution Layer implemented as a Slice, Conv2D and Concatenate layers.
|
|
30
|
+
|
|
31
|
+
Split filters to groups, apply Conv2D and concatenate back.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
filters: Integer, the dimensionality of the output space
|
|
35
|
+
(i.e. the number of output filters in the convolution).
|
|
36
|
+
kernel_size: An integer or tuple/list of a single integer,
|
|
37
|
+
specifying the length of the 1D convolution window.
|
|
38
|
+
strides: An integer or tuple/list of a single integer, specifying the stride
|
|
39
|
+
length of the convolution.
|
|
40
|
+
groups: Integer, number of groups to split input filters to.
|
|
41
|
+
kernel_initializer: Regularizer function applied to the kernel weights matrix.
|
|
42
|
+
use_bias: Boolean, whether the layer uses a bias vector.
|
|
43
|
+
activation: Activation function to use (see activations).
|
|
44
|
+
If you don't specify anything, no activation is applied
|
|
45
|
+
(ie. "linear" activation: a(x) = x).
|
|
46
|
+
padding: one of "valid" or "same" (case-insensitive).
|
|
47
|
+
|
|
48
|
+
Input shape:
|
|
49
|
+
4D tensor with shape: (batch, rows, cols, channels) if data_format is
|
|
50
|
+
"channels_last".
|
|
51
|
+
|
|
52
|
+
Output shape:
|
|
53
|
+
4D tensor with shape: (batch, new_rows, new_cols, filters) if data_format is
|
|
54
|
+
"channels_last". rows and cols values might have changed due to padding.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
slice_axis = 3 if backend.image_data_format() == "channels_last" else 1
|
|
58
|
+
|
|
59
|
+
def layer(input_tensor):
|
|
60
|
+
inp_ch = int(
|
|
61
|
+
backend.int_shape(input_tensor)[-1] // groups
|
|
62
|
+
) # input grouped channels
|
|
63
|
+
out_ch = int(filters // groups) # output grouped channels
|
|
64
|
+
|
|
65
|
+
blocks = []
|
|
66
|
+
for c in range(groups):
|
|
67
|
+
slice_arguments = {
|
|
68
|
+
"start": c * inp_ch,
|
|
69
|
+
"stop": (c + 1) * inp_ch,
|
|
70
|
+
"axis": slice_axis,
|
|
71
|
+
}
|
|
72
|
+
x = layers.Lambda(slice_tensor, arguments=slice_arguments)(input_tensor)
|
|
73
|
+
x = layers.Conv2D(
|
|
74
|
+
out_ch,
|
|
75
|
+
kernel_size,
|
|
76
|
+
strides=strides,
|
|
77
|
+
kernel_initializer=kernel_initializer,
|
|
78
|
+
use_bias=use_bias,
|
|
79
|
+
activation=activation,
|
|
80
|
+
padding=padding,
|
|
81
|
+
)(x)
|
|
82
|
+
blocks.append(x)
|
|
83
|
+
|
|
84
|
+
x = layers.Concatenate(axis=slice_axis)(blocks)
|
|
85
|
+
return x
|
|
86
|
+
|
|
87
|
+
return layer
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def expand_dims(x, channels_axis):
|
|
91
|
+
if channels_axis == 3:
|
|
92
|
+
return x[:, None, None, :]
|
|
93
|
+
elif channels_axis == 1:
|
|
94
|
+
return x[:, :, None, None]
|
|
95
|
+
else:
|
|
96
|
+
raise ValueError(f"Slice axis should be in (1, 3), got {channels_axis}.")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def ChannelSE(reduction=16, **kwargs): # noqa: ARG001
|
|
100
|
+
"""
|
|
101
|
+
Squeeze and Excitation block, reimplementation inspired by
|
|
102
|
+
https://github.com/Cadene/pretrained-models.pytorch/blob/master/pretrainedmodels/models/senet.py
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
reduction: channels squeeze factor
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
channels_axis = 3 if backend.image_data_format() == "channels_last" else 1
|
|
109
|
+
|
|
110
|
+
def layer(input_tensor):
|
|
111
|
+
# get number of channels/filters
|
|
112
|
+
channels = backend.int_shape(input_tensor)[channels_axis]
|
|
113
|
+
|
|
114
|
+
x = input_tensor
|
|
115
|
+
|
|
116
|
+
# squeeze and excitation block in PyTorch style with
|
|
117
|
+
x = layers.GlobalAveragePooling2D()(x)
|
|
118
|
+
x = layers.Lambda(expand_dims, arguments={"channels_axis": channels_axis})(x)
|
|
119
|
+
x = layers.Conv2D(
|
|
120
|
+
channels // reduction, (1, 1), kernel_initializer="he_uniform"
|
|
121
|
+
)(x)
|
|
122
|
+
x = layers.Activation("relu")(x)
|
|
123
|
+
x = layers.Conv2D(channels, (1, 1), kernel_initializer="he_uniform")(x)
|
|
124
|
+
x = layers.Activation("sigmoid")(x)
|
|
125
|
+
|
|
126
|
+
# apply attention
|
|
127
|
+
x = layers.Multiply()([input_tensor, x])
|
|
128
|
+
|
|
129
|
+
return x
|
|
130
|
+
|
|
131
|
+
return layer
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"""Helper to download weights for backbones.
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/qubvel/classification_models
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from keras.utils import get_file
|
|
7
|
+
|
|
8
|
+
__all__ = ["load_model_weights"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _find_weights(model_name, dataset, include_top):
|
|
12
|
+
w = list(filter(lambda x: x["model"] == model_name, WEIGHTS_COLLECTION))
|
|
13
|
+
w = list(filter(lambda x: x["dataset"] == dataset, w))
|
|
14
|
+
w = list(filter(lambda x: x["include_top"] == include_top, w))
|
|
15
|
+
return w
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_model_weights(model, model_name, dataset, classes, include_top):
|
|
19
|
+
|
|
20
|
+
weights = _find_weights(model_name, dataset, include_top)
|
|
21
|
+
|
|
22
|
+
if weights:
|
|
23
|
+
weights = weights[0]
|
|
24
|
+
|
|
25
|
+
if include_top and weights["classes"] != classes:
|
|
26
|
+
raise ValueError(
|
|
27
|
+
"If using `weights` and `include_top`"
|
|
28
|
+
" as true, `classes` should be {}".format(weights["classes"])
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
weights_path = get_file(
|
|
32
|
+
weights["name"],
|
|
33
|
+
weights["url"],
|
|
34
|
+
cache_subdir="models",
|
|
35
|
+
md5_hash=weights["md5"],
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
model.load_weights(weights_path)
|
|
39
|
+
|
|
40
|
+
else:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
"There is no weights for such configuration: "
|
|
43
|
+
f"model = {model.name}, dataset = {dataset}, "
|
|
44
|
+
f"classes = {classes}, include_top = {include_top}."
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
WEIGHTS_COLLECTION = [
|
|
49
|
+
# ResNet18
|
|
50
|
+
{
|
|
51
|
+
"model": "resnet18",
|
|
52
|
+
"dataset": "imagenet",
|
|
53
|
+
"classes": 1000,
|
|
54
|
+
"include_top": True,
|
|
55
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet18_imagenet_1000.h5",
|
|
56
|
+
"name": "resnet18_imagenet_1000.h5",
|
|
57
|
+
"md5": "64da73012bb70e16c901316c201d9803",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"model": "resnet18",
|
|
61
|
+
"dataset": "imagenet",
|
|
62
|
+
"classes": 1000,
|
|
63
|
+
"include_top": False,
|
|
64
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet18_imagenet_1000_no_top.h5",
|
|
65
|
+
"name": "resnet18_imagenet_1000_no_top.h5",
|
|
66
|
+
"md5": "318e3ac0cd98d51e917526c9f62f0b50",
|
|
67
|
+
},
|
|
68
|
+
# ResNet34
|
|
69
|
+
{
|
|
70
|
+
"model": "resnet34",
|
|
71
|
+
"dataset": "imagenet",
|
|
72
|
+
"classes": 1000,
|
|
73
|
+
"include_top": True,
|
|
74
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet34_imagenet_1000.h5",
|
|
75
|
+
"name": "resnet34_imagenet_1000.h5",
|
|
76
|
+
"md5": "2ac8277412f65e5d047f255bcbd10383",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"model": "resnet34",
|
|
80
|
+
"dataset": "imagenet",
|
|
81
|
+
"classes": 1000,
|
|
82
|
+
"include_top": False,
|
|
83
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet34_imagenet_1000_no_top.h5",
|
|
84
|
+
"name": "resnet34_imagenet_1000_no_top.h5",
|
|
85
|
+
"md5": "8caaa0ad39d927cb8ba5385bf945d582",
|
|
86
|
+
},
|
|
87
|
+
# ResNet50
|
|
88
|
+
{
|
|
89
|
+
"model": "resnet50",
|
|
90
|
+
"dataset": "imagenet",
|
|
91
|
+
"classes": 1000,
|
|
92
|
+
"include_top": True,
|
|
93
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet50_imagenet_1000.h5",
|
|
94
|
+
"name": "resnet50_imagenet_1000.h5",
|
|
95
|
+
"md5": "d0feba4fc650e68ac8c19166ee1ba87f",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"model": "resnet50",
|
|
99
|
+
"dataset": "imagenet",
|
|
100
|
+
"classes": 1000,
|
|
101
|
+
"include_top": False,
|
|
102
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet50_imagenet_1000_no_top.h5",
|
|
103
|
+
"name": "resnet50_imagenet_1000_no_top.h5",
|
|
104
|
+
"md5": "db3b217156506944570ac220086f09b6",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"model": "resnet50",
|
|
108
|
+
"dataset": "imagenet11k-places365ch",
|
|
109
|
+
"classes": 11586,
|
|
110
|
+
"include_top": True,
|
|
111
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet50_imagenet11k-places365ch_11586.h5",
|
|
112
|
+
"name": "resnet50_imagenet11k-places365ch_11586.h5",
|
|
113
|
+
"md5": "bb8963db145bc9906452b3d9c9917275",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"model": "resnet50",
|
|
117
|
+
"dataset": "imagenet11k-places365ch",
|
|
118
|
+
"classes": 11586,
|
|
119
|
+
"include_top": False,
|
|
120
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet50_imagenet11k-places365ch_11586_no_top.h5",
|
|
121
|
+
"name": "resnet50_imagenet11k-places365ch_11586_no_top.h5",
|
|
122
|
+
"md5": "d8bf4e7ea082d9d43e37644da217324a",
|
|
123
|
+
},
|
|
124
|
+
# ResNet101
|
|
125
|
+
{
|
|
126
|
+
"model": "resnet101",
|
|
127
|
+
"dataset": "imagenet",
|
|
128
|
+
"classes": 1000,
|
|
129
|
+
"include_top": True,
|
|
130
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet101_imagenet_1000.h5",
|
|
131
|
+
"name": "resnet101_imagenet_1000.h5",
|
|
132
|
+
"md5": "9489ed2d5d0037538134c880167622ad",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"model": "resnet101",
|
|
136
|
+
"dataset": "imagenet",
|
|
137
|
+
"classes": 1000,
|
|
138
|
+
"include_top": False,
|
|
139
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet101_imagenet_1000_no_top.h5",
|
|
140
|
+
"name": "resnet101_imagenet_1000_no_top.h5",
|
|
141
|
+
"md5": "1016e7663980d5597a4e224d915c342d",
|
|
142
|
+
},
|
|
143
|
+
# ResNet152
|
|
144
|
+
{
|
|
145
|
+
"model": "resnet152",
|
|
146
|
+
"dataset": "imagenet",
|
|
147
|
+
"classes": 1000,
|
|
148
|
+
"include_top": True,
|
|
149
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet152_imagenet_1000.h5",
|
|
150
|
+
"name": "resnet152_imagenet_1000.h5",
|
|
151
|
+
"md5": "1efffbcc0708fb0d46a9d096ae14f905",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"model": "resnet152",
|
|
155
|
+
"dataset": "imagenet",
|
|
156
|
+
"classes": 1000,
|
|
157
|
+
"include_top": False,
|
|
158
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet152_imagenet_1000_no_top.h5",
|
|
159
|
+
"name": "resnet152_imagenet_1000_no_top.h5",
|
|
160
|
+
"md5": "5867b94098df4640918941115db93734",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"model": "resnet152",
|
|
164
|
+
"dataset": "imagenet11k",
|
|
165
|
+
"classes": 11221,
|
|
166
|
+
"include_top": True,
|
|
167
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet152_imagenet11k_11221.h5",
|
|
168
|
+
"name": "resnet152_imagenet11k_11221.h5",
|
|
169
|
+
"md5": "24791790f6ef32f274430ce4a2ffee5d",
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"model": "resnet152",
|
|
173
|
+
"dataset": "imagenet11k",
|
|
174
|
+
"classes": 11221,
|
|
175
|
+
"include_top": False,
|
|
176
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnet152_imagenet11k_11221_no_top.h5",
|
|
177
|
+
"name": "resnet152_imagenet11k_11221_no_top.h5",
|
|
178
|
+
"md5": "25ab66dec217cb774a27d0f3659cafb3",
|
|
179
|
+
},
|
|
180
|
+
# ResNeXt50
|
|
181
|
+
{
|
|
182
|
+
"model": "resnext50",
|
|
183
|
+
"dataset": "imagenet",
|
|
184
|
+
"classes": 1000,
|
|
185
|
+
"include_top": True,
|
|
186
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnext50_imagenet_1000.h5",
|
|
187
|
+
"name": "resnext50_imagenet_1000.h5",
|
|
188
|
+
"md5": "7c5c40381efb044a8dea5287ab2c83db",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"model": "resnext50",
|
|
192
|
+
"dataset": "imagenet",
|
|
193
|
+
"classes": 1000,
|
|
194
|
+
"include_top": False,
|
|
195
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnext50_imagenet_1000_no_top.h5",
|
|
196
|
+
"name": "resnext50_imagenet_1000_no_top.h5",
|
|
197
|
+
"md5": "7ade5c8aac9194af79b1724229bdaa50",
|
|
198
|
+
},
|
|
199
|
+
# ResNeXt101
|
|
200
|
+
{
|
|
201
|
+
"model": "resnext101",
|
|
202
|
+
"dataset": "imagenet",
|
|
203
|
+
"classes": 1000,
|
|
204
|
+
"include_top": True,
|
|
205
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnext101_imagenet_1000.h5",
|
|
206
|
+
"name": "resnext101_imagenet_1000.h5",
|
|
207
|
+
"md5": "432536e85ee811568a0851c328182735",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"model": "resnext101",
|
|
211
|
+
"dataset": "imagenet",
|
|
212
|
+
"classes": 1000,
|
|
213
|
+
"include_top": False,
|
|
214
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/resnext101_imagenet_1000_no_top.h5",
|
|
215
|
+
"name": "resnext101_imagenet_1000_no_top.h5",
|
|
216
|
+
"md5": "91fe0126320e49f6ee607a0719828c7e",
|
|
217
|
+
},
|
|
218
|
+
# SE models
|
|
219
|
+
{
|
|
220
|
+
"model": "seresnet50",
|
|
221
|
+
"dataset": "imagenet",
|
|
222
|
+
"classes": 1000,
|
|
223
|
+
"include_top": True,
|
|
224
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet50_imagenet_1000.h5",
|
|
225
|
+
"name": "seresnet50_imagenet_1000.h5",
|
|
226
|
+
"md5": "ff0ce1ed5accaad05d113ecef2d29149",
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"model": "seresnet50",
|
|
230
|
+
"dataset": "imagenet",
|
|
231
|
+
"classes": 1000,
|
|
232
|
+
"include_top": False,
|
|
233
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet50_imagenet_1000_no_top.h5",
|
|
234
|
+
"name": "seresnet50_imagenet_1000_no_top.h5",
|
|
235
|
+
"md5": "043777781b0d5ca756474d60bf115ef1",
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"model": "seresnet101",
|
|
239
|
+
"dataset": "imagenet",
|
|
240
|
+
"classes": 1000,
|
|
241
|
+
"include_top": True,
|
|
242
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet101_imagenet_1000.h5",
|
|
243
|
+
"name": "seresnet101_imagenet_1000.h5",
|
|
244
|
+
"md5": "5c31adee48c82a66a32dee3d442f5be8",
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"model": "seresnet101",
|
|
248
|
+
"dataset": "imagenet",
|
|
249
|
+
"classes": 1000,
|
|
250
|
+
"include_top": False,
|
|
251
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet101_imagenet_1000_no_top.h5",
|
|
252
|
+
"name": "seresnet101_imagenet_1000_no_top.h5",
|
|
253
|
+
"md5": "1c373b0c196918713da86951d1239007",
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"model": "seresnet152",
|
|
257
|
+
"dataset": "imagenet",
|
|
258
|
+
"classes": 1000,
|
|
259
|
+
"include_top": True,
|
|
260
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet152_imagenet_1000.h5",
|
|
261
|
+
"name": "seresnet152_imagenet_1000.h5",
|
|
262
|
+
"md5": "96fc14e3a939d4627b0174a0e80c7371",
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"model": "seresnet152",
|
|
266
|
+
"dataset": "imagenet",
|
|
267
|
+
"classes": 1000,
|
|
268
|
+
"include_top": False,
|
|
269
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet152_imagenet_1000_no_top.h5",
|
|
270
|
+
"name": "seresnet152_imagenet_1000_no_top.h5",
|
|
271
|
+
"md5": "f58d4c1a511c7445ab9a2c2b83ee4e7b",
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"model": "seresnext50",
|
|
275
|
+
"dataset": "imagenet",
|
|
276
|
+
"classes": 1000,
|
|
277
|
+
"include_top": True,
|
|
278
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnext50_imagenet_1000.h5",
|
|
279
|
+
"name": "seresnext50_imagenet_1000.h5",
|
|
280
|
+
"md5": "5310dcd58ed573aecdab99f8df1121d5",
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"model": "seresnext50",
|
|
284
|
+
"dataset": "imagenet",
|
|
285
|
+
"classes": 1000,
|
|
286
|
+
"include_top": False,
|
|
287
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnext50_imagenet_1000_no_top.h5",
|
|
288
|
+
"name": "seresnext50_imagenet_1000_no_top.h5",
|
|
289
|
+
"md5": "b0f23d2e1cd406d67335fb92d85cc279",
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"model": "seresnext101",
|
|
293
|
+
"dataset": "imagenet",
|
|
294
|
+
"classes": 1000,
|
|
295
|
+
"include_top": True,
|
|
296
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnext101_imagenet_1000.h5",
|
|
297
|
+
"name": "seresnext101_imagenet_1000.h5",
|
|
298
|
+
"md5": "be5b26b697a0f7f11efaa1bb6272fc84",
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"model": "seresnext101",
|
|
302
|
+
"dataset": "imagenet",
|
|
303
|
+
"classes": 1000,
|
|
304
|
+
"include_top": False,
|
|
305
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnext101_imagenet_1000_no_top.h5",
|
|
306
|
+
"name": "seresnext101_imagenet_1000_no_top.h5",
|
|
307
|
+
"md5": "e48708cbe40071cc3356016c37f6c9c7",
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"model": "senet154",
|
|
311
|
+
"dataset": "imagenet",
|
|
312
|
+
"classes": 1000,
|
|
313
|
+
"include_top": True,
|
|
314
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/senet154_imagenet_1000.h5",
|
|
315
|
+
"name": "senet154_imagenet_1000.h5",
|
|
316
|
+
"md5": "c8eac0e1940ea4d8a2e0b2eb0cdf4e75",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"model": "senet154",
|
|
320
|
+
"dataset": "imagenet",
|
|
321
|
+
"classes": 1000,
|
|
322
|
+
"include_top": False,
|
|
323
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/senet154_imagenet_1000_no_top.h5",
|
|
324
|
+
"name": "senet154_imagenet_1000_no_top.h5",
|
|
325
|
+
"md5": "d854ff2cd7e6a87b05a8124cd283e0f2",
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"model": "seresnet18",
|
|
329
|
+
"dataset": "imagenet",
|
|
330
|
+
"classes": 1000,
|
|
331
|
+
"include_top": True,
|
|
332
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet18_imagenet_1000.h5",
|
|
333
|
+
"name": "seresnet18_imagenet_1000.h5",
|
|
334
|
+
"md5": "9a925fd96d050dbf7cc4c54aabfcf749",
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"model": "seresnet18",
|
|
338
|
+
"dataset": "imagenet",
|
|
339
|
+
"classes": 1000,
|
|
340
|
+
"include_top": False,
|
|
341
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet18_imagenet_1000_no_top.h5",
|
|
342
|
+
"name": "seresnet18_imagenet_1000_no_top.h5",
|
|
343
|
+
"md5": "a46e5cd4114ac946ecdc58741e8d92ea",
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"model": "seresnet34",
|
|
347
|
+
"dataset": "imagenet",
|
|
348
|
+
"classes": 1000,
|
|
349
|
+
"include_top": True,
|
|
350
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet34_imagenet_1000.h5",
|
|
351
|
+
"name": "seresnet34_imagenet_1000.h5",
|
|
352
|
+
"md5": "863976b3bd439ff0cc05c91821218a6b",
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
"model": "seresnet34",
|
|
356
|
+
"dataset": "imagenet",
|
|
357
|
+
"classes": 1000,
|
|
358
|
+
"include_top": False,
|
|
359
|
+
"url": "https://github.com/qubvel/classification_models/releases/download/0.0.1/seresnet34_imagenet_1000_no_top.h5",
|
|
360
|
+
"name": "seresnet34_imagenet_1000_no_top.h5",
|
|
361
|
+
"md5": "3348fd049f1f9ad307c070ff2b6ec4cb",
|
|
362
|
+
},
|
|
363
|
+
]
|
|
@@ -2,6 +2,7 @@ import keras.applications as ka
|
|
|
2
2
|
|
|
3
3
|
from . import inception_resnet_v2 as irv2
|
|
4
4
|
from . import inception_v3 as iv3
|
|
5
|
+
from . import resnet
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class BackbonesFactory:
|
|
@@ -16,39 +17,37 @@ class BackbonesFactory:
|
|
|
16
17
|
"""
|
|
17
18
|
|
|
18
19
|
_models = { # noqa: RUF012
|
|
19
|
-
# ResNets
|
|
20
|
+
# ResNets < 50 layers are NOT available via keras.applications, so use
|
|
21
|
+
# implementation from classification_models.
|
|
22
|
+
"resnet18": (
|
|
23
|
+
resnet.ResNet18,
|
|
24
|
+
resnet.preprocess_input,
|
|
25
|
+
("stage4_unit1_relu1", "stage3_unit1_relu1", "stage2_unit1_relu1", "relu0"),
|
|
26
|
+
),
|
|
27
|
+
"resnet34": (
|
|
28
|
+
resnet.ResNet34,
|
|
29
|
+
resnet.preprocess_input,
|
|
30
|
+
("stage4_unit1_relu1", "stage3_unit1_relu1", "stage2_unit1_relu1", "relu0"),
|
|
31
|
+
),
|
|
32
|
+
# ResNets > 50 layers are available via keras.applications, so use those.
|
|
33
|
+
# Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
|
|
20
34
|
"resnet50": (
|
|
21
35
|
ka.ResNet50,
|
|
22
36
|
ka.resnet.preprocess_input,
|
|
23
|
-
(
|
|
24
|
-
"conv4_block6_out",
|
|
25
|
-
"conv3_block4_out",
|
|
26
|
-
"conv2_block3_out",
|
|
27
|
-
"conv1_relu",
|
|
28
|
-
),
|
|
37
|
+
("conv4_block6_out", "conv3_block4_out", "conv2_block3_out", "conv1_relu"),
|
|
29
38
|
),
|
|
30
39
|
"resnet101": (
|
|
31
40
|
ka.ResNet101,
|
|
32
41
|
ka.resnet.preprocess_input,
|
|
33
|
-
(
|
|
34
|
-
"conv4_block23_out",
|
|
35
|
-
"conv3_block4_out",
|
|
36
|
-
"conv2_block3_out",
|
|
37
|
-
"conv1_relu",
|
|
38
|
-
),
|
|
42
|
+
("conv4_block23_out", "conv3_block4_out", "conv2_block3_out", "conv1_relu"),
|
|
39
43
|
),
|
|
40
44
|
"resnet152": (
|
|
41
45
|
ka.ResNet152,
|
|
42
46
|
ka.resnet.preprocess_input,
|
|
43
|
-
(
|
|
44
|
-
"conv4_block36_out",
|
|
45
|
-
"conv3_block8_out",
|
|
46
|
-
"conv2_block3_out",
|
|
47
|
-
"conv1_relu",
|
|
48
|
-
),
|
|
47
|
+
("conv4_block36_out", "conv3_block8_out", "conv2_block3_out", "conv1_relu"),
|
|
49
48
|
),
|
|
50
49
|
# ResNetV2
|
|
51
|
-
#
|
|
50
|
+
# Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
|
|
52
51
|
"resnet50v2": (
|
|
53
52
|
ka.ResNet50V2,
|
|
54
53
|
ka.resnet_v2.preprocess_input,
|
|
@@ -80,6 +79,7 @@ class BackbonesFactory:
|
|
|
80
79
|
),
|
|
81
80
|
),
|
|
82
81
|
# VGG
|
|
82
|
+
# Skip layers from segmentation_models
|
|
83
83
|
"vgg16": (
|
|
84
84
|
ka.vgg16.VGG16,
|
|
85
85
|
ka.vgg16.preprocess_input,
|
|
@@ -103,6 +103,7 @@ class BackbonesFactory:
|
|
|
103
103
|
),
|
|
104
104
|
),
|
|
105
105
|
# DenseNet
|
|
106
|
+
# Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
|
|
106
107
|
"densenet121": (
|
|
107
108
|
ka.densenet.DenseNet121,
|
|
108
109
|
ka.densenet.preprocess_input,
|
|
@@ -122,6 +123,7 @@ class BackbonesFactory:
|
|
|
122
123
|
("pool4_conv", "pool3_conv", "pool2_conv", "conv1/relu"),
|
|
123
124
|
),
|
|
124
125
|
# Inception
|
|
126
|
+
# Skip layers from segmentation_models
|
|
125
127
|
"inceptionresnetv2": (
|
|
126
128
|
irv2.InceptionResNetV2,
|
|
127
129
|
irv2.preprocess_input,
|
|
@@ -138,6 +140,7 @@ class BackbonesFactory:
|
|
|
138
140
|
),
|
|
139
141
|
"inceptionv3": (iv3.InceptionV3, iv3.preprocess_input, (228, 86, 16, 9)),
|
|
140
142
|
# MobileNet
|
|
143
|
+
# Skip layers from segmentation_models
|
|
141
144
|
"mobilenet": (
|
|
142
145
|
ka.mobilenet.MobileNet,
|
|
143
146
|
ka.mobilenet.preprocess_input,
|
|
@@ -158,6 +161,8 @@ class BackbonesFactory:
|
|
|
158
161
|
"block_1_expand_relu",
|
|
159
162
|
),
|
|
160
163
|
),
|
|
164
|
+
# EfficientNet
|
|
165
|
+
# Skip layers from segmentation_models
|
|
161
166
|
"efficientnetb0": [
|
|
162
167
|
ka.EfficientNetB0,
|
|
163
168
|
ka.efficientnet.preprocess_input,
|
|
@@ -239,7 +244,7 @@ class BackbonesFactory:
|
|
|
239
244
|
),
|
|
240
245
|
],
|
|
241
246
|
# EfficientNetV2
|
|
242
|
-
#
|
|
247
|
+
# Skip layers from https://github.com/chinefed/segmentation_models_fork
|
|
243
248
|
"efficientnetv2m": (
|
|
244
249
|
ka.EfficientNetV2M,
|
|
245
250
|
ka.efficientnet_v2.preprocess_input,
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"""Resnet keras models.
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/qubvel/classification_models
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import collections
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
from keras import backend, layers, models
|
|
10
|
+
from keras.utils import get_source_inputs
|
|
11
|
+
|
|
12
|
+
from ._common_blocks import ChannelSE
|
|
13
|
+
from ._weights import load_model_weights
|
|
14
|
+
|
|
15
|
+
ModelParams = collections.namedtuple(
|
|
16
|
+
"ModelParams", ["model_name", "repetitions", "residual_block", "attention"]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
BASE_WEIGHT_URL = (
|
|
20
|
+
"https://github.com/qubvel/classification_models/releases/download/0.0.1/"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# -------------------------------------------------------------------------
|
|
24
|
+
# Helpers functions
|
|
25
|
+
# -------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def handle_block_names(stage, block):
|
|
29
|
+
name_base = f"stage{stage + 1}_unit{block + 1}_"
|
|
30
|
+
conv_name = name_base + "conv"
|
|
31
|
+
bn_name = name_base + "bn"
|
|
32
|
+
relu_name = name_base + "relu"
|
|
33
|
+
sc_name = name_base + "sc"
|
|
34
|
+
return conv_name, bn_name, relu_name, sc_name
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_conv_params(**params):
|
|
38
|
+
default_conv_params = {
|
|
39
|
+
"kernel_initializer": "he_uniform",
|
|
40
|
+
"use_bias": False,
|
|
41
|
+
"padding": "valid",
|
|
42
|
+
}
|
|
43
|
+
default_conv_params.update(params)
|
|
44
|
+
return default_conv_params
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_bn_params(**params):
|
|
48
|
+
axis = 3 if backend.image_data_format() == "channels_last" else 1
|
|
49
|
+
default_bn_params = {
|
|
50
|
+
"axis": axis,
|
|
51
|
+
"momentum": 0.99,
|
|
52
|
+
"epsilon": 2e-5,
|
|
53
|
+
"center": True,
|
|
54
|
+
"scale": True,
|
|
55
|
+
}
|
|
56
|
+
default_bn_params.update(params)
|
|
57
|
+
return default_bn_params
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# -------------------------------------------------------------------------
|
|
61
|
+
# Residual blocks
|
|
62
|
+
# -------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def residual_conv_block(
|
|
66
|
+
filters, stage, block, strides=(1, 1), attention=None, cut="pre"
|
|
67
|
+
):
|
|
68
|
+
"""The identity block is the block that has no conv layer at shortcut.
|
|
69
|
+
# Arguments
|
|
70
|
+
input_tensor: input tensor
|
|
71
|
+
kernel_size: default 3, the kernel size of
|
|
72
|
+
middle conv layer at main path
|
|
73
|
+
filters: list of integers, the filters of 3 conv layer at main path
|
|
74
|
+
stage: integer, current stage label, used for generating layer names
|
|
75
|
+
block: 'a','b'..., current block label, used for generating layer names
|
|
76
|
+
cut: one of 'pre', 'post'. used to decide where skip connection is taken
|
|
77
|
+
# Returns
|
|
78
|
+
Output tensor for the block.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def layer(input_tensor):
|
|
82
|
+
|
|
83
|
+
# get params and names of layers
|
|
84
|
+
conv_params = get_conv_params()
|
|
85
|
+
bn_params = get_bn_params()
|
|
86
|
+
conv_name, bn_name, relu_name, sc_name = handle_block_names(stage, block)
|
|
87
|
+
|
|
88
|
+
x = layers.BatchNormalization(name=bn_name + "1", **bn_params)(input_tensor)
|
|
89
|
+
x = layers.Activation("relu", name=relu_name + "1")(x)
|
|
90
|
+
|
|
91
|
+
# defining shortcut connection
|
|
92
|
+
if cut == "pre":
|
|
93
|
+
shortcut = input_tensor
|
|
94
|
+
elif cut == "post":
|
|
95
|
+
shortcut = layers.Conv2D(
|
|
96
|
+
filters, (1, 1), name=sc_name, strides=strides, **conv_params
|
|
97
|
+
)(x)
|
|
98
|
+
else:
|
|
99
|
+
raise ValueError('Cut type not in ["pre", "post"]')
|
|
100
|
+
|
|
101
|
+
# continue with convolution layers
|
|
102
|
+
x = layers.ZeroPadding2D(padding=(1, 1))(x)
|
|
103
|
+
x = layers.Conv2D(
|
|
104
|
+
filters, (3, 3), strides=strides, name=conv_name + "1", **conv_params
|
|
105
|
+
)(x)
|
|
106
|
+
|
|
107
|
+
x = layers.BatchNormalization(name=bn_name + "2", **bn_params)(x)
|
|
108
|
+
x = layers.Activation("relu", name=relu_name + "2")(x)
|
|
109
|
+
x = layers.ZeroPadding2D(padding=(1, 1))(x)
|
|
110
|
+
x = layers.Conv2D(filters, (3, 3), name=conv_name + "2", **conv_params)(x)
|
|
111
|
+
|
|
112
|
+
# use attention block if defined
|
|
113
|
+
if attention is not None:
|
|
114
|
+
x = attention(x)
|
|
115
|
+
|
|
116
|
+
# add residual connection
|
|
117
|
+
x = layers.Add()([x, shortcut])
|
|
118
|
+
return x
|
|
119
|
+
|
|
120
|
+
return layer
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def residual_bottleneck_block(
|
|
124
|
+
filters, stage, block, strides=None, attention=None, cut="pre"
|
|
125
|
+
):
|
|
126
|
+
"""The identity block is the block that has no conv layer at shortcut.
|
|
127
|
+
# Arguments
|
|
128
|
+
input_tensor: input tensor
|
|
129
|
+
kernel_size: default 3, the kernel size of
|
|
130
|
+
middle conv layer at main path
|
|
131
|
+
filters: list of integers, the filters of 3 conv layer at main path
|
|
132
|
+
stage: integer, current stage label, used for generating layer names
|
|
133
|
+
block: 'a','b'..., current block label, used for generating layer names
|
|
134
|
+
cut: one of 'pre', 'post'. used to decide where skip connection is taken
|
|
135
|
+
# Returns
|
|
136
|
+
Output tensor for the block.
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
def layer(input_tensor):
|
|
140
|
+
|
|
141
|
+
# get params and names of layers
|
|
142
|
+
conv_params = get_conv_params()
|
|
143
|
+
bn_params = get_bn_params()
|
|
144
|
+
conv_name, bn_name, relu_name, sc_name = handle_block_names(stage, block)
|
|
145
|
+
|
|
146
|
+
x = layers.BatchNormalization(name=bn_name + "1", **bn_params)(input_tensor)
|
|
147
|
+
x = layers.Activation("relu", name=relu_name + "1")(x)
|
|
148
|
+
|
|
149
|
+
# defining shortcut connection
|
|
150
|
+
if cut == "pre":
|
|
151
|
+
shortcut = input_tensor
|
|
152
|
+
elif cut == "post":
|
|
153
|
+
shortcut = layers.Conv2D(
|
|
154
|
+
filters * 4, (1, 1), name=sc_name, strides=strides, **conv_params
|
|
155
|
+
)(x)
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError('Cut type not in ["pre", "post"]')
|
|
158
|
+
|
|
159
|
+
# continue with convolution layers
|
|
160
|
+
x = layers.Conv2D(filters, (1, 1), name=conv_name + "1", **conv_params)(x)
|
|
161
|
+
|
|
162
|
+
x = layers.BatchNormalization(name=bn_name + "2", **bn_params)(x)
|
|
163
|
+
x = layers.Activation("relu", name=relu_name + "2")(x)
|
|
164
|
+
x = layers.ZeroPadding2D(padding=(1, 1))(x)
|
|
165
|
+
x = layers.Conv2D(
|
|
166
|
+
filters, (3, 3), strides=strides, name=conv_name + "2", **conv_params
|
|
167
|
+
)(x)
|
|
168
|
+
|
|
169
|
+
x = layers.BatchNormalization(name=bn_name + "3", **bn_params)(x)
|
|
170
|
+
x = layers.Activation("relu", name=relu_name + "3")(x)
|
|
171
|
+
x = layers.Conv2D(filters * 4, (1, 1), name=conv_name + "3", **conv_params)(x)
|
|
172
|
+
|
|
173
|
+
# use attention block if defined
|
|
174
|
+
if attention is not None:
|
|
175
|
+
x = attention(x)
|
|
176
|
+
|
|
177
|
+
# add residual connection
|
|
178
|
+
x = layers.Add()([x, shortcut])
|
|
179
|
+
|
|
180
|
+
return x
|
|
181
|
+
|
|
182
|
+
return layer
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# -------------------------------------------------------------------------
|
|
186
|
+
# Residual Model Builder
|
|
187
|
+
# -------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def ResNet(
|
|
191
|
+
model_params,
|
|
192
|
+
input_shape=None,
|
|
193
|
+
input_tensor=None,
|
|
194
|
+
include_top=True,
|
|
195
|
+
classes=1000,
|
|
196
|
+
weights="imagenet",
|
|
197
|
+
**kwargs,
|
|
198
|
+
):
|
|
199
|
+
"""Instantiates the ResNet, SEResNet architecture.
|
|
200
|
+
Optionally loads weights pre-trained on ImageNet.
|
|
201
|
+
Note that the data format convention used by the model is
|
|
202
|
+
the one specified in your Keras config at `~/.keras/keras.json`.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
include_top: whether to include the fully-connected
|
|
206
|
+
layer at the top of the network.
|
|
207
|
+
weights: one of `None` (random initialization),
|
|
208
|
+
'imagenet' (pre-training on ImageNet),
|
|
209
|
+
or the path to the weights file to be loaded.
|
|
210
|
+
input_tensor: optional Keras tensor
|
|
211
|
+
(i.e. output of `layers.Input()`)
|
|
212
|
+
to use as image input for the model.
|
|
213
|
+
input_shape: optional shape tuple, only to be specified
|
|
214
|
+
if `include_top` is False (otherwise the input shape
|
|
215
|
+
has to be `(224, 224, 3)` (with `channels_last` data format)
|
|
216
|
+
or `(3, 224, 224)` (with `channels_first` data format).
|
|
217
|
+
It should have exactly 3 inputs channels.
|
|
218
|
+
classes: optional number of classes to classify images
|
|
219
|
+
into, only to be specified if `include_top` is True, and
|
|
220
|
+
if no `weights` argument is specified.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
A Keras model instance.
|
|
224
|
+
|
|
225
|
+
Raises:
|
|
226
|
+
ValueError: in case of invalid argument for `weights`,
|
|
227
|
+
or invalid input shape.
|
|
228
|
+
"""
|
|
229
|
+
if input_tensor is None:
|
|
230
|
+
img_input = layers.Input(shape=input_shape, name="data")
|
|
231
|
+
else:
|
|
232
|
+
if not backend.is_keras_tensor(input_tensor):
|
|
233
|
+
img_input = layers.Input(tensor=input_tensor, shape=input_shape)
|
|
234
|
+
else:
|
|
235
|
+
img_input = input_tensor
|
|
236
|
+
|
|
237
|
+
# choose residual block type
|
|
238
|
+
ResidualBlock = model_params.residual_block
|
|
239
|
+
if model_params.attention:
|
|
240
|
+
Attention = model_params.attention(**kwargs)
|
|
241
|
+
else:
|
|
242
|
+
Attention = None
|
|
243
|
+
|
|
244
|
+
# get parameters for model layers
|
|
245
|
+
no_scale_bn_params = get_bn_params(scale=False)
|
|
246
|
+
bn_params = get_bn_params()
|
|
247
|
+
conv_params = get_conv_params()
|
|
248
|
+
init_filters = 64
|
|
249
|
+
|
|
250
|
+
# resnet bottom
|
|
251
|
+
x = layers.BatchNormalization(name="bn_data", **no_scale_bn_params)(img_input)
|
|
252
|
+
x = layers.ZeroPadding2D(padding=(3, 3))(x)
|
|
253
|
+
x = layers.Conv2D(
|
|
254
|
+
init_filters, (7, 7), strides=(2, 2), name="conv0", **conv_params
|
|
255
|
+
)(x)
|
|
256
|
+
x = layers.BatchNormalization(name="bn0", **bn_params)(x)
|
|
257
|
+
x = layers.Activation("relu", name="relu0")(x)
|
|
258
|
+
x = layers.ZeroPadding2D(padding=(1, 1))(x)
|
|
259
|
+
x = layers.MaxPooling2D((3, 3), strides=(2, 2), padding="valid", name="pooling0")(x)
|
|
260
|
+
|
|
261
|
+
# resnet body
|
|
262
|
+
for stage, rep in enumerate(model_params.repetitions):
|
|
263
|
+
for block in range(rep):
|
|
264
|
+
filters = init_filters * (2**stage)
|
|
265
|
+
|
|
266
|
+
# first block of first stage without strides because we have maxpooling
|
|
267
|
+
# before
|
|
268
|
+
if block == 0 and stage == 0:
|
|
269
|
+
x = ResidualBlock(
|
|
270
|
+
filters,
|
|
271
|
+
stage,
|
|
272
|
+
block,
|
|
273
|
+
strides=(1, 1),
|
|
274
|
+
cut="post",
|
|
275
|
+
attention=Attention,
|
|
276
|
+
)(x)
|
|
277
|
+
|
|
278
|
+
elif block == 0:
|
|
279
|
+
x = ResidualBlock(
|
|
280
|
+
filters,
|
|
281
|
+
stage,
|
|
282
|
+
block,
|
|
283
|
+
strides=(2, 2),
|
|
284
|
+
cut="post",
|
|
285
|
+
attention=Attention,
|
|
286
|
+
)(x)
|
|
287
|
+
|
|
288
|
+
else:
|
|
289
|
+
x = ResidualBlock(
|
|
290
|
+
filters,
|
|
291
|
+
stage,
|
|
292
|
+
block,
|
|
293
|
+
strides=(1, 1),
|
|
294
|
+
cut="pre",
|
|
295
|
+
attention=Attention,
|
|
296
|
+
)(x)
|
|
297
|
+
|
|
298
|
+
x = layers.BatchNormalization(name="bn1", **bn_params)(x)
|
|
299
|
+
x = layers.Activation("relu", name="relu1")(x)
|
|
300
|
+
|
|
301
|
+
# resnet top
|
|
302
|
+
if include_top:
|
|
303
|
+
x = layers.GlobalAveragePooling2D(name="pool1")(x)
|
|
304
|
+
x = layers.Dense(classes, name="fc1")(x)
|
|
305
|
+
x = layers.Activation("softmax", name="softmax")(x)
|
|
306
|
+
|
|
307
|
+
# Ensure that the model takes into account any potential predecessors of
|
|
308
|
+
# `input_tensor`.
|
|
309
|
+
if input_tensor is not None:
|
|
310
|
+
inputs = get_source_inputs(input_tensor)
|
|
311
|
+
else:
|
|
312
|
+
inputs = img_input
|
|
313
|
+
|
|
314
|
+
# Create model.
|
|
315
|
+
model = models.Model(inputs, x)
|
|
316
|
+
|
|
317
|
+
if weights:
|
|
318
|
+
if isinstance(weights, str) and os.path.exists(weights):
|
|
319
|
+
model.load_weights(weights)
|
|
320
|
+
else:
|
|
321
|
+
load_model_weights(
|
|
322
|
+
model, model_params.model_name, weights, classes, include_top, **kwargs
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return model
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# -------------------------------------------------------------------------
|
|
329
|
+
# Residual Models
|
|
330
|
+
# -------------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
MODELS_PARAMS = {
|
|
333
|
+
"resnet18": ModelParams("resnet18", (2, 2, 2, 2), residual_conv_block, None),
|
|
334
|
+
"resnet34": ModelParams("resnet34", (3, 4, 6, 3), residual_conv_block, None),
|
|
335
|
+
"resnet50": ModelParams("resnet50", (3, 4, 6, 3), residual_bottleneck_block, None),
|
|
336
|
+
"resnet101": ModelParams(
|
|
337
|
+
"resnet101", (3, 4, 23, 3), residual_bottleneck_block, None
|
|
338
|
+
),
|
|
339
|
+
"resnet152": ModelParams(
|
|
340
|
+
"resnet152", (3, 8, 36, 3), residual_bottleneck_block, None
|
|
341
|
+
),
|
|
342
|
+
"seresnet18": ModelParams(
|
|
343
|
+
"seresnet18", (2, 2, 2, 2), residual_conv_block, ChannelSE
|
|
344
|
+
),
|
|
345
|
+
"seresnet34": ModelParams(
|
|
346
|
+
"seresnet34", (3, 4, 6, 3), residual_conv_block, ChannelSE
|
|
347
|
+
),
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def ResNet18(
|
|
352
|
+
input_shape=None,
|
|
353
|
+
input_tensor=None,
|
|
354
|
+
weights=None,
|
|
355
|
+
classes=1000,
|
|
356
|
+
include_top=True,
|
|
357
|
+
**kwargs,
|
|
358
|
+
):
|
|
359
|
+
return ResNet(
|
|
360
|
+
MODELS_PARAMS["resnet18"],
|
|
361
|
+
input_shape=input_shape,
|
|
362
|
+
input_tensor=input_tensor,
|
|
363
|
+
include_top=include_top,
|
|
364
|
+
classes=classes,
|
|
365
|
+
weights=weights,
|
|
366
|
+
**kwargs,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def ResNet34(
|
|
371
|
+
input_shape=None,
|
|
372
|
+
input_tensor=None,
|
|
373
|
+
weights=None,
|
|
374
|
+
classes=1000,
|
|
375
|
+
include_top=True,
|
|
376
|
+
**kwargs,
|
|
377
|
+
):
|
|
378
|
+
return ResNet(
|
|
379
|
+
MODELS_PARAMS["resnet34"],
|
|
380
|
+
input_shape=input_shape,
|
|
381
|
+
input_tensor=input_tensor,
|
|
382
|
+
include_top=include_top,
|
|
383
|
+
classes=classes,
|
|
384
|
+
weights=weights,
|
|
385
|
+
**kwargs,
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def ResNet50(
|
|
390
|
+
input_shape=None,
|
|
391
|
+
input_tensor=None,
|
|
392
|
+
weights=None,
|
|
393
|
+
classes=1000,
|
|
394
|
+
include_top=True,
|
|
395
|
+
**kwargs,
|
|
396
|
+
):
|
|
397
|
+
return ResNet(
|
|
398
|
+
MODELS_PARAMS["resnet50"],
|
|
399
|
+
input_shape=input_shape,
|
|
400
|
+
input_tensor=input_tensor,
|
|
401
|
+
include_top=include_top,
|
|
402
|
+
classes=classes,
|
|
403
|
+
weights=weights,
|
|
404
|
+
**kwargs,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def ResNet101(
|
|
409
|
+
input_shape=None,
|
|
410
|
+
input_tensor=None,
|
|
411
|
+
weights=None,
|
|
412
|
+
classes=1000,
|
|
413
|
+
include_top=True,
|
|
414
|
+
**kwargs,
|
|
415
|
+
):
|
|
416
|
+
return ResNet(
|
|
417
|
+
MODELS_PARAMS["resnet101"],
|
|
418
|
+
input_shape=input_shape,
|
|
419
|
+
input_tensor=input_tensor,
|
|
420
|
+
include_top=include_top,
|
|
421
|
+
classes=classes,
|
|
422
|
+
weights=weights,
|
|
423
|
+
**kwargs,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def ResNet152(
|
|
428
|
+
input_shape=None,
|
|
429
|
+
input_tensor=None,
|
|
430
|
+
weights=None,
|
|
431
|
+
classes=1000,
|
|
432
|
+
include_top=True,
|
|
433
|
+
**kwargs,
|
|
434
|
+
):
|
|
435
|
+
return ResNet(
|
|
436
|
+
MODELS_PARAMS["resnet152"],
|
|
437
|
+
input_shape=input_shape,
|
|
438
|
+
input_tensor=input_tensor,
|
|
439
|
+
include_top=include_top,
|
|
440
|
+
classes=classes,
|
|
441
|
+
weights=weights,
|
|
442
|
+
**kwargs,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def SEResNet18(
|
|
447
|
+
input_shape=None,
|
|
448
|
+
input_tensor=None,
|
|
449
|
+
weights=None,
|
|
450
|
+
classes=1000,
|
|
451
|
+
include_top=True,
|
|
452
|
+
**kwargs,
|
|
453
|
+
):
|
|
454
|
+
return ResNet(
|
|
455
|
+
MODELS_PARAMS["seresnet18"],
|
|
456
|
+
input_shape=input_shape,
|
|
457
|
+
input_tensor=input_tensor,
|
|
458
|
+
include_top=include_top,
|
|
459
|
+
classes=classes,
|
|
460
|
+
weights=weights,
|
|
461
|
+
**kwargs,
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def SEResNet34(
|
|
466
|
+
input_shape=None,
|
|
467
|
+
input_tensor=None,
|
|
468
|
+
weights=None,
|
|
469
|
+
classes=1000,
|
|
470
|
+
include_top=True,
|
|
471
|
+
**kwargs,
|
|
472
|
+
):
|
|
473
|
+
return ResNet(
|
|
474
|
+
MODELS_PARAMS["seresnet34"],
|
|
475
|
+
input_shape=input_shape,
|
|
476
|
+
input_tensor=input_tensor,
|
|
477
|
+
include_top=include_top,
|
|
478
|
+
classes=classes,
|
|
479
|
+
weights=weights,
|
|
480
|
+
**kwargs,
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def preprocess_input(x, **kwargs): # noqa: ARG001
|
|
485
|
+
return x
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
ResNet18.__doc__ = ResNet.__doc__
|
|
489
|
+
ResNet34.__doc__ = ResNet.__doc__
|
|
490
|
+
ResNet50.__doc__ = ResNet.__doc__
|
|
491
|
+
ResNet101.__doc__ = ResNet.__doc__
|
|
492
|
+
ResNet152.__doc__ = ResNet.__doc__
|
|
493
|
+
SEResNet18.__doc__ = ResNet.__doc__
|
|
494
|
+
SEResNet34.__doc__ = ResNet.__doc__
|
{segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/base/functional.py
RENAMED
|
@@ -60,7 +60,7 @@ def round_if_needed(x, threshold, **kwargs):
|
|
|
60
60
|
return x
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def average(x, per_image=False, class_weights=None, **kwargs):
|
|
63
|
+
def average(x, per_image=False, class_weights=None, **kwargs): # noqa: ARG001
|
|
64
64
|
if per_image:
|
|
65
65
|
x = ops.mean(x, axis=0)
|
|
66
66
|
if class_weights is not None:
|
|
@@ -308,7 +308,7 @@ def categorical_crossentropy(gt, pr, class_weights=1.0, class_indexes=None, **kw
|
|
|
308
308
|
return -ops.mean(output)
|
|
309
309
|
|
|
310
310
|
|
|
311
|
-
def binary_crossentropy(gt, pr, **kwargs):
|
|
311
|
+
def binary_crossentropy(gt, pr, **kwargs): # noqa: ARG001
|
|
312
312
|
return ops.mean(ops.binary_crossentropy(gt, pr))
|
|
313
313
|
|
|
314
314
|
|
|
@@ -338,7 +338,7 @@ def categorical_focal_loss(gt, pr, gamma=2.0, alpha=0.25, class_indexes=None, **
|
|
|
338
338
|
return ops.mean(loss)
|
|
339
339
|
|
|
340
340
|
|
|
341
|
-
def binary_focal_loss(gt, pr, gamma=2.0, alpha=0.25, **kwargs):
|
|
341
|
+
def binary_focal_loss(gt, pr, gamma=2.0, alpha=0.25, **kwargs): # noqa: ARG001
|
|
342
342
|
r"""Implementation of Focal Loss from the paper in binary classification
|
|
343
343
|
|
|
344
344
|
Formula:
|
{segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras.egg-info/SOURCES.txt
RENAMED
|
@@ -15,9 +15,12 @@ segmodels_keras.egg-info/dependency_links.txt
|
|
|
15
15
|
segmodels_keras.egg-info/requires.txt
|
|
16
16
|
segmodels_keras.egg-info/top_level.txt
|
|
17
17
|
segmodels_keras/backbones/__init__.py
|
|
18
|
+
segmodels_keras/backbones/_common_blocks.py
|
|
19
|
+
segmodels_keras/backbones/_weights.py
|
|
18
20
|
segmodels_keras/backbones/backbones_factory.py
|
|
19
21
|
segmodels_keras/backbones/inception_resnet_v2.py
|
|
20
22
|
segmodels_keras/backbones/inception_v3.py
|
|
23
|
+
segmodels_keras/backbones/resnet.py
|
|
21
24
|
segmodels_keras/base/__init__.py
|
|
22
25
|
segmodels_keras/base/functional.py
|
|
23
26
|
segmodels_keras/base/objects.py
|
|
@@ -20,13 +20,7 @@ def str2bool(v):
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def get_backbones():
|
|
23
|
-
|
|
24
|
-
exclude = ["efficientnetb6", "efficientnetb7"]
|
|
25
|
-
backbones = get_available_backbone_names()
|
|
26
|
-
|
|
27
|
-
if is_travis:
|
|
28
|
-
backbones = [b for b in backbones if b not in exclude]
|
|
29
|
-
return backbones
|
|
23
|
+
return get_available_backbone_names()
|
|
30
24
|
|
|
31
25
|
|
|
32
26
|
BACKBONES = get_backbones()
|
|
@@ -35,7 +29,13 @@ BACKBONES = get_backbones()
|
|
|
35
29
|
def _test_backbones(names):
|
|
36
30
|
is_full = str2bool(os.environ.get("FULL_TEST", "False"))
|
|
37
31
|
if not is_full:
|
|
38
|
-
return [
|
|
32
|
+
return [
|
|
33
|
+
"resnet34",
|
|
34
|
+
"resnet50",
|
|
35
|
+
"inceptionresnetv2",
|
|
36
|
+
"efficientnetb0",
|
|
37
|
+
"efficientnetv2m",
|
|
38
|
+
]
|
|
39
39
|
else:
|
|
40
40
|
return names
|
|
41
41
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/backbones/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/__init__.py
RENAMED
|
File without changes
|
{segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras/models/_common_blocks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras.egg-info/requires.txt
RENAMED
|
File without changes
|
{segmodels_keras-0.1.0.dev6 → segmodels_keras-0.1.0.dev7}/segmodels_keras.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|