Skip to content

Commit 41ab259

Browse files
Fixing VGG Architecture: (#2618)
* Fixing VGG Architecture: * Use `pooling=flatten` correctly * Use ReLU activation function in classification head Conv2D/Dense layers Signed-off-by: Bernhard Bermeitinger <bernhard.bermeitinger@unisg.ch> * Update keras_hub/src/models/vgg/vgg_image_classifier.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Align dropout layer names Signed-off-by: Bernhard Bermeitinger <bernhard.bermeitinger@unisg.ch> * Apply suggestions Signed-off-by: Bernhard Bermeitinger <bernhard.bermeitinger@unisg.ch> * Change first conv layer's padding in head to "valid" The file `tools/checkpoint_conversion/convert_vgg_checkpoints.py` shows for each of the 4 presets a modeling and preprocessing difference <1e-6. Signed-off-by: Bernhard Bermeitinger <bernhard.bermeitinger@unisg.ch> --------- Signed-off-by: Bernhard Bermeitinger <bernhard.bermeitinger@unisg.ch> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent b872721 commit 41ab259

2 files changed

Lines changed: 68 additions & 52 deletions

File tree

keras_hub/src/models/vgg/vgg_image_classifier.py

Lines changed: 65 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ class VGGImageClassifier(ImageClassifier):
2121
To fine-tune with `fit()`, pass a dataset containing tuples of `(x, y)`
2222
labels where `x` is a string and `y` is a integer from `[0, num_classes)`.
2323
24-
Not that unlike `keras_hub.model.ImageClassifier`, the `VGGImageClassifier`
25-
allows and defaults to `pooling="flatten"`, when inputs are flatten and
24+
Note that unlike `keras_hub.model.ImageClassifier`, the `VGGImageClassifier`
25+
allows `pooling="flatten"`, where the backbone outputs are flattened and
2626
passed through two intermediate dense layers before the final output
2727
projection.
2828
@@ -33,11 +33,12 @@ class VGGImageClassifier(ImageClassifier):
3333
a `keras.Layer` instance, or a callable. If `None` no preprocessing
3434
will be applied to the inputs.
3535
pooling: `"flatten"`, `"avg"`, or `"max"`. The type of pooling to apply
36-
on backbone output. The default is flatten to match the original
37-
VGG implementation, where backbone inputs will be flattened and
38-
passed through two dense layers with a `"relu"` activation.
39-
pooling_hidden_dim: the output feature size of the pooling dense layers.
40-
This only applies when `pooling="flatten"`.
36+
on the backbone output. The default is `"avg"`.
37+
pooling_hidden_dim: the output feature size of the classification head
38+
before the last `Dense` layer. Defaults to 4096 to match the
39+
original VGG implementation.
40+
dropout: float. The dropout rate to apply on the classification head.
41+
Defaults to 0.0 (no dropout).
4142
activation: `None`, str, or callable. The activation function to use on
4243
the `Dense` layer. Set `activation=None` to return the output
4344
logits. Defaults to `"softmax"`.
@@ -85,7 +86,7 @@ class VGGImageClassifier(ImageClassifier):
8586
```python
8687
images = np.random.randint(0, 256, size=(2, 224, 224, 3))
8788
labels = [0, 3]
88-
model = keras_hub.models.VGGBackbone(
89+
backbone = keras_hub.models.VGGBackbone(
8990
stackwise_num_repeats = [2, 2, 3, 3, 3],
9091
stackwise_num_filters = [64, 128, 256, 512, 512],
9192
image_shape = (224, 224, 3),
@@ -132,54 +133,68 @@ def __init__(
132133
name="pooler",
133134
)
134135
elif pooling == "flatten":
135-
self.pooler = keras.Sequential(
136+
self.pooler = keras.layers.Flatten(name="flatten")
137+
else:
138+
raise ValueError(
139+
"Unknown `pooling` type. Pooling should be either `'avg'`, "
140+
f"`'max'` or `'flatten'`. Received: pooling={pooling}."
141+
)
142+
143+
if pooling == "flatten":
144+
self.head = keras.Sequential(
136145
[
137-
keras.layers.Flatten(name="flatten"),
138-
keras.layers.Dense(pooling_hidden_dim, activation="relu"),
139-
keras.layers.Dense(pooling_hidden_dim, activation="relu"),
146+
self.pooler,
147+
keras.layers.Dense(
148+
pooling_hidden_dim, activation="relu", name="fc1"
149+
),
150+
keras.layers.Dropout(
151+
rate=dropout, dtype=head_dtype, name="output_dropout"
152+
),
153+
keras.layers.Dense(
154+
pooling_hidden_dim, activation="relu", name="fc2"
155+
),
156+
keras.layers.Dense(
157+
num_classes,
158+
activation=activation,
159+
dtype=head_dtype,
160+
name="predictions",
161+
),
140162
],
141-
name="pooler",
163+
name="head",
142164
)
143165
else:
144-
raise ValueError(
145-
"Unknown `pooling` type. Polling should be either `'avg'` or "
146-
f"`'max'`. Received: pooling={pooling}."
166+
self.head = keras.Sequential(
167+
[
168+
keras.layers.Conv2D(
169+
filters=pooling_hidden_dim,
170+
kernel_size=7,
171+
name="fc1",
172+
activation="relu",
173+
use_bias=True,
174+
padding="valid",
175+
),
176+
keras.layers.Dropout(
177+
rate=dropout, dtype=head_dtype, name="output_dropout"
178+
),
179+
keras.layers.Conv2D(
180+
filters=pooling_hidden_dim,
181+
kernel_size=1,
182+
name="fc2",
183+
activation="relu",
184+
use_bias=True,
185+
padding="same",
186+
),
187+
self.pooler,
188+
keras.layers.Dense(
189+
num_classes,
190+
activation=activation,
191+
dtype=head_dtype,
192+
name="predictions",
193+
),
194+
],
195+
name="head",
147196
)
148197

149-
self.head = keras.Sequential(
150-
[
151-
keras.layers.Conv2D(
152-
filters=4096,
153-
kernel_size=7,
154-
name="fc1",
155-
activation=activation,
156-
use_bias=True,
157-
padding="same",
158-
),
159-
keras.layers.Dropout(
160-
rate=dropout,
161-
dtype=head_dtype,
162-
name="output_dropout",
163-
),
164-
keras.layers.Conv2D(
165-
filters=4096,
166-
kernel_size=1,
167-
name="fc2",
168-
activation=activation,
169-
use_bias=True,
170-
padding="same",
171-
),
172-
self.pooler,
173-
keras.layers.Dense(
174-
num_classes,
175-
activation=activation,
176-
dtype=head_dtype,
177-
name="predictions",
178-
),
179-
],
180-
name="head",
181-
)
182-
183198
# === Functional Model ===
184199
inputs = self.backbone.input
185200
x = self.backbone(inputs)

keras_hub/src/utils/timm/convert_vgg.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ def convert_head(
7171
loader,
7272
timm_config: dict[Any],
7373
):
74-
convert_conv2d(task.head, loader, "fc1", "pre_logits.fc1")
75-
convert_conv2d(task.head, loader, "fc2", "pre_logits.fc2")
74+
if task.pooling != "flatten":
75+
convert_conv2d(task.head, loader, "fc1", "pre_logits.fc1")
76+
convert_conv2d(task.head, loader, "fc2", "pre_logits.fc2")
7677

7778
loader.port_weight(
7879
task.head.get_layer("predictions").kernel,

0 commit comments

Comments
 (0)