Skip to content

Commit 0fcd6a8

Browse files
authored
Merge pull request keras-team#4 from ianstenbit/core-csp
Port CSPDarkNet and DarkNet to Keras Core
2 parents 9e2774f + 3c14acd commit 0fcd6a8

File tree

9 files changed

+273
-81
lines changed

9 files changed

+273
-81
lines changed

.github/workflows/actions.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ jobs:
8585
pytest keras_cv/models/backbones/resnet_v1 \
8686
keras_cv/models/backbones/resnet_v2 \
8787
keras_cv/models/backbones/efficientnet_v2 \
88+
keras_cv/models/backbones/csp_darknet \
8889
keras_cv/models/classification/image_classifier_test.py \
8990
--durations 0 --run_large --run_extra_large
9091
format:

keras_cv/backend/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,7 @@
6161
else:
6262
from tensorflow import keras
6363

64+
# TF Keras doesn't have this rename.
65+
keras.activations.silu = keras.activations.swish
66+
6467
from keras_cv.backend import ops # noqa: E402

keras_cv/models/backbones/csp_darknet/csp_darknet_backbone.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
"""CSPDarkNet models for KerasCV. """
1616
import copy
1717

18-
from tensorflow import keras
19-
from tensorflow.keras import layers
20-
18+
from keras_cv.backend import keras
2119
from keras_cv.models import utils
2220
from keras_cv.models.backbones.backbone import Backbone
2321
from keras_cv.models.backbones.csp_darknet.csp_darknet_backbone_presets import (
@@ -64,8 +62,8 @@ class CSPDarkNetBackbone(Backbone):
6462
use_depthwise: bool, whether a `DarknetConvBlockDepthwise` should be
6563
used over a `DarknetConvBlock`, defaults to False.
6664
input_shape: optional shape tuple, defaults to (None, None, 3).
67-
input_tensor: optional Keras tensor (i.e. output of `layers.Input()`)
68-
to use as image input for the model.
65+
input_tensor: optional Keras tensor (i.e. output of
66+
`keras.layers.Input()`) to use as image input for the model.
6967
7068
Returns:
7169
A `keras.Model` instance.
@@ -111,7 +109,7 @@ def __init__(
111109

112110
x = inputs
113111
if include_rescaling:
114-
x = layers.Rescaling(1 / 255.0)(x)
112+
x = keras.layers.Rescaling(1 / 255.0)(x)
115113

116114
# stem
117115
x = Focus(name="stem_focus")(x)
@@ -144,7 +142,9 @@ def __init__(
144142
residual=(index != len(stackwise_depth) - 1),
145143
name=f"dark{index + 2}_csp",
146144
)(x)
147-
pyramid_level_inputs[f"P{index + 2}"] = x.node.layer.name
145+
pyramid_level_inputs[f"P{index + 2}"] = utils.get_tensor_input_name(
146+
x
147+
)
148148

149149
super().__init__(inputs=inputs, outputs=x, **kwargs)
150150
self.pyramid_level_inputs = pyramid_level_inputs
@@ -195,8 +195,8 @@ def presets_with_weights(cls):
195195
Args:
196196
include_rescaling: bool, whether or not to rescale the inputs. If set to
197197
True, inputs will be passed through a `Rescaling(1/255.0)` layer.
198-
input_tensor: optional Keras tensor (i.e. output of `layers.Input()`)
199-
to use as image input for the model.
198+
input_tensor: optional Keras tensor (i.e. output of
199+
`keras.layers.Input()`) to use as image input for the model.
200200
input_shape: optional shape tuple, defaults to (None, None, 3).
201201
202202
Examples:

keras_cv/models/backbones/csp_darknet/csp_darknet_backbone_test.py

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@
1414

1515
import os
1616

17+
import numpy as np
1718
import pytest
1819
import tensorflow as tf
1920
from absl.testing import parameterized
20-
from tensorflow import keras
2121

22+
from keras_cv.backend import keras
2223
from keras_cv.models.backbones.csp_darknet import csp_darknet_backbone
2324
from keras_cv.utils.train import get_feature_extractor
2425

2526

2627
class CSPDarkNetBackboneTest(tf.test.TestCase, parameterized.TestCase):
2728
def setUp(self):
28-
self.input_batch = tf.ones(shape=(2, 224, 224, 3))
29+
self.input_batch = np.ones(shape=(2, 224, 224, 3))
2930

3031
def test_valid_call(self):
3132
model = csp_darknet_backbone.CSPDarkNetBackbone(
@@ -47,20 +48,18 @@ def test_valid_call_with_rescaling(self):
4748
)
4849
model(self.input_batch)
4950

50-
@parameterized.named_parameters(
51-
("tf_format", "tf", "model"),
52-
("keras_format", "keras_v3", "model.keras"),
53-
)
5451
@pytest.mark.large # Saving is slow, so mark these large.
55-
def test_saved_model(self, save_format, filename):
52+
def test_saved_model(self):
5653
model = csp_darknet_backbone.CSPDarkNetBackbone(
5754
stackwise_channels=[48, 96, 192, 384],
5855
stackwise_depth=[1, 3, 3, 1],
5956
include_rescaling=True,
6057
)
6158
model_output = model(self.input_batch)
62-
save_path = os.path.join(self.get_temp_dir(), filename)
63-
model.save(save_path, save_format=save_format)
59+
save_path = os.path.join(
60+
self.get_temp_dir(), "csp_darknet_backbone.keras"
61+
)
62+
model.save(save_path)
6463
restored_model = keras.models.load_model(save_path)
6564

6665
# Check we got the real object back.
@@ -72,16 +71,14 @@ def test_saved_model(self, save_format, filename):
7271
restored_output = restored_model(self.input_batch)
7372
self.assertAllClose(model_output, restored_output)
7473

75-
@parameterized.named_parameters(
76-
("tf_format", "tf", "model"),
77-
("keras_format", "keras_v3", "model.keras"),
78-
)
7974
@pytest.mark.large # Saving is slow, so mark these large.
80-
def test_saved_alias_model(self, save_format, filename):
75+
def test_saved_alias_model(self):
8176
model = csp_darknet_backbone.CSPDarkNetLBackbone()
8277
model_output = model(self.input_batch)
83-
save_path = os.path.join(self.get_temp_dir(), filename)
84-
model.save(save_path, save_format=save_format)
78+
save_path = os.path.join(
79+
self.get_temp_dir(), "csp_darknet_backbone.keras"
80+
)
81+
model.save(save_path)
8582
restored_model = keras.models.load_model(save_path)
8683

8784
# Check we got the real object back.
@@ -109,10 +106,10 @@ def test_create_backbone_model_from_alias_model(self):
109106
levels = ["P2", "P3", "P4", "P5"]
110107
self.assertLen(outputs, 4)
111108
self.assertEquals(list(outputs.keys()), levels)
112-
self.assertEquals(outputs["P2"].shape, [None, 56, 56, 128])
113-
self.assertEquals(outputs["P3"].shape, [None, 28, 28, 256])
114-
self.assertEquals(outputs["P4"].shape, [None, 14, 14, 512])
115-
self.assertEquals(outputs["P5"].shape, [None, 7, 7, 1024])
109+
self.assertEquals(outputs["P2"].shape, (None, 56, 56, 128))
110+
self.assertEquals(outputs["P3"].shape, (None, 28, 28, 256))
111+
self.assertEquals(outputs["P4"].shape, (None, 14, 14, 512))
112+
self.assertEquals(outputs["P5"].shape, (None, 7, 7, 1024))
116113

117114
def test_create_backbone_model_with_level_config(self):
118115
model = csp_darknet_backbone.CSPDarkNetBackbone(
@@ -127,8 +124,8 @@ def test_create_backbone_model_with_level_config(self):
127124
outputs = backbone_model(inputs)
128125
self.assertLen(outputs, 2)
129126
self.assertEquals(list(outputs.keys()), levels)
130-
self.assertEquals(outputs["P3"].shape, [None, 32, 32, 96])
131-
self.assertEquals(outputs["P4"].shape, [None, 16, 16, 192])
127+
self.assertEquals(outputs["P3"].shape, (None, 32, 32, 96))
128+
self.assertEquals(outputs["P4"].shape, (None, 16, 16, 192))
132129

133130
@parameterized.named_parameters(
134131
("Tiny", csp_darknet_backbone.CSPDarkNetTinyBackbone),
@@ -139,7 +136,7 @@ def test_create_backbone_model_with_level_config(self):
139136
)
140137
def test_specific_arch_forward_pass(self, arch_class):
141138
backbone = arch_class()
142-
backbone(tf.random.uniform(shape=[2, 256, 256, 3]))
139+
backbone(np.random.uniform(size=(2, 256, 256, 3)))
143140

144141
@parameterized.named_parameters(
145142
("Tiny", csp_darknet_backbone.CSPDarkNetTinyBackbone),

keras_cv/models/backbones/csp_darknet/csp_darknet_utils.py

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@
1818
- [YoloV3 implementation](https://github.com/ultralytics/yolov3)
1919
"""
2020

21-
import tensorflow as tf
22-
from tensorflow import keras
23-
from tensorflow.keras import backend
24-
from tensorflow.keras import layers
21+
from keras_cv.backend import keras
2522

2623

2724
def DarknetConvBlock(
@@ -46,28 +43,30 @@ def DarknetConvBlock(
4643
"""
4744

4845
if name is None:
49-
name = f"conv_block{backend.get_uid('conv_block')}"
46+
name = f"conv_block{keras.backend.get_uid('conv_block')}"
5047

5148
model_layers = [
52-
layers.Conv2D(
49+
keras.layers.Conv2D(
5350
filters,
5451
kernel_size,
5552
strides,
5653
padding="same",
5754
use_bias=use_bias,
5855
name=name + "_conv",
5956
),
60-
layers.BatchNormalization(name=name + "_bn"),
57+
keras.layers.BatchNormalization(name=name + "_bn"),
6158
]
6259

6360
if activation == "silu":
64-
model_layers.append(layers.Lambda(lambda x: keras.activations.swish(x)))
61+
model_layers.append(
62+
keras.layers.Lambda(lambda x: keras.activations.silu(x))
63+
)
6564
elif activation == "relu":
66-
model_layers.append(layers.ReLU())
65+
model_layers.append(keras.layers.ReLU())
6766
elif activation == "leaky_relu":
68-
model_layers.append(layers.LeakyReLU(0.1))
67+
model_layers.append(keras.layers.LeakyReLU(0.1))
6968

70-
return keras.Sequential(model_layers, name=None)
69+
return keras.Sequential(model_layers, name=name)
7170

7271

7372
def ResidualBlocks(filters, num_blocks, name=None):
@@ -84,7 +83,7 @@ def ResidualBlocks(filters, num_blocks, name=None):
8483
"""
8584

8685
if name is None:
87-
name = f"residual_block{backend.get_uid('residual_block')}"
86+
name = f"residual_block{keras.backend.get_uid('residual_block')}"
8887

8988
def apply(x):
9089
x = DarknetConvBlock(
@@ -114,9 +113,9 @@ def apply(x):
114113
)(x)
115114

116115
if i == num_blocks:
117-
x = layers.Add(name=f"{name}_out")([residual, x])
116+
x = keras.layers.Add(name=f"{name}_out")([residual, x])
118117
else:
119-
x = layers.Add(name=f"{name}_add_{i}")([residual, x])
118+
x = keras.layers.Add(name=f"{name}_add_{i}")([residual, x])
120119

121120
return x
122121

@@ -149,7 +148,7 @@ def SpatialPyramidPoolingBottleneck(
149148
SpatialPyramidPoolingBottleneck.
150149
"""
151150
if name is None:
152-
name = f"spp{backend.get_uid('spp')}"
151+
name = f"spp{keras.backend.get_uid('spp')}"
153152

154153
if hidden_filters is None:
155154
hidden_filters = filters
@@ -166,15 +165,15 @@ def apply(x):
166165

167166
for kernel_size in kernel_sizes:
168167
x.append(
169-
layers.MaxPooling2D(
168+
keras.layers.MaxPooling2D(
170169
kernel_size,
171170
strides=1,
172171
padding="same",
173172
name=f"{name}_maxpool_{kernel_size}",
174173
)(x[0])
175174
)
176175

177-
x = layers.Concatenate(name=f"{name}_concat")(x)
176+
x = keras.layers.Concatenate(name=f"{name}_concat")(x)
178177
x = DarknetConvBlock(
179178
filters,
180179
kernel_size=1,
@@ -209,21 +208,23 @@ def DarknetConvBlockDepthwise(
209208
"""
210209

211210
if name is None:
212-
name = f"conv_block{backend.get_uid('conv_block')}"
211+
name = f"conv_block{keras.backend.get_uid('conv_block')}"
213212

214213
model_layers = [
215-
layers.DepthwiseConv2D(
214+
keras.layers.DepthwiseConv2D(
216215
kernel_size, strides, padding="same", use_bias=False
217216
),
218-
layers.BatchNormalization(),
217+
keras.layers.BatchNormalization(),
219218
]
220219

221220
if activation == "silu":
222-
model_layers.append(layers.Lambda(lambda x: keras.activations.swish(x)))
221+
model_layers.append(
222+
keras.layers.Lambda(lambda x: keras.activations.swish(x))
223+
)
223224
elif activation == "relu":
224-
model_layers.append(layers.ReLU())
225+
model_layers.append(keras.layers.ReLU())
225226
elif activation == "leaky_relu":
226-
model_layers.append(layers.LeakyReLU(0.1))
227+
model_layers.append(keras.layers.LeakyReLU(0.1))
227228

228229
model_layers.append(
229230
DarknetConvBlock(
@@ -235,7 +236,7 @@ def DarknetConvBlockDepthwise(
235236

236237

237238
@keras.utils.register_keras_serializable(package="keras_cv")
238-
class CrossStagePartial(layers.Layer):
239+
class CrossStagePartial(keras.layers.Layer):
239240
"""A block used in Cross Stage Partial Darknet.
240241
241242
Args:
@@ -309,8 +310,8 @@ def __init__(
309310
)
310311
)
311312

312-
self.add = layers.Add()
313-
self.concatenate = layers.Concatenate()
313+
self.add = keras.layers.Add()
314+
self.concatenate = keras.layers.Concatenate()
314315

315316
self.darknet_conv3 = DarknetConvBlock(
316317
filters, kernel_size=1, strides=1, activation=activation
@@ -360,17 +361,13 @@ def Focus(name=None):
360361
""" # noqa: E501
361362

362363
def apply(x):
363-
return layers.Lambda(
364-
lambda x: tf.concat(
365-
[
366-
x[..., ::2, ::2, :],
367-
x[..., 1::2, ::2, :],
368-
x[..., ::2, 1::2, :],
369-
x[..., 1::2, 1::2, :],
370-
],
371-
axis=-1,
372-
),
373-
name=name,
374-
)(x)
364+
return keras.layers.Concatenate(name=name)(
365+
[
366+
x[..., ::2, ::2, :],
367+
x[..., 1::2, ::2, :],
368+
x[..., ::2, 1::2, :],
369+
x[..., 1::2, 1::2, :],
370+
],
371+
)
375372

376373
return apply

0 commit comments

Comments
 (0)