skip to content

Keras

Keras is the high-level modelling API — layers, models, compile and fit, callbacks — now able to run on more than one backend. It is where most interviews start when the question is build me a network rather than implement the loop.

on this pageshow

explore

questions

30

In Keras, what does ModelCheckpoint(save_best_only=True) change about when a file is written?

level: juniorimportance: must knowfreq 70%

answer

  1. writes only when the number improves
  2. monitor plus mode decides improvement
  3. default is write every epoch
  4. Keras 3 wants a .keras path
  5. missing monitor key warns, then skips

basics

~20 s

With save_best_only=True, ModelCheckpoint writes a file only on epochs where the monitored value improves on the best seen so far. With the default False it writes every epoch. The monitored value is named by the monitor argument, which defaults to val_loss.

solid answer

~40 s

`keras.callbacks.ModelCheckpoint` is passed to `fit(callbacks=[...])` and saves the model at the end of each epoch (or every N batches with `save_freq=N`). By default `save_best_only=False`, so it writes unconditionally. Setting it to `True` makes the callback compare `logs[monitor]` against the best value it has seen and write only on an improvement — where "improvement" means lower for `mode="min"`, higher for `mode="max"`, and `mode="auto"` guesses from the metric name. Two practical points in Keras 3: the `filepath` must end in `.keras` for a full-model save, or `.weights.h5` when `save_weights_only=True`, and the path is a format template, so `"m.{epoch:02d}-{val_loss:.3f}.keras"` gets filled from the epoch index and the logs dict. If the monitored key is absent from logs — a typo, or `val_loss` with no validation data — the callback warns and silently skips saving.

code

python · 16 lines
python
import keras

model = keras.Sequential([keras.Input(shape=(4,)), keras.layers.Dense(1)])
model.compile(optimizer="adam", loss="mse")

best_only = keras.callbacks.ModelCheckpoint(
    filepath="ckpt/best.keras",
    monitor="val_loss",
    mode="min",
    save_best_only=True,
    verbose=1,
)

every_epoch = keras.callbacks.ModelCheckpoint(
    filepath="ckpt/model.{epoch:02d}-{val_loss:.3f}.keras",
)

go deeper

for a junior

Know that without save_best_only=True you keep the last epoch, not the best one, and that monitor names a key such as val_loss or val_accuracy that must actually be produced by the run.

for a middle

Explain how mode decides the comparison direction, how the filepath template is filled from the epoch index and the logs dict, and why a missing monitor key warns and skips instead of raising.

for a senior

Show the operational side: one constant path versus templated paths and disk growth, the difference between a full .keras save and a weights-only file when resuming, and how you detect a run that quietly checkpointed nothing.

for a principal

Own the policy — what artifact a training job is contractually required to emit, whether best-only or every-N-batches suits the failure modes of your infrastructure, and how checkpoint naming and retention feed the downstream promotion process.

