segmodels-keras 0.1.0.dev7__tar.gz → 0.1.0.dev8__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.dev7 → segmodels_keras-0.1.0.dev8}/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/README.rst +3 -1
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/_version.py +1 -1
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/backbones_factory.py +14 -9
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/inception_resnet_v2.py +2 -117
- segmodels_keras-0.1.0.dev8/segmodels_keras/backbones/resnet_18_34.py +368 -0
- segmodels_keras-0.1.0.dev8/segmodels_keras/backbones/resnet_common.py +122 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras.egg-info/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras.egg-info/SOURCES.txt +2 -1
- segmodels_keras-0.1.0.dev7/segmodels_keras/backbones/resnet.py +0 -494
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/LICENSE +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/MANIFEST.in +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/pyproject.toml +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/_compat.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/_common_blocks.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/_weights.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/inception_v3.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/base/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/base/functional.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/base/objects.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/losses.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/metrics.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/models/__init__.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/models/_common_blocks.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/models/_utils.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/models/fpn.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/models/linknet.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/models/pspnet.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/models/unet.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/utils.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras.egg-info/dependency_links.txt +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras.egg-info/requires.txt +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras.egg-info/top_level.txt +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/setup.cfg +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/setup.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/tests/test_backbones.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/tests/test_metrics.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/tests/test_models.py +0 -0
- {segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/tests/test_utils.py +0 -0
|
@@ -24,7 +24,9 @@ improvements and support for some extra backbone models.
|
|
|
24
24
|
It is not meant as a full replacement of the original library, but rather as a
|
|
25
25
|
solution for a library I developed and depended on segmentation_models:
|
|
26
26
|
`orthoseg <https://github.com/orthoseg/orthoseg>`__ . Hence, full backwards
|
|
27
|
-
compatibility
|
|
27
|
+
compatibility or support for all features is not an explicit goal. For example,
|
|
28
|
+
the resnet backbones got small changes compared to the original library, so existing
|
|
29
|
+
weights you saved for them won't be compatible.
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
**The main features** of this library are:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__versioninfo__ = (0, 1, 0, "
|
|
1
|
+
__versioninfo__ = (0, 1, 0, "dev8")
|
|
2
2
|
__version__ = ".".join(map(str, __versioninfo__))
|
|
@@ -2,7 +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
|
|
5
|
+
from . import resnet_18_34
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class BackbonesFactory:
|
|
@@ -17,17 +17,22 @@ class BackbonesFactory:
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
_models = { # noqa: RUF012
|
|
20
|
-
# ResNets < 50 layers are NOT available via keras.applications, so use
|
|
21
|
-
#
|
|
20
|
+
# ResNets < 50 layers are NOT available via keras.applications, so use a
|
|
21
|
+
# torchvision-compatible post-activation BasicBlock implementation.
|
|
22
|
+
# Layer names follow the keras.applications ResNet50+ naming convention.
|
|
22
23
|
"resnet18": (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
resnet_18_34.ResNet18,
|
|
25
|
+
resnet_18_34.preprocess_input,
|
|
26
|
+
# resnet18: layer1=2, layer2=2, layer3=2, layer4=2 blocks
|
|
27
|
+
# backbone output = conv5_block2_out (512ch); skips are the 4 stages before it
|
|
28
|
+
("conv4_block2_out", "conv3_block2_out", "conv2_block2_out", "conv1_relu"),
|
|
26
29
|
),
|
|
27
30
|
"resnet34": (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
resnet_18_34.ResNet34,
|
|
32
|
+
resnet_18_34.preprocess_input,
|
|
33
|
+
# resnet34: layer1=3, layer2=4, layer3=6, layer4=3 blocks
|
|
34
|
+
# backbone output = conv5_block3_out (512ch); skips are the 4 stages before it
|
|
35
|
+
("conv4_block6_out", "conv3_block4_out", "conv2_block3_out", "conv1_relu"),
|
|
31
36
|
),
|
|
32
37
|
# ResNets > 50 layers are available via keras.applications, so use those.
|
|
33
38
|
# Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
|
|
@@ -32,13 +32,14 @@ are the following:
|
|
|
32
32
|
keras, and here we need to use the public API of keras.
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
import warnings
|
|
36
35
|
from pathlib import Path
|
|
37
36
|
|
|
38
37
|
from keras import backend, layers, models
|
|
39
38
|
from keras.applications import imagenet_utils
|
|
40
39
|
from keras.utils import get_file, get_source_inputs
|
|
41
40
|
|
|
41
|
+
from .resnet_common import _obtain_input_shape
|
|
42
|
+
|
|
42
43
|
BASE_WEIGHT_URL = (
|
|
43
44
|
"https://storage.googleapis.com/tensorflow/keras-applications/inception_resnet_v2/"
|
|
44
45
|
)
|
|
@@ -442,119 +443,3 @@ def decode_predictions(preds, top=5):
|
|
|
442
443
|
|
|
443
444
|
def get_custom_objects():
|
|
444
445
|
return {"CustomScaleLayer": CustomScaleLayer}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
def _obtain_input_shape(
|
|
448
|
-
input_shape,
|
|
449
|
-
default_size,
|
|
450
|
-
min_size,
|
|
451
|
-
data_format,
|
|
452
|
-
require_flatten,
|
|
453
|
-
weights=None,
|
|
454
|
-
):
|
|
455
|
-
"""Internal utility to compute/validate a model's input shape.
|
|
456
|
-
|
|
457
|
-
Args:
|
|
458
|
-
input_shape: Either None (will return the default network input shape),
|
|
459
|
-
or a user-provided shape to be validated.
|
|
460
|
-
default_size: Default input width/height for the model.
|
|
461
|
-
min_size: Minimum input width/height accepted by the model.
|
|
462
|
-
data_format: Image data format to use.
|
|
463
|
-
require_flatten: Whether the model is expected to
|
|
464
|
-
be linked to a classifier via a Flatten layer.
|
|
465
|
-
weights: One of `None` (random initialization)
|
|
466
|
-
or 'imagenet' (pre-training on ImageNet).
|
|
467
|
-
If weights='imagenet' input channels must be equal to 3.
|
|
468
|
-
|
|
469
|
-
Returns:
|
|
470
|
-
An integer shape tuple (may include None entries).
|
|
471
|
-
|
|
472
|
-
Raises:
|
|
473
|
-
ValueError: In case of invalid argument values.
|
|
474
|
-
"""
|
|
475
|
-
if weights != "imagenet" and input_shape and len(input_shape) == 3:
|
|
476
|
-
if data_format == "channels_first":
|
|
477
|
-
correct_channel_axis = 1 if len(input_shape) == 4 else 0
|
|
478
|
-
if input_shape[correct_channel_axis] not in {1, 3}:
|
|
479
|
-
warnings.warn(
|
|
480
|
-
"This model usually expects 1 or 3 input channels. "
|
|
481
|
-
"However, it was passed an input_shape "
|
|
482
|
-
f"with {input_shape[0]} input channels.",
|
|
483
|
-
stacklevel=2,
|
|
484
|
-
)
|
|
485
|
-
default_shape = (input_shape[0], default_size, default_size)
|
|
486
|
-
else:
|
|
487
|
-
if input_shape[-1] not in {1, 3}:
|
|
488
|
-
warnings.warn(
|
|
489
|
-
"This model usually expects 1 or 3 input channels. "
|
|
490
|
-
"However, it was passed an input_shape "
|
|
491
|
-
f"with {input_shape[-1]} input channels.",
|
|
492
|
-
stacklevel=2,
|
|
493
|
-
)
|
|
494
|
-
default_shape = (default_size, default_size, input_shape[-1])
|
|
495
|
-
else:
|
|
496
|
-
if data_format == "channels_first":
|
|
497
|
-
default_shape = (3, default_size, default_size)
|
|
498
|
-
else:
|
|
499
|
-
default_shape = (default_size, default_size, 3)
|
|
500
|
-
if weights == "imagenet" and require_flatten:
|
|
501
|
-
if input_shape is not None:
|
|
502
|
-
if input_shape != default_shape:
|
|
503
|
-
raise ValueError(
|
|
504
|
-
"When setting `include_top=True` "
|
|
505
|
-
"and loading `imagenet` weights, "
|
|
506
|
-
f"`input_shape` should be {default_shape}. "
|
|
507
|
-
f"Received: input_shape={input_shape}"
|
|
508
|
-
)
|
|
509
|
-
return default_shape
|
|
510
|
-
if input_shape:
|
|
511
|
-
if data_format == "channels_first":
|
|
512
|
-
if input_shape is not None:
|
|
513
|
-
if len(input_shape) != 3:
|
|
514
|
-
raise ValueError("`input_shape` must be a tuple of three integers.")
|
|
515
|
-
if input_shape[0] != 3 and weights == "imagenet":
|
|
516
|
-
raise ValueError(
|
|
517
|
-
"The input must have 3 channels; Received "
|
|
518
|
-
f"`input_shape={input_shape}`"
|
|
519
|
-
)
|
|
520
|
-
if (input_shape[1] is not None and input_shape[1] < min_size) or (
|
|
521
|
-
input_shape[2] is not None and input_shape[2] < min_size
|
|
522
|
-
):
|
|
523
|
-
raise ValueError(
|
|
524
|
-
f"Input size must be at least {min_size}"
|
|
525
|
-
f"x{min_size}; Received: "
|
|
526
|
-
f"input_shape={input_shape}"
|
|
527
|
-
)
|
|
528
|
-
else:
|
|
529
|
-
if input_shape is not None:
|
|
530
|
-
if len(input_shape) != 3:
|
|
531
|
-
raise ValueError("`input_shape` must be a tuple of three integers.")
|
|
532
|
-
if input_shape[-1] != 3 and weights == "imagenet":
|
|
533
|
-
raise ValueError(
|
|
534
|
-
"The input must have 3 channels; Received "
|
|
535
|
-
f"`input_shape={input_shape}`"
|
|
536
|
-
)
|
|
537
|
-
if (input_shape[0] is not None and input_shape[0] < min_size) or (
|
|
538
|
-
input_shape[1] is not None and input_shape[1] < min_size
|
|
539
|
-
):
|
|
540
|
-
raise ValueError(
|
|
541
|
-
"Input size must be at least "
|
|
542
|
-
f"{min_size}x{min_size}; Received: "
|
|
543
|
-
f"input_shape={input_shape}"
|
|
544
|
-
)
|
|
545
|
-
else:
|
|
546
|
-
if require_flatten:
|
|
547
|
-
input_shape = default_shape
|
|
548
|
-
else:
|
|
549
|
-
if data_format == "channels_first":
|
|
550
|
-
input_shape = (3, None, None)
|
|
551
|
-
else:
|
|
552
|
-
input_shape = (None, None, 3)
|
|
553
|
-
if require_flatten:
|
|
554
|
-
if None in input_shape:
|
|
555
|
-
raise ValueError(
|
|
556
|
-
"If `include_top` is True, "
|
|
557
|
-
"you should specify a static `input_shape`. "
|
|
558
|
-
f"Received: input_shape={input_shape}"
|
|
559
|
-
)
|
|
560
|
-
return input_shape
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""ResNet18 and ResNet34 Keras models.
|
|
2
|
+
|
|
3
|
+
Complements keras.applications (which provides ResNet50+) with BasicBlock-based
|
|
4
|
+
ResNet18 and ResNet34. The implementation follows the keras.applications structure
|
|
5
|
+
closely so that the two can be maintained together:
|
|
6
|
+
|
|
7
|
+
- ``ResNet(stack_fn, ...)`` is the generic builder, matching the signature of
|
|
8
|
+
``keras.applications.resnet.ResNet``.
|
|
9
|
+
- ``residual_basicblock(x, filters, ..., name)`` is the BasicBlock equivalent of
|
|
10
|
+
``keras.applications.resnet.residual_block_v1``.
|
|
11
|
+
- ``stack_residual_basicblocks(x, filters, blocks, ..., name)`` groups blocks into a
|
|
12
|
+
stage, equivalent to ``keras.applications.resnet.stack_residual_blocks_v1``.
|
|
13
|
+
|
|
14
|
+
Layer naming follows the same ``conv{N}_block{M}_*`` convention used by
|
|
15
|
+
keras.applications, so models built here share the same weight-loading patterns.
|
|
16
|
+
|
|
17
|
+
Note that the resulting resnet18 and resnet34 are not exactly the same as the version
|
|
18
|
+
in https://github.com/qubvel/classification_models and hence is also different than
|
|
19
|
+
the segmentation models in https://github.com/qubvel/segmentation_models.
|
|
20
|
+
There are some differences in the bottom and top layers of the resnet, e.g. because
|
|
21
|
+
the classification_models version applies also post-activation after the last residual
|
|
22
|
+
block.
|
|
23
|
+
|
|
24
|
+
This implementation is designed to be compatible with torchvision ResNet18/34 pretrained
|
|
25
|
+
weights. Because the resnet implementation in keras.applications.resnet.ResNet (which
|
|
26
|
+
only supports ResNet50+) already uses the same bottom and top layers this implementation
|
|
27
|
+
is based on it, but in addition the following tweaks have been applied:
|
|
28
|
+
- BatchNormalization uses ``momentum=0.9`` and ``epsilon=1e-5`` (torchvision defaults)
|
|
29
|
+
instead of the keras.applications defaults of ``momentum=0.99`` and
|
|
30
|
+
``epsilon=1.001e-5``.
|
|
31
|
+
Note that momentum is only used during training. In addition, the value in pytorch
|
|
32
|
+
is inverted compared to the tf.keras implementation, so the default in pytorch
|
|
33
|
+
is actually ``momentum=0.1``, but after inversion we use ``momentum=0.9`` here.
|
|
34
|
+
- The single bottom convolutional layer gets ``use_bias=False`` (like all convolutional
|
|
35
|
+
layers in the model just like the version in torchvision and in segmentation_models.
|
|
36
|
+
In the keras.applications ResNet(50+) implementation ``use_bias=True`` is used for the
|
|
37
|
+
single bottom convolutional layer.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
import os
|
|
41
|
+
|
|
42
|
+
from keras import backend, layers, models
|
|
43
|
+
from keras.applications import imagenet_utils
|
|
44
|
+
from keras.utils import get_source_inputs
|
|
45
|
+
|
|
46
|
+
from .resnet_common import _obtain_input_shape
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def ResNet(
|
|
50
|
+
stack_fn,
|
|
51
|
+
preact,
|
|
52
|
+
use_bias,
|
|
53
|
+
include_top=True,
|
|
54
|
+
weights=None,
|
|
55
|
+
input_tensor=None,
|
|
56
|
+
input_shape=None,
|
|
57
|
+
pooling=None,
|
|
58
|
+
classes=1000,
|
|
59
|
+
classifier_activation="softmax",
|
|
60
|
+
name="resnet",
|
|
61
|
+
):
|
|
62
|
+
"""Generic ResNet builder for BasicBlock variants (ResNet18/34).
|
|
63
|
+
|
|
64
|
+
Follows the same signature as ``keras.applications.resnet.ResNet`` so that
|
|
65
|
+
both can be used interchangeably.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
stack_fn: A function that returns output tensor for the
|
|
69
|
+
stacked residual blocks.
|
|
70
|
+
preact: Whether to use pre-activation (BN before conv). `True` for ResNetV2,
|
|
71
|
+
`False` (post-activation) for ResNet (including ResNet18/34) and ResNeXt.
|
|
72
|
+
use_bias: Whether to use bias for the single bottom convolutional layer or not.
|
|
73
|
+
`True` for ResNet and ResNetV2 in the standard keras.applications
|
|
74
|
+
implementation, `False` for ResNeXt. However, for torchvision compatibility
|
|
75
|
+
we will use False here for ResNet18/34.
|
|
76
|
+
include_top: Whether to include the fully-connected layer at the top of the
|
|
77
|
+
network (=classification head). Defaults to ``True``.
|
|
78
|
+
weights: Path to a weights file to load, or ``None``. Defaults to
|
|
79
|
+
``None``.
|
|
80
|
+
input_tensor: Optional Keras tensor to use as the model input.
|
|
81
|
+
(i.e. output of `layers.Input()`)
|
|
82
|
+
input_shape: Optional shape tuple, only to be specified
|
|
83
|
+
if `include_top` is `False` (otherwise the input shape
|
|
84
|
+
has to be `(224, 224, 3)` (with `channels_last` data format)
|
|
85
|
+
or `(3, 224, 224)` (with `"channels_first"` data format). It
|
|
86
|
+
should have exactly 3 inputs channels.
|
|
87
|
+
pooling: Optional pooling mode for feature extraction
|
|
88
|
+
when `include_top` is `False`.
|
|
89
|
+
- `None` means that the output of the model will be
|
|
90
|
+
the 4D tensor output of the
|
|
91
|
+
last convolutional layer.
|
|
92
|
+
- `avg` means that global average pooling
|
|
93
|
+
will be applied to the output of the
|
|
94
|
+
last convolutional layer, and thus
|
|
95
|
+
the output of the model will be a 2D tensor.
|
|
96
|
+
- `max` means that global max pooling will
|
|
97
|
+
be applied.
|
|
98
|
+
classes: optional number of classes to classify images
|
|
99
|
+
into, only to be specified if `include_top` is `True`,
|
|
100
|
+
and if no `weights` argument is specified.
|
|
101
|
+
Defaults to ``1000``.
|
|
102
|
+
classifier_activation: A `str` or callable. The activation
|
|
103
|
+
function to use on the "top" layer. Ignored unless
|
|
104
|
+
`include_top=True`. Set `classifier_activation=None` to
|
|
105
|
+
return the logits of the "top" layer. When loading
|
|
106
|
+
pretrained weights, `classifier_activation` can only be
|
|
107
|
+
`None` or `"softmax"`. Defaults to ``"softmax"``.
|
|
108
|
+
name: The name of the model (string). Defaults to ``"resnet"``.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
A ``keras.Model`` instance.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
# Determine proper input shape
|
|
115
|
+
input_shape = _obtain_input_shape(
|
|
116
|
+
input_shape,
|
|
117
|
+
default_size=224,
|
|
118
|
+
min_size=32,
|
|
119
|
+
data_format=backend.image_data_format(),
|
|
120
|
+
require_flatten=include_top,
|
|
121
|
+
weights=weights,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if input_tensor is None:
|
|
125
|
+
img_input = layers.Input(shape=input_shape)
|
|
126
|
+
else:
|
|
127
|
+
if not backend.is_keras_tensor(input_tensor):
|
|
128
|
+
img_input = layers.Input(tensor=input_tensor, shape=input_shape)
|
|
129
|
+
else:
|
|
130
|
+
img_input = input_tensor
|
|
131
|
+
|
|
132
|
+
if backend.image_data_format() == "channels_last":
|
|
133
|
+
bn_axis = 3
|
|
134
|
+
else:
|
|
135
|
+
bn_axis = 1
|
|
136
|
+
|
|
137
|
+
x = layers.ZeroPadding2D(padding=((3, 3), (3, 3)), name="conv1_pad")(img_input)
|
|
138
|
+
x = layers.Conv2D(64, 7, strides=2, use_bias=use_bias, name="conv1_conv")(x)
|
|
139
|
+
|
|
140
|
+
if not preact:
|
|
141
|
+
# The batchnormalization parameters have been changed to be compatible with the
|
|
142
|
+
# torchvision ResNet18/34 pretrained weights.
|
|
143
|
+
x = layers.BatchNormalization(
|
|
144
|
+
axis=bn_axis, momentum=0.1, epsilon=1e-5, name="conv1_bn"
|
|
145
|
+
)(x)
|
|
146
|
+
x = layers.Activation("relu", name="conv1_relu")(x)
|
|
147
|
+
|
|
148
|
+
x = layers.ZeroPadding2D(padding=((1, 1), (1, 1)), name="pool1_pad")(x)
|
|
149
|
+
x = layers.MaxPooling2D(3, strides=2, name="pool1_pool")(x)
|
|
150
|
+
|
|
151
|
+
x = stack_fn(x)
|
|
152
|
+
|
|
153
|
+
if preact:
|
|
154
|
+
# The batchnormalization parameters have been changed to be compatible with the
|
|
155
|
+
# torchvision ResNet18/34 pretrained weights.
|
|
156
|
+
x = layers.BatchNormalization(
|
|
157
|
+
axis=bn_axis, momentum=0.1, epsilon=1e-5, name="post_bn"
|
|
158
|
+
)(x)
|
|
159
|
+
x = layers.Activation("relu", name="post_relu")(x)
|
|
160
|
+
|
|
161
|
+
if include_top:
|
|
162
|
+
x = layers.GlobalAveragePooling2D(name="avg_pool")(x)
|
|
163
|
+
|
|
164
|
+
# Validate activation for the classifier layer
|
|
165
|
+
imagenet_utils.validate_activation(classifier_activation, weights)
|
|
166
|
+
|
|
167
|
+
x = layers.Dense(classes, activation=classifier_activation, name="predictions")(
|
|
168
|
+
x
|
|
169
|
+
)
|
|
170
|
+
else:
|
|
171
|
+
if pooling == "avg":
|
|
172
|
+
x = layers.GlobalAveragePooling2D(name="avg_pool")(x)
|
|
173
|
+
elif pooling == "max":
|
|
174
|
+
x = layers.GlobalMaxPooling2D(name="max_pool")(x)
|
|
175
|
+
|
|
176
|
+
# Ensure that the model takes into account
|
|
177
|
+
# any potential predecessors of `input_tensor`.
|
|
178
|
+
if input_tensor is not None:
|
|
179
|
+
inputs = get_source_inputs(input_tensor)
|
|
180
|
+
else:
|
|
181
|
+
inputs = img_input
|
|
182
|
+
|
|
183
|
+
# Create model.
|
|
184
|
+
model = models.Model(inputs, x, name=name)
|
|
185
|
+
|
|
186
|
+
if weights is not None and os.path.exists(weights):
|
|
187
|
+
model.load_weights(weights)
|
|
188
|
+
|
|
189
|
+
return model
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def residual_basicblock(
|
|
193
|
+
x, filters, kernel_size=3, stride=1, conv_shortcut=True, name=None
|
|
194
|
+
):
|
|
195
|
+
"""A BasicBlock residual block for ResNet18/34.
|
|
196
|
+
|
|
197
|
+
Similar to ``keras.applications.resnet.residual_block_v1`` that is used for
|
|
198
|
+
ResNet50+, but with following differences for resnet 18/34:
|
|
199
|
+
- two 3x3 convolutions instead of the 1x1 / 3x3 / 1x1 bottleneck.
|
|
200
|
+
- ``use_bias=False`` instead of ``True``
|
|
201
|
+
|
|
202
|
+
Following changes have been applied to be compatible with torchvision ResNet18/34
|
|
203
|
+
pretrained weights:
|
|
204
|
+
- BN momentum is set to 0.1 instead of 0.99
|
|
205
|
+
- BN epsilon is set to 1e-5 instead of 1.001e-5
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
x: Input tensor.
|
|
209
|
+
filters: No of filters for both 3x3 convolutions.
|
|
210
|
+
kernel_size: Kernel size of the 3x3 convolutions. Defaults to ``3``.
|
|
211
|
+
stride: Stride of the first convolution. Defaults to ``1``.
|
|
212
|
+
conv_shortcut: Use a strided 1x1 conv on the shortcut path if ``True``,
|
|
213
|
+
otherwise use an identity shortcut. Defaults to ``True``.
|
|
214
|
+
name: Block name prefix, e.g. ``"conv3_block1"``.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Output tensor for the block.
|
|
218
|
+
"""
|
|
219
|
+
if backend.image_data_format() == "channels_last":
|
|
220
|
+
bn_axis = 3
|
|
221
|
+
else:
|
|
222
|
+
bn_axis = 1
|
|
223
|
+
|
|
224
|
+
if conv_shortcut:
|
|
225
|
+
shortcut = layers.Conv2D(
|
|
226
|
+
filters, 1, strides=stride, use_bias=False, name=f"{name}_0_conv"
|
|
227
|
+
)(x)
|
|
228
|
+
shortcut = layers.BatchNormalization(
|
|
229
|
+
axis=bn_axis, momentum=0.1, epsilon=1e-5, name=f"{name}_0_bn"
|
|
230
|
+
)(shortcut)
|
|
231
|
+
else:
|
|
232
|
+
shortcut = x
|
|
233
|
+
|
|
234
|
+
x = layers.ZeroPadding2D(padding=((1, 1), (1, 1)), name=f"{name}_1_pad")(x)
|
|
235
|
+
x = layers.Conv2D(
|
|
236
|
+
filters, kernel_size, strides=stride, use_bias=False, name=f"{name}_1_conv"
|
|
237
|
+
)(x)
|
|
238
|
+
x = layers.BatchNormalization(
|
|
239
|
+
axis=bn_axis, momentum=0.1, epsilon=1e-5, name=f"{name}_1_bn"
|
|
240
|
+
)(x)
|
|
241
|
+
x = layers.Activation("relu", name=f"{name}_1_relu")(x)
|
|
242
|
+
|
|
243
|
+
x = layers.ZeroPadding2D(padding=((1, 1), (1, 1)), name=f"{name}_2_pad")(x)
|
|
244
|
+
x = layers.Conv2D(filters, kernel_size, use_bias=False, name=f"{name}_2_conv")(x)
|
|
245
|
+
x = layers.BatchNormalization(
|
|
246
|
+
axis=bn_axis, momentum=0.1, epsilon=1e-5, name=f"{name}_2_bn"
|
|
247
|
+
)(x)
|
|
248
|
+
|
|
249
|
+
x = layers.Add(name=f"{name}_add")([shortcut, x])
|
|
250
|
+
x = layers.Activation("relu", name=f"{name}_out")(x)
|
|
251
|
+
return x
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def stack_residual_basicblocks(x, filters, blocks, stride1=2, name=None):
|
|
255
|
+
"""A set of stacked BasicBlock residual blocks forming one ResNet stage.
|
|
256
|
+
|
|
257
|
+
Similar to ``keras.applications.resnet.stack_residual_blocks_v1``.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
x: Input tensor.
|
|
261
|
+
filters: Number of filters for all blocks in this stage.
|
|
262
|
+
blocks: Number of BasicBlocks in the stage.
|
|
263
|
+
stride1: Stride of the first block (use ``2`` to downsample spatially,
|
|
264
|
+
``1`` for the first stage where spatial size is unchanged). Defaults
|
|
265
|
+
to ``2``.
|
|
266
|
+
name: Stage name prefix, e.g. ``"conv3"``.
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Output tensor for the stacked blocks.
|
|
270
|
+
"""
|
|
271
|
+
x = residual_basicblock(
|
|
272
|
+
x, filters, stride=stride1, conv_shortcut=stride1 > 1, name=f"{name}_block1"
|
|
273
|
+
)
|
|
274
|
+
for i in range(2, blocks + 1):
|
|
275
|
+
x = residual_basicblock(
|
|
276
|
+
x, filters, conv_shortcut=False, name=f"{name}_block{i}"
|
|
277
|
+
)
|
|
278
|
+
return x
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def ResNet18(
|
|
282
|
+
include_top=True,
|
|
283
|
+
weights=None,
|
|
284
|
+
input_tensor=None,
|
|
285
|
+
input_shape=None,
|
|
286
|
+
pooling=None,
|
|
287
|
+
classes=1000,
|
|
288
|
+
classifier_activation="softmax",
|
|
289
|
+
name="resnet18",
|
|
290
|
+
use_bias=False,
|
|
291
|
+
):
|
|
292
|
+
"""Instantiates the ResNet18 architecture with torchvision-compatible tweaks.
|
|
293
|
+
|
|
294
|
+
Remark: torchvision uses bias=False, keras.applications ResNet uses bias=True, but
|
|
295
|
+
for torchvision compatibility we will use False here.
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
def stack_fn(x):
|
|
299
|
+
x = stack_residual_basicblocks(x, 64, 2, stride1=1, name="conv2")
|
|
300
|
+
x = stack_residual_basicblocks(x, 128, 2, stride1=2, name="conv3")
|
|
301
|
+
x = stack_residual_basicblocks(x, 256, 2, stride1=2, name="conv4")
|
|
302
|
+
return stack_residual_basicblocks(x, 512, 2, stride1=2, name="conv5")
|
|
303
|
+
|
|
304
|
+
return ResNet(
|
|
305
|
+
stack_fn,
|
|
306
|
+
preact=False, # torchvision ResNet18/34 is post-activation (Conv -> BN -> ReLU)
|
|
307
|
+
use_bias=use_bias,
|
|
308
|
+
name=name,
|
|
309
|
+
include_top=include_top,
|
|
310
|
+
weights=weights,
|
|
311
|
+
input_tensor=input_tensor,
|
|
312
|
+
input_shape=input_shape,
|
|
313
|
+
pooling=pooling,
|
|
314
|
+
classes=classes,
|
|
315
|
+
classifier_activation=classifier_activation,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def ResNet34(
|
|
320
|
+
include_top=True,
|
|
321
|
+
weights=None,
|
|
322
|
+
input_tensor=None,
|
|
323
|
+
input_shape=None,
|
|
324
|
+
pooling=None,
|
|
325
|
+
classes=1000,
|
|
326
|
+
classifier_activation="softmax",
|
|
327
|
+
name="resnet34",
|
|
328
|
+
use_bias=False,
|
|
329
|
+
):
|
|
330
|
+
"""Instantiates the ResNet34 architecture with torchvision-compatible tweaks.
|
|
331
|
+
|
|
332
|
+
Remark: torchvision uses bias=False, keras.applications ResNet uses bias=True, but
|
|
333
|
+
for torchvision compatibility we will use False here.
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
def stack_fn(x):
|
|
337
|
+
x = stack_residual_basicblocks(x, 64, 3, stride1=1, name="conv2")
|
|
338
|
+
x = stack_residual_basicblocks(x, 128, 4, stride1=2, name="conv3")
|
|
339
|
+
x = stack_residual_basicblocks(x, 256, 6, stride1=2, name="conv4")
|
|
340
|
+
return stack_residual_basicblocks(x, 512, 3, stride1=2, name="conv5")
|
|
341
|
+
|
|
342
|
+
return ResNet(
|
|
343
|
+
stack_fn,
|
|
344
|
+
preact=False, # torchvision ResNet18/34 is post-activation (Conv -> BN -> ReLU)
|
|
345
|
+
use_bias=use_bias,
|
|
346
|
+
name=name,
|
|
347
|
+
include_top=include_top,
|
|
348
|
+
weights=weights,
|
|
349
|
+
input_tensor=input_tensor,
|
|
350
|
+
input_shape=input_shape,
|
|
351
|
+
pooling=pooling,
|
|
352
|
+
classes=classes,
|
|
353
|
+
classifier_activation=classifier_activation,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def preprocess_input(x, **kwargs):
|
|
358
|
+
"""Torchvision-compatible ImageNet normalization (input in [0, 1])."""
|
|
359
|
+
import numpy as np
|
|
360
|
+
|
|
361
|
+
# FLAIR-compatible normalization (input in [0, 255]).
|
|
362
|
+
mean = np.array([105.08, 110.87, 101.82, 106.38, 53.26], dtype="float32")
|
|
363
|
+
std = np.array([52.17, 45.38, 44, 39.69, 79.3], dtype="float32")
|
|
364
|
+
|
|
365
|
+
# Torchvision-compatible ImageNet normalization (input in [0, 1]).
|
|
366
|
+
# mean = np.array([0.485, 0.456, 0.406], dtype="float32")
|
|
367
|
+
# std = np.array([0.229, 0.224, 0.225], dtype="float32")
|
|
368
|
+
return (x - mean) / std
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Some common utilities for ResNet backbones.
|
|
2
|
+
|
|
3
|
+
Source: keras.applications.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _obtain_input_shape(
|
|
10
|
+
input_shape,
|
|
11
|
+
default_size,
|
|
12
|
+
min_size,
|
|
13
|
+
data_format,
|
|
14
|
+
require_flatten,
|
|
15
|
+
weights=None,
|
|
16
|
+
):
|
|
17
|
+
"""Internal utility to compute/validate a model's input shape.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
input_shape: Either None (will return the default network input shape),
|
|
21
|
+
or a user-provided shape to be validated.
|
|
22
|
+
default_size: Default input width/height for the model.
|
|
23
|
+
min_size: Minimum input width/height accepted by the model.
|
|
24
|
+
data_format: Image data format to use.
|
|
25
|
+
require_flatten: Whether the model is expected to
|
|
26
|
+
be linked to a classifier via a Flatten layer.
|
|
27
|
+
weights: One of `None` (random initialization)
|
|
28
|
+
or 'imagenet' (pre-training on ImageNet).
|
|
29
|
+
If weights='imagenet' input channels must be equal to 3.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
An integer shape tuple (may include None entries).
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
ValueError: In case of invalid argument values.
|
|
36
|
+
"""
|
|
37
|
+
if weights != "imagenet" and input_shape and len(input_shape) == 3:
|
|
38
|
+
if data_format == "channels_first":
|
|
39
|
+
correct_channel_axis = 1 if len(input_shape) == 4 else 0
|
|
40
|
+
if input_shape[correct_channel_axis] not in {1, 3}:
|
|
41
|
+
warnings.warn(
|
|
42
|
+
"This model usually expects 1 or 3 input channels. "
|
|
43
|
+
"However, it was passed an input_shape "
|
|
44
|
+
f"with {input_shape[0]} input channels.",
|
|
45
|
+
stacklevel=2,
|
|
46
|
+
)
|
|
47
|
+
default_shape = (input_shape[0], default_size, default_size)
|
|
48
|
+
else:
|
|
49
|
+
if input_shape[-1] not in {1, 3}:
|
|
50
|
+
warnings.warn(
|
|
51
|
+
"This model usually expects 1 or 3 input channels. "
|
|
52
|
+
"However, it was passed an input_shape "
|
|
53
|
+
f"with {input_shape[-1]} input channels.",
|
|
54
|
+
stacklevel=2,
|
|
55
|
+
)
|
|
56
|
+
default_shape = (default_size, default_size, input_shape[-1])
|
|
57
|
+
else:
|
|
58
|
+
if data_format == "channels_first":
|
|
59
|
+
default_shape = (3, default_size, default_size)
|
|
60
|
+
else:
|
|
61
|
+
default_shape = (default_size, default_size, 3)
|
|
62
|
+
if weights == "imagenet" and require_flatten:
|
|
63
|
+
if input_shape is not None:
|
|
64
|
+
if input_shape != default_shape:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"When setting `include_top=True` "
|
|
67
|
+
"and loading `imagenet` weights, "
|
|
68
|
+
f"`input_shape` should be {default_shape}. "
|
|
69
|
+
f"Received: input_shape={input_shape}"
|
|
70
|
+
)
|
|
71
|
+
return default_shape
|
|
72
|
+
if input_shape:
|
|
73
|
+
if data_format == "channels_first":
|
|
74
|
+
if input_shape is not None:
|
|
75
|
+
if len(input_shape) != 3:
|
|
76
|
+
raise ValueError("`input_shape` must be a tuple of three integers.")
|
|
77
|
+
if input_shape[0] != 3 and weights == "imagenet":
|
|
78
|
+
raise ValueError(
|
|
79
|
+
"The input must have 3 channels; Received "
|
|
80
|
+
f"`input_shape={input_shape}`"
|
|
81
|
+
)
|
|
82
|
+
if (input_shape[1] is not None and input_shape[1] < min_size) or (
|
|
83
|
+
input_shape[2] is not None and input_shape[2] < min_size
|
|
84
|
+
):
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"Input size must be at least {min_size}"
|
|
87
|
+
f"x{min_size}; Received: "
|
|
88
|
+
f"input_shape={input_shape}"
|
|
89
|
+
)
|
|
90
|
+
else:
|
|
91
|
+
if input_shape is not None:
|
|
92
|
+
if len(input_shape) != 3:
|
|
93
|
+
raise ValueError("`input_shape` must be a tuple of three integers.")
|
|
94
|
+
if input_shape[-1] != 3 and weights == "imagenet":
|
|
95
|
+
raise ValueError(
|
|
96
|
+
"The input must have 3 channels; Received "
|
|
97
|
+
f"`input_shape={input_shape}`"
|
|
98
|
+
)
|
|
99
|
+
if (input_shape[0] is not None and input_shape[0] < min_size) or (
|
|
100
|
+
input_shape[1] is not None and input_shape[1] < min_size
|
|
101
|
+
):
|
|
102
|
+
raise ValueError(
|
|
103
|
+
"Input size must be at least "
|
|
104
|
+
f"{min_size}x{min_size}; Received: "
|
|
105
|
+
f"input_shape={input_shape}"
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
if require_flatten:
|
|
109
|
+
input_shape = default_shape
|
|
110
|
+
else:
|
|
111
|
+
if data_format == "channels_first":
|
|
112
|
+
input_shape = (3, None, None)
|
|
113
|
+
else:
|
|
114
|
+
input_shape = (None, None, 3)
|
|
115
|
+
if require_flatten:
|
|
116
|
+
if None in input_shape:
|
|
117
|
+
raise ValueError(
|
|
118
|
+
"If `include_top` is True, "
|
|
119
|
+
"you should specify a static `input_shape`. "
|
|
120
|
+
f"Received: input_shape={input_shape}"
|
|
121
|
+
)
|
|
122
|
+
return input_shape
|
{segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras.egg-info/SOURCES.txt
RENAMED
|
@@ -20,7 +20,8 @@ segmodels_keras/backbones/_weights.py
|
|
|
20
20
|
segmodels_keras/backbones/backbones_factory.py
|
|
21
21
|
segmodels_keras/backbones/inception_resnet_v2.py
|
|
22
22
|
segmodels_keras/backbones/inception_v3.py
|
|
23
|
-
segmodels_keras/backbones/
|
|
23
|
+
segmodels_keras/backbones/resnet_18_34.py
|
|
24
|
+
segmodels_keras/backbones/resnet_common.py
|
|
24
25
|
segmodels_keras/base/__init__.py
|
|
25
26
|
segmodels_keras/base/functional.py
|
|
26
27
|
segmodels_keras/base/objects.py
|
|
@@ -1,494 +0,0 @@
|
|
|
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__
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/_weights.py
RENAMED
|
File without changes
|
{segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/backbones/inception_v3.py
RENAMED
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/base/functional.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras/models/__init__.py
RENAMED
|
File without changes
|
{segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/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.dev7 → segmodels_keras-0.1.0.dev8}/segmodels_keras.egg-info/requires.txt
RENAMED
|
File without changes
|
{segmodels_keras-0.1.0.dev7 → segmodels_keras-0.1.0.dev8}/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
|
|
File without changes
|