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.