This is the biggest difference from PyTorch. Instead of writing the training loop by hand, you configure it with compile() and run it with fit() — convenience for control.
Why: in PyTorch you write the five-step loop (forward, loss, zero_grad, backward, step); Keras does it for you — compile() configures the optimizer, loss, and metrics, and fit() runs the whole loop including validation and progress bars. When: use fit() for standard supervised training; drop to a custom loop (later lesson) only when you need it. Where: this is the core trade — less code and boilerplate, but the loop’s mechanics are hidden.
from tensorflow import keras
model.compile(
optimizer=keras.optimizers.Adam(1e-3), # like torch.optim.Adam
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=["accuracy"], # computed for you
)
# One call runs the ENTIRE loop PyTorch made you write by hand:
model.fit(X_train, y_train, validation_data=(X_val, y_val),
epochs=10, batch_size=32)Why: the loss choices are the same as PyTorch, just named differently — the one gotcha is that Keras losses expect probabilities by default, so you pass from_logits=True to get the numerically-stable behavior PyTorch’s CrossEntropyLoss gives automatically. When: set from_logits=True whenever your last layer outputs raw scores (no softmax/sigmoid). Where: "Sparse" categorical crossentropy takes integer labels (like PyTorch), the non-sparse version takes one-hot.
PyTorch loss Keras loss (this course)
------------------------------ ---------------------------------------
nn.CrossEntropyLoss SparseCategoricalCrossentropy(from_logits=True)
(integer labels, logits) (integer labels)
one-hot cross-entropy CategoricalCrossentropy(from_logits=True)
nn.BCEWithLogitsLoss BinaryCrossentropy(from_logits=True)
nn.MSELoss MeanSquaredError()
KEY: pass from_logits=True when your last layer has NO softmax/sigmoid —
Keras adds the activation internally, stably (what PyTorch does for you).Why: after training, evaluate() computes the loss and metrics on a test set and predict() produces outputs — the direct analogs of a PyTorch eval loop and model(x), but without manually switching modes or wrapping in no_grad. When: call evaluate() for final metrics, predict() for inference. Where: Keras handles train/eval mode (dropout, batch norm) automatically inside fit/evaluate/predict, so there is no model.train()/model.eval() to remember.
# Final metrics on held-out data (like a PyTorch eval loop):
loss, acc = model.evaluate(X_test, y_test)
print(f"test accuracy: {acc:.3f}")
# Inference (like model(x) under torch.no_grad() — Keras handles eval mode):
import numpy as np
logits = model.predict(X_test[:5])
preds = np.argmax(logits, axis=1)