C.W.K.
Stream
Lesson 04 of 06 · published

Model Subclassing — Maximum Flexibility

~13 min · subclassing, research, training-flag

Level 0Level 0
0 XP0/78 lessons0/17 achievements
0/100 XP to next level100 XP to go0% complete

When you need full Python control

Subclassing keras.Model gives you control over the forward pass via Python. You define layers in __init__ and computation in call(). This is the most flexible approach — used for dynamic architectures, conditional logic, recurrent cells with novel state, and research prototypes that don't fit a static graph.

The training argument matters. Layers like Dropout and BatchNormalization behave differently between training and inference. model.fit passes training=True automatically; in custom loops you must pass it explicitly: model(x, training=True) for training steps, model(x, training=False) for evaluation and inference.

Subclassing tradeoffs: model.summary() shows less detail. Saving/loading requires get_config() + from_config() for full architecture serialization. For models you plan to deploy, the Functional API is generally preferred unless subclassing is genuinely necessary.

Code

ResidualBlock + ResNet, subclassing 스타일·python
import tensorflow as tf
from tensorflow import keras
from keras import layers

class ResidualBlock(keras.layers.Layer):
    def __init__(self, filters, **kwargs):
        super().__init__(**kwargs)
        self.conv1 = layers.Conv2D(filters, 3, padding='same', activation='relu')
        self.conv2 = layers.Conv2D(filters, 3, padding='same')
        self.bn1 = layers.BatchNormalization()
        self.bn2 = layers.BatchNormalization()
        self.add = layers.Add()
        self.relu = layers.Activation('relu')

    def call(self, inputs, training=False):
        x = self.conv1(inputs)
        x = self.bn1(x, training=training)
        x = self.conv2(x)
        x = self.bn2(x, training=training)
        x = self.add([x, inputs])    # skip connection
        return self.relu(x)


class MyResNet(keras.Model):
    def __init__(self, num_classes=10, **kwargs):
        super().__init__(**kwargs)
        self.stem = layers.Conv2D(32, 3, padding='same', activation='relu')
        self.block1 = ResidualBlock(32)
        self.pool = layers.GlobalAveragePooling2D()
        self.dropout = layers.Dropout(0.3)
        self.classifier = layers.Dense(num_classes, activation='softmax')

    def call(self, inputs, training=False):
        x = self.stem(inputs)
        x = self.block1(x, training=training)
        x = self.pool(x)
        x = self.dropout(x, training=training)
        return self.classifier(x)

model = MyResNet(num_classes=10)
model.build(input_shape=(None, 32, 32, 32))   # explicit build
model.summary()

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.