## What the callback does `keras.callbacks.ModelCheckpoint` is the standard way to persist a model *during* a `fit()` run rather than after it. You never call it yourself; you construct it and hand it to `model.fit(..., callbacks=[ckpt])`, and Keras invokes its hooks around each epoch and batch. Its job is narrow: decide *whether* to save right now, and *what* to write. ``` ckpt = keras.callbacks.ModelCheckpoint( filepath="ckpt/best.keras", monitor="val_loss", mode="min", save_best_only=True, ) ``` ## save_best_only, and what "best" means With the default `save_best_only=False`, the callback writes at the end of every epoch. If `filepath` is a constant string, each write overwrites the previous file, so you end up with the *last* epoch, not the best one. That is the classic beginner outcome: training visibly overfit after epoch 12, and the file on disk is epoch 40. With `save_best_only=True`, the callback keeps an internal `best` value. At the end of each epoch it reads `logs[self.monitor]` and compares. The comparison direction comes from `mode`: - `mode="min"` — an improvement is a *smaller* value. Correct for losses and error metrics. - `mode="max"` — an improvement is a *larger* value. Correct for accuracy, AUC, F1. - `mode="auto"` (the default) — Keras infers the direction from the metric name, treating names that look like accuracy/AUC as `max` and everything else as `min`. `auto` is right most of the time and wrong exactly when your metric has an unusual name. If you have a custom metric where higher is better and the name does not advertise it, set `mode` explicitly rather than trusting the guess. ## The monitor key must actually exist The monitored value is looked up in the `logs` dict Keras passes to the callback at epoch end. That dict contains the epoch's training metrics under their metric names, plus validation metrics prefixed with `val_` — but the `val_` entries only exist when `fit()` was given `validation_data` or `validation_split`. So `monitor="val_loss"` with no validation data, or `monitor="val_acc"` when the compiled metric is named `accuracy` (giving `val_accuracy`), both miss. Keras does not raise; it emits a warning saying the monitored value is not available and skips the save. A run can therefore complete with an empty checkpoint directory and no error. When a checkpoint file is mysteriously missing, check the metric name first. ## The filepath is a template `filepath` is run through Python string formatting with the epoch number and the contents of `logs`, so `"model.{epoch:02d}-{val_loss:.3f}.keras"` produces `model.07-0.312.keras`. A templated path keeps every write as a separate file — useful for inspecting a training trajectory, dangerous for disk usage on a long run. A constant path keeps exactly one file. Combining a constant path with `save_best_only=True` is the usual production choice: one file, always the best epoch. ## Keras 3 extension rules and what is inside the file Keras 3 validates the suffix. With `save_weights_only=False` (the default) the path must end in `.keras`, the backend-agnostic v3 archive; with `save_weights_only=True` it must end in `.weights.h5`. Passing the wrong suffix raises rather than guessing. The distinction matters beyond naming: a full `.keras` save carries architecture, weights and the optimizer's state, so reloading gives you an optimizer that can continue training sensibly. A weights-only file carries the weight values alone — trainable kernels and biases, plus non-trainable weights such as BatchNormalization moving statistics — but no optimizer slots, so resuming from it restarts the optimizer's momentum from scratch. ## save_freq `save_freq="epoch"` is the default. Passing an integer switches to "every N training batches", which is how you checkpoint inside a very long epoch. Note that `save_best_only` still needs a monitored value to compare, and validation metrics only refresh once per epoch, so mid-epoch best-only checkpointing on a `val_` metric compares against a stale number. ## What it does not do ModelCheckpoint writes artifacts; it does not resume anything. Restarting a killed run means loading the file yourself and passing `initial_epoch` to `fit()`, and the callback keeps no memory of its `best` value across processes — a fresh run starts with an empty best and will happily overwrite a better file from the previous run.

  • What actually happens if you set monitor to a metric name that never appears in the logs?
    Nothing fatal. At epoch end the callback looks the key up in the logs dict, finds it missing, emits a warning that the monitored value is unavailable, and skips the save for that epoch. Repeat for every epoch and the run finishes with no checkpoint written and no exception raised — which is why a silently empty checkpoint directory usually means a metric-name typo or missing validation data.
  • How do save_best_only and save_weights_only interact, and when would you pick weights-only?
    They are independent: one decides *when* to write, the other decides *what*. Weights-only makes sense when you already have the architecture in code and want small, fast writes — but it drops the optimizer state, so a resumed run restarts momentum and any optimizer accumulators from zero. For anything you intend to continue training from, save the full `.keras` model.
  • You keep one constant filepath with save_best_only=True, then restart training in a new process. What is the risk?
    The callback's internal `best` starts empty in the new process, so the first epoch of the restarted run counts as an improvement and overwrites the file — potentially replacing a genuinely better model from the earlier run. Either write to a fresh directory per run, or template the filepath so old files survive.

saying these in an interview costs you the question

  • Believes save_best_only=True is the default
  • Assumes the final epoch's weights are the best
  • Monitors val_loss with no validation data supplied
  • Uses mode='min' while monitoring val_accuracy
  • Thinks a weights-only file also stores optimizer state

context

open as a page

In Keras, what does Dense(64) output for an input of shape (32, 10, 8)?

level: juniorimportance: must knowfreq 65%

basics

~20 s

Shape (32, 10, 64). A Dense layer transforms only the last axis, reusing one kernel of shape (8, 64) at every one of the 10 positions, so it holds 8 * 64 + 64 = 576 parameters no matter the batch or sequence length.

open as a page

In Keras, when is Sequential not enough and you need the Functional API?

level: juniorimportance: must knowfreq 82%

basics

~20 s

keras.Sequential models a straight stack: one input, one output, each layer feeding the next. Anything with branches, several inputs or outputs, skip connections, or one layer instance reused twice needs the Functional API, which wires an explicit graph of layer calls.

open as a page

In Keras 3, what does model.save('model.keras') store and how do you load it?

level: juniorimportance: must knowfreq 72%

basics

~10 s

model.save('model.keras') writes one zip archive holding the architecture config, the weights and the optimizer state. keras.saving.load_model('model.keras') rebuilds the whole model, ready to predict or resume training without re-running the build code.

open as a page

In Keras, what does model.compile() configure before you call fit()?

level: juniorimportance: must knowfreq 78%

basics

~20 s

model.compile() attaches the training configuration to a model: the optimizer that updates the weights, the loss that fit() minimises, and the metrics reported each epoch. Until you call it, fit() and evaluate() have nothing to optimise and raise an error.

open as a page

When subclassing keras.callbacks.Callback, what is in logs at on_epoch_end vs on_train_batch_end?

level: middleimportance: must knowfreq 58%

basics

~20 s

At on_epoch_end, logs holds the epoch's training metrics plus validation metrics under val_-prefixed keys. At on_train_batch_end, only training metrics are present — validation has not run — and their values are running aggregates over the epoch so far, not that single batch.

open as a page

In Keras EarlyStopping, what do patience and restore_best_weights actually control?

level: middleimportance: must knowfreq 76%

basics

~20 s

patience is how many consecutive epochs without improvement in the monitored value Keras tolerates before halting. restore_best_weights, which defaults to False, decides whether the model is rolled back to the best epoch's weights — leave it off and you keep the final, already-degraded weights.

open as a page

In a custom Keras 3 Layer, what goes in build() versus call()?

level: middleimportance: must knowfreq 75%

basics

~10 s

build(input_shape) creates the weights, and Keras runs it once on the first input, when the shape is finally known. call(inputs) does only the forward computation. Creating weights inside call() is a bug.

open as a page

What does the training argument in a Keras layer's call() do, and who sets it?

level: middleimportance: must knowfreq 70%

basics

~20 s

It selects train-time versus inference behaviour per call. Dropout drops units and BatchNormalization uses batch statistics only when it is true. fit() passes True, evaluate() and predict() pass False, and a bare model(x) defaults to inference.

open as a page

Why does model.summary() fail on a subclassed Keras model before it runs?

level: middleimportance: must knowfreq 64%

basics

~20 s

A subclassed keras.Model has no declared input, so Keras cannot know any shapes until data flows through call(). Until then the model is unbuilt, its sublayers have no weights, and summary() raises a ValueError. Calling model.build(input_shape=...) or running one batch fixes it.

open as a page

How do you choose the Keras 3 backend, and when must that choice happen?

level: middleimportance: must knowfreq 60%

basics

~20 s

Set the KERAS_BACKEND environment variable to tensorflow, jax or torch before the first import of keras, or set the backend field in the ~/.keras/keras.json config file. The choice is fixed for the process once keras is imported; keras.backend.backend() reports it.

open as a page

Why does Keras load_model fail to locate your custom layer, and how do you fix it?

level: middleimportance: must knowfreq 68%

basics

~20 s

The .keras archive stores a class name and its constructor arguments, never the code. On load, Keras must map that name to a real Python class; if it cannot, the load fails. Fix it with @keras.saving.register_keras_serializable or by passing custom_objects.

open as a page

In Keras compile(), when must the loss be given from_logits=True?

level: middleimportance: must knowfreq 58%

basics

~20 s

Set from_logits=True whenever the model's last layer emits raw unbounded scores with no softmax or sigmoid activation. The loss then applies the activation internally, which is numerically safer. If the last layer already outputs probabilities, leave it False.

open as a page

In Keras fit(), how does validation_split differ from validation_data?

level: middleimportance: must knowfreq 68%

basics

~20 s

validation_split=0.2 tells fit() to carve a validation set out of the arrays you passed, taking the last 20% of the rows before any shuffling. validation_data hands fit() a set you split yourself. The split argument works only for in-memory arrays.

open as a page

In Keras, when do you use ReduceLROnPlateau instead of LearningRateScheduler?

level: middleimportance: should knowfreq 48%

basics

~20 s

LearningRateScheduler applies a rate you compute from the epoch index — a curve fixed before the run starts. ReduceLROnPlateau is reactive: it multiplies the current rate by factor only after a monitored metric fails to improve for patience epochs. Use it when you cannot pick the curve in advance.

open as a page

In a custom Keras Layer, how do you hold state that gradients never update?

level: middleimportance: should knowfreq 45%

basics

~20 s

Create it with self.add_weight(..., trainable=False) and mutate it with .assign() or .assign_add(). It becomes a tracked keras.Variable listed under layer.non_trainable_weights, saved with the model, and ignored by the optimizer — exactly how BatchNormalization keeps its moving statistics.

open as a page

In Keras, how do you extract intermediate layer activations from a model?

level: middleimportance: should knowfreq 50%

basics

~20 s

For a Functional model, build a second model over the same graph: keras.Model(inputs=model.inputs, outputs=model.get_layer("hidden").output). It reuses the same layer objects, so weights are shared and nothing is retrained. Subclassed models have no recorded graph, so this does not work on them.

open as a page

In Keras, how do you wire a multi-input, multi-output Functional model?

level: middleimportance: should knowfreq 58%

basics

~20 s

Create one keras.Input per input, build each branch, merge them with a layer such as Concatenate or Add, then create one head per output. Pass the lists (or dicts) to keras.Model(inputs=[...], outputs=[...]); name the inputs and output layers so they can be referred to later.

open as a page

When would you use Keras save_weights instead of saving the whole model?

level: middleimportance: should knowfreq 58%

basics

~20 s

Use save_weights when the architecture already lives in code you trust: it writes only variable values to a .weights.h5 file, so loading needs a model that is already built with the identical structure. Whole-model .keras saving is for artifacts that must reconstruct themselves.

open as a page

In Keras fit(), how do class_weight and sample_weight differ?

level: middleimportance: should knowfreq 50%

basics

~20 s

class_weight is a dict mapping class index to a multiplier, so every sample of a class is weighted the same. sample_weight is an array with one weight per training row. Both scale the per-sample loss before it is averaged; only their granularity differs.

open as a page

A Keras fit() run is killed at epoch 40 of 100 — which callback resumes it automatically?

level: seniorimportance: should knowfreq 33%

basics

~20 s

keras.callbacks.BackupAndRestore(backup_dir=...). It snapshots model weights, optimizer state and the epoch counter as training proceeds, and on the next fit() call with the same backup_dir it restores them and continues from where it stopped. ModelCheckpoint only writes files; resuming from those is manual.

open as a page

Adding Keras's TensorBoard callback doubled epoch time — which settings cause that?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Per-step and per-weight work. update_freq='batch' writes a summary every training step, histogram_freq greater than zero walks every weight tensor each epoch, write_images renders them, and profile_batch runs the profiler. The defaults — update_freq='epoch', histogram_freq=0 — cost almost nothing.

open as a page

In Keras 3, what happens to a BatchNormalization layer when you set trainable=False?

level: seniorimportance: should knowfreq 50%

basics

~20 s

BatchNormalization is special-cased: trainable=False also puts it in inference mode, so it normalizes with the stored moving_mean and moving_variance and stops updating them. For every other layer, trainable=False only stops the optimizer from updating weights.

open as a page

How does a Keras mask from Embedding(mask_zero=True) reach an LSTM layer?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Embedding with mask_zero=True emits a boolean mask marking timesteps whose token id is 0. Keras attaches it to the layer's output and forwards it through every layer that declares supports_masking, so a downstream LSTM receives it and skips the padded steps.

open as a page

What do you give up by subclassing keras.Model instead of using the Functional API?

level: seniorimportance: should knowfreq 54%

basics

~20 s

You give up the static layer graph. Without it there is no shape inference before the first batch, summary and plotting are degraded, get_layer(name).output surgery is impossible, and the model no longer serializes from configuration alone. In return you get arbitrary Python control flow in the forward pass.

open as a page

What must a Keras custom layer's get_config return to survive a .keras round trip?

level: seniorimportance: should knowfreq 46%

basics

~10 s

get_config must return a JSON-serializable dict carrying every constructor argument, merged over super().get_config(). Anything omitted comes back as a default after reload. Nested Keras objects need keras.saving.serialize_keras_object plus a from_config override that deserializes them.

open as a page

Why does calling framework ops inside a Keras 3 custom layer break portability?

level: seniorimportance: should knowfreq 42%

basics

~20 s

A layer whose call() invokes one framework's tensor API only runs when that framework is the active backend, so the saved model silently stops being portable. keras.ops is the backend-agnostic numerics layer, and keras.random is its counterpart for random numbers.

open as a page

Why does a Keras function metric report the wrong F1 for an epoch?

level: seniorimportance: should knowfreq 36%

basics

~20 s

A plain function passed in compile(metrics=[...]) is wrapped so that the epoch value is the mean of its per-batch values. F1, precision and recall are ratios of accumulated counts, and an average of per-batch ratios is not the ratio over the epoch. Use a stateful keras.metrics.Metric instead.

open as a page

In Keras 3, how do you override train_step(), and what must it return?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Subclass keras.Model and define train_step(self, data): unpack the batch, run the forward pass, compute the loss with self.compute_loss(), apply the gradients through self.optimizer, update self.metrics, and return a dict mapping metric names to their current results.

open as a page

Which Keras model API should a team standardize on across a shared codebase?

level: principalimportance: should knowfreq 32%

basics

~20 s

There is no universal answer, but a workable policy is: Functional as the default assembly layer, custom Layer subclasses as the extension point, Model subclassing reserved for genuinely dynamic forward passes, and Sequential kept for baselines. Decide on export, review and tooling needs rather than taste.

open as a page