segmodels-keras 0.1.0.dev4__tar.gz → 0.1.0.dev5__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.dev4 → segmodels_keras-0.1.0.dev5}/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/README.rst +4 -1
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/_version.py +1 -1
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/fpn.py +12 -1
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/linknet.py +12 -1
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/pspnet.py +12 -1
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/unet.py +12 -1
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/utils.py +32 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras.egg-info/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/tests/test_utils.py +40 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/LICENSE +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/MANIFEST.in +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/pyproject.toml +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/_compat.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/backbones/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/backbones/backbones_factory.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/backbones/inception_resnet_v2.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/backbones/inception_v3.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/base/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/base/functional.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/base/objects.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/losses.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/metrics.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/_common_blocks.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/_utils.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras.egg-info/SOURCES.txt +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras.egg-info/dependency_links.txt +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras.egg-info/requires.txt +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras.egg-info/top_level.txt +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/setup.cfg +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/setup.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/tests/test_backbones.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/tests/test_metrics.py +0 -0
- {segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/tests/test_models.py +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|Actions Status| |PyPI version|
|
|
1
|
+
|Actions Status| |Code coverage| |PyPI version|
|
|
2
2
|
|
|
3
3
|
.. |Actions Status| image:: https://github.com/orthoseg/segmodels_keras/actions/workflows/tests.yml/badge.svg?branch=main
|
|
4
4
|
:target: https://github.com/orthoseg/segmodels_keras/actions/workflows/tests.yml?query=workflow%3ATests
|
|
5
5
|
|
|
6
|
+
.. |Code coverage| image:: https://codecov.io/gh/orthoseg/segmodels_keras/graph/badge.svg?token=o01OyTMDuj
|
|
7
|
+
:target: https://codecov.io/gh/orthoseg/segmodels_keras
|
|
8
|
+
|
|
6
9
|
.. |PyPI version| image:: https://img.shields.io/pypi/v/segmodels-keras.svg
|
|
7
10
|
:target: https://pypi.org/project/segmodels-keras
|
|
8
11
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__versioninfo__ = (0, 1, 0, "
|
|
1
|
+
__versioninfo__ = (0, 1, 0, "dev5")
|
|
2
2
|
__version__ = ".".join(map(str, __versioninfo__))
|
|
@@ -107,6 +107,7 @@ def build_fpn(
|
|
|
107
107
|
use_batchnorm=True,
|
|
108
108
|
aggregation="sum",
|
|
109
109
|
dropout=None,
|
|
110
|
+
weights_notop=None,
|
|
110
111
|
):
|
|
111
112
|
input_ = backbone.input
|
|
112
113
|
x = backbone.output
|
|
@@ -172,6 +173,12 @@ def build_fpn(
|
|
|
172
173
|
size=(2, 2), interpolation="bilinear", name="final_upsampling"
|
|
173
174
|
)(x)
|
|
174
175
|
|
|
176
|
+
# load weights without top if path provided
|
|
177
|
+
if weights_notop is not None:
|
|
178
|
+
model_notop = models.Model(input_, x)
|
|
179
|
+
model_notop.load_weights(weights_notop)
|
|
180
|
+
x = model_notop.output
|
|
181
|
+
|
|
175
182
|
# model head (define number of output classes)
|
|
176
183
|
x = layers.Conv2D(
|
|
177
184
|
filters=classes,
|
|
@@ -200,6 +207,7 @@ def FPN(
|
|
|
200
207
|
classes=21,
|
|
201
208
|
activation="softmax",
|
|
202
209
|
weights=None,
|
|
210
|
+
weights_notop=None,
|
|
203
211
|
encoder_weights="imagenet",
|
|
204
212
|
encoder_freeze=False,
|
|
205
213
|
encoder_features="default",
|
|
@@ -219,7 +227,9 @@ def FPN(
|
|
|
219
227
|
your model be able to process images af any size, but ``H`` and ``W`` of
|
|
220
228
|
input images should be divisible by factor ``32``.
|
|
221
229
|
classes: a number of classes for output (output shape - ``(h, w, classes)``).
|
|
222
|
-
weights: optional, path to model weights.
|
|
230
|
+
weights: optional, path to model weights to be loaded.
|
|
231
|
+
weights_notop: optional, path to model weights without top (without segmentation
|
|
232
|
+
head) to be loaded.
|
|
223
233
|
activation: name of one of ``keras.activations`` for last model layer
|
|
224
234
|
(e.g. ``sigmoid``, ``softmax``, ``linear``).
|
|
225
235
|
encoder_weights: one of ``None`` (random initialization), ``imagenet``
|
|
@@ -265,6 +275,7 @@ def FPN(
|
|
|
265
275
|
activation=activation,
|
|
266
276
|
classes=classes,
|
|
267
277
|
aggregation=pyramid_aggregation,
|
|
278
|
+
weights_notop=weights_notop,
|
|
268
279
|
)
|
|
269
280
|
|
|
270
281
|
# lock encoder weights for fine-tuning
|
|
@@ -141,6 +141,7 @@ def build_linknet(
|
|
|
141
141
|
classes=1,
|
|
142
142
|
activation="sigmoid",
|
|
143
143
|
use_batchnorm=True,
|
|
144
|
+
weights_notop=None,
|
|
144
145
|
):
|
|
145
146
|
input_ = backbone.input
|
|
146
147
|
x = backbone.output
|
|
@@ -169,6 +170,12 @@ def build_linknet(
|
|
|
169
170
|
x, skip
|
|
170
171
|
)
|
|
171
172
|
|
|
173
|
+
# load weights without top if path provided
|
|
174
|
+
if weights_notop is not None:
|
|
175
|
+
model_notop = models.Model(input_, x)
|
|
176
|
+
model_notop.load_weights(weights_notop)
|
|
177
|
+
x = model_notop.output
|
|
178
|
+
|
|
172
179
|
# model head (define number of output classes)
|
|
173
180
|
x = layers.Conv2D(
|
|
174
181
|
filters=classes,
|
|
@@ -196,6 +203,7 @@ def Linknet(
|
|
|
196
203
|
classes=1,
|
|
197
204
|
activation="sigmoid",
|
|
198
205
|
weights=None,
|
|
206
|
+
weights_notop=None,
|
|
199
207
|
encoder_weights="imagenet",
|
|
200
208
|
encoder_freeze=False,
|
|
201
209
|
encoder_features="default",
|
|
@@ -219,7 +227,9 @@ def Linknet(
|
|
|
219
227
|
classes: a number of classes for output (output shape - ``(h, w, classes)``).
|
|
220
228
|
activation: name of one of ``keras.activations`` for last model layer
|
|
221
229
|
(e.g. ``sigmoid``, ``softmax``, ``linear``).
|
|
222
|
-
weights: optional, path to model weights.
|
|
230
|
+
weights: optional, path to model weights to be loaded.
|
|
231
|
+
weights_notop: optional, path to model weights without top (without segmentation
|
|
232
|
+
head) to be loaded.
|
|
223
233
|
encoder_weights: one of ``None`` (random initialization), ``imagenet``
|
|
224
234
|
(pre-training on ImageNet).
|
|
225
235
|
encoder_freeze: if ``True`` set all layers of encoder (backbone model) as
|
|
@@ -274,6 +284,7 @@ def Linknet(
|
|
|
274
284
|
activation=activation,
|
|
275
285
|
n_upsample_blocks=len(decoder_filters),
|
|
276
286
|
use_batchnorm=decoder_use_batchnorm,
|
|
287
|
+
weights_notop=weights_notop,
|
|
277
288
|
)
|
|
278
289
|
|
|
279
290
|
# lock encoder weights for fine-tuning
|
|
@@ -124,6 +124,7 @@ def build_psp(
|
|
|
124
124
|
classes=21,
|
|
125
125
|
activation="softmax",
|
|
126
126
|
dropout=None,
|
|
127
|
+
weights_notop=None,
|
|
127
128
|
):
|
|
128
129
|
input_ = backbone.input
|
|
129
130
|
x = (
|
|
@@ -147,6 +148,12 @@ def build_psp(
|
|
|
147
148
|
if dropout is not None:
|
|
148
149
|
x = layers.SpatialDropout2D(dropout, name="spatial_dropout")(x)
|
|
149
150
|
|
|
151
|
+
# load weights without top if path provided
|
|
152
|
+
if weights_notop is not None:
|
|
153
|
+
model_notop = models.Model(input_, x)
|
|
154
|
+
model_notop.load_weights(weights_notop)
|
|
155
|
+
x = model_notop.output
|
|
156
|
+
|
|
150
157
|
# model head
|
|
151
158
|
x = layers.Conv2D(
|
|
152
159
|
filters=classes,
|
|
@@ -177,6 +184,7 @@ def PSPNet(
|
|
|
177
184
|
classes=21,
|
|
178
185
|
activation="softmax",
|
|
179
186
|
weights=None,
|
|
187
|
+
weights_notop=None,
|
|
180
188
|
encoder_weights="imagenet",
|
|
181
189
|
encoder_freeze=False,
|
|
182
190
|
downsample_factor=8,
|
|
@@ -197,7 +205,9 @@ def PSPNet(
|
|
|
197
205
|
classes: a number of classes for output (output shape - ``(h, w, classes)``).
|
|
198
206
|
activation: name of one of ``keras.activations`` for last model layer
|
|
199
207
|
(e.g. ``sigmoid``, ``softmax``, ``linear``).
|
|
200
|
-
weights: optional, path to model weights.
|
|
208
|
+
weights: optional, path to model weights to be loaded.
|
|
209
|
+
weights_notop: optional, path to model weights without top (without segmentation
|
|
210
|
+
head) to be loaded.
|
|
201
211
|
encoder_weights: one of ``None`` (random initialization), ``imagenet``
|
|
202
212
|
(pre-training on ImageNet).
|
|
203
213
|
encoder_freeze: if ``True`` set all layers of encoder (backbone model) as
|
|
@@ -250,6 +260,7 @@ def PSPNet(
|
|
|
250
260
|
classes=classes,
|
|
251
261
|
activation=activation,
|
|
252
262
|
dropout=psp_dropout,
|
|
263
|
+
weights_notop=weights_notop,
|
|
253
264
|
)
|
|
254
265
|
|
|
255
266
|
# lock encoder weights for fine-tuning
|
|
@@ -113,6 +113,7 @@ def build_unet(
|
|
|
113
113
|
classes=1,
|
|
114
114
|
activation="sigmoid",
|
|
115
115
|
use_batchnorm=True,
|
|
116
|
+
weights_notop=None,
|
|
116
117
|
):
|
|
117
118
|
input_ = backbone.input
|
|
118
119
|
x = backbone.output
|
|
@@ -141,6 +142,12 @@ def build_unet(
|
|
|
141
142
|
x, skip
|
|
142
143
|
)
|
|
143
144
|
|
|
145
|
+
# load weights without top if path provided
|
|
146
|
+
if weights_notop is not None:
|
|
147
|
+
model_notop = models.Model(input_, x)
|
|
148
|
+
model_notop.load_weights(weights_notop)
|
|
149
|
+
x = model_notop.output
|
|
150
|
+
|
|
144
151
|
# model head (define number of output classes)
|
|
145
152
|
x = layers.Conv2D(
|
|
146
153
|
filters=classes,
|
|
@@ -169,6 +176,7 @@ def Unet(
|
|
|
169
176
|
classes=1,
|
|
170
177
|
activation="sigmoid",
|
|
171
178
|
weights=None,
|
|
179
|
+
weights_notop=None,
|
|
172
180
|
encoder_weights="imagenet",
|
|
173
181
|
encoder_freeze=False,
|
|
174
182
|
encoder_features="default",
|
|
@@ -190,7 +198,9 @@ def Unet(
|
|
|
190
198
|
classes: a number of classes for output (output shape - ``(h, w, classes)``).
|
|
191
199
|
activation: name of one of ``keras.activations`` for last model layer
|
|
192
200
|
(e.g. ``sigmoid``, ``softmax``, ``linear``).
|
|
193
|
-
weights: optional, path to model weights.
|
|
201
|
+
weights: optional, path to model weights to be loaded.
|
|
202
|
+
weights_notop: optional, path to model weights without top (without segmentation
|
|
203
|
+
head) to be loaded.
|
|
194
204
|
encoder_weights: one of ``None`` (random initialization), ``imagenet``
|
|
195
205
|
(pre-training on ImageNet).
|
|
196
206
|
encoder_freeze: if ``True`` set all layers of encoder (backbone model) as
|
|
@@ -245,6 +255,7 @@ def Unet(
|
|
|
245
255
|
activation=activation,
|
|
246
256
|
n_upsample_blocks=len(decoder_filters),
|
|
247
257
|
use_batchnorm=decoder_use_batchnorm,
|
|
258
|
+
weights_notop=weights_notop,
|
|
248
259
|
)
|
|
249
260
|
|
|
250
261
|
# lock encoder weights for fine-tuning
|
|
@@ -1,8 +1,40 @@
|
|
|
1
1
|
"""Utility functions for segmentation models"""
|
|
2
2
|
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
3
5
|
from keras import models
|
|
4
6
|
|
|
5
7
|
|
|
8
|
+
def save_model_weights_notop(
|
|
9
|
+
model: models.Model, decoder: str, path: str | Path, overwrite: bool = True
|
|
10
|
+
):
|
|
11
|
+
"""Save model weights without top (without segmentation head).
|
|
12
|
+
|
|
13
|
+
The weights saved like this can be used to preload a segmentation model for
|
|
14
|
+
fine-tuning by passing the path to these weights to ``weights_notop`` argument of
|
|
15
|
+
the model constructor, e.g. ``Unet(weights_notop="path/to/weights.h5")``.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
model (``keras.models.Model``): instance of keras model
|
|
19
|
+
decoder: type of the decoder part of the model. Should be one of ``fpn``,
|
|
20
|
+
``linknet``, ``unet``, ``pspnet``.
|
|
21
|
+
path (str | Path): path to save model weights
|
|
22
|
+
overwrite (bool): whether to overwrite existing file at ``path``.
|
|
23
|
+
Defaults to ``True``.
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
decoder_top_layers = {"fpn": 2, "linknet": 2, "unet": 2, "pspnet": 3}
|
|
27
|
+
if decoder not in decoder_top_layers:
|
|
28
|
+
raise ValueError(
|
|
29
|
+
f"Decoder should be one of {decoder_top_layers.keys()}, got {decoder}"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Add 1 to the slice to actually remove the number of top layers specified
|
|
33
|
+
nb_top_layers = decoder_top_layers[decoder] + 1
|
|
34
|
+
model_notop = models.Model(model.input, model.layers[-nb_top_layers].output)
|
|
35
|
+
model_notop.save_weights(str(path), overwrite=overwrite)
|
|
36
|
+
|
|
37
|
+
|
|
6
38
|
def set_trainable(model, recompile=True, **kwargs): # noqa: ARG001
|
|
7
39
|
"""Set all layers of model trainable and recompile it
|
|
8
40
|
|
|
@@ -106,5 +106,45 @@ def test_activity_reg(case):
|
|
|
106
106
|
_test_regularizer(model, reg_model, x, y)
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
@pytest.mark.parametrize("decoder", ["fpn", "linknet", "unet", "pspnet"])
|
|
110
|
+
def test_save_model_weights_notop(tmp_path, decoder):
|
|
111
|
+
backbone_name = "mobilenetv2"
|
|
112
|
+
if decoder == "pspnet":
|
|
113
|
+
model = sm.PSPNet(backbone_name, encoder_weights=None)
|
|
114
|
+
elif decoder == "fpn":
|
|
115
|
+
model = sm.FPN(backbone_name, encoder_weights=None)
|
|
116
|
+
elif decoder == "linknet":
|
|
117
|
+
model = sm.Linknet(backbone_name, encoder_weights=None)
|
|
118
|
+
elif decoder == "unet":
|
|
119
|
+
model = sm.Unet(backbone_name, encoder_weights=None)
|
|
120
|
+
else:
|
|
121
|
+
raise ValueError(f"Incorrect decoder {decoder}")
|
|
122
|
+
|
|
123
|
+
# Test saving the model weights without the top layers
|
|
124
|
+
path = tmp_path / f"{decoder}.weights.h5"
|
|
125
|
+
sm.utils.save_model_weights_notop(model, decoder=decoder, path=path)
|
|
126
|
+
|
|
127
|
+
# Test loading the model weights without the top layers again
|
|
128
|
+
assert path.exists()
|
|
129
|
+
if decoder == "pspnet":
|
|
130
|
+
model = sm.PSPNet(backbone_name, weights_notop=path)
|
|
131
|
+
elif decoder == "fpn":
|
|
132
|
+
model = sm.FPN(backbone_name, weights_notop=path)
|
|
133
|
+
elif decoder == "linknet":
|
|
134
|
+
model = sm.Linknet(backbone_name, weights_notop=path)
|
|
135
|
+
elif decoder == "unet":
|
|
136
|
+
model = sm.Unet(backbone_name, weights_notop=path)
|
|
137
|
+
else:
|
|
138
|
+
raise ValueError(f"Incorrect decoder {decoder}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_save_model_weights_notop_invalid_decoder():
|
|
142
|
+
model = keras.models.Model()
|
|
143
|
+
with pytest.raises(ValueError, match="Decoder should be one of"):
|
|
144
|
+
sm.utils.save_model_weights_notop(
|
|
145
|
+
model, decoder="invalid_decoder", path="path/to/weights.h5"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
109
149
|
if __name__ == "__main__":
|
|
110
150
|
pytest.main([__file__])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/backbones/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/backbones/inception_v3.py
RENAMED
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/base/functional.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/__init__.py
RENAMED
|
File without changes
|
{segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras/models/_common_blocks.py
RENAMED
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/segmodels_keras.egg-info/requires.txt
RENAMED
|
File without changes
|
{segmodels_keras-0.1.0.dev4 → segmodels_keras-0.1.0.dev5}/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
|