Loading...
Loading...
Loading Curriculum...
Loading Subject...
Loading Topic...
Loading Lesson...
TensorFlow is a deep learning framework with strong production tooling. Keras provides a high-level API for building and training models on CPU/GPU/TPU.
N-D arrays with automatic dispatch to CPU, GPU, or TPU. Operations mirror NumPy but add device management and autodiff.
High-level model building via Sequential, Functional, and Subclassing APIs. Handles the training loop, metrics, and callbacks for you.
Low-level autodiff for custom training loops. Record forward ops, then call tape.gradient() to get exact gradients for any variable.
Declarative data pipeline API. Chains map, filter, batch, shuffle, prefetch. AUTOTUNE overlaps preprocessing with GPU compute.
Decorator that traces Python functions into optimised TF graphs. Speeds up training steps significantly — especially on loops.
SavedModel format works with TF Serving, TFLite (mobile/edge), and TF.js (browser). Package preprocessing inside the model for consistent inference.
Simple, linear stacks
Layers in order, one input → one output. Quickest to write; not suitable for skip connections or multiple inputs.
keras.Sequential([
Dense(64, activation='relu'),
Dense(1)
])Multi-input/output, skip connections
Define a directed acyclic graph of layers explicitly. The standard choice for most non-trivial architectures.
x = Dense(64)(inputs)
out = Dense(1)(x)
keras.Model(inputs, out)Custom research loops
Full Python flexibility — define __init__ and call(). Use when the Functional API is too constrained.
class MyModel(keras.Model):
def call(self, x):
return self.dense(x)The Functional API is the recommended default for production code — its explicit, visualisable with keras.utils.plot_model, and serialises cleanly to SavedModel.
import tensorflow as tf
from tensorflow import keras
# Functional API — for multi-input, multi-output, or skip connections
inputs = keras.Input(shape=(128,), name="features")
x = keras.layers.Dense(64, activation="relu")(inputs)
x = keras.layers.BatchNormalization()(x)
x = keras.layers.Dropout(0.3)(x)
x = keras.layers.Dense(32, activation="relu")(x)
outputs = keras.layers.Dense(10, activation="softmax", name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(
optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)model.fit() handles the training loop, metrics, callbacks, and validation split. Use it unless you need behaviour that Keras cant express. For custom loops — non-standard gradient updates, meta-learning, RL — drop to GradientTape directly.
model.compile(
optimizer="adam",
loss="mse",
metrics=["mae"],
)
model.fit(
X_train, y_train,
epochs=20,
batch_size=64,
validation_split=0.1,
)Handles progress bars, history logging, callbacks, and distributed training. The right default for most projects.
with tf.GradientTape() as tape:
preds = model(X, training=True)
loss = loss_fn(y, preds)
grads = tape.gradient(
loss, model.trainable_variables
)
optimizer.apply_gradients(
zip(grads, model.trainable_variables)
)Full control over every gradient. Decorate the step function with @tf.function for graph-mode speed.
import tensorflow as tf
optimizer = tf.keras.optimizers.Adam(learning_rate=3e-4)
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy()
train_acc = tf.keras.metrics.SparseCategoricalAccuracy()
@tf.function # compile to a graph for speed
def train_step(X_batch, y_batch):
with tf.GradientTape() as tape:
logits = model(X_batch, training=True)
loss = loss_fn(y_batch, logits)
grads = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
train_acc.update_state(y_batch, logits)
return loss
for epoch in range(num_epochs):
train_acc.reset_state()
for X_batch, y_batch in train_dataset:
loss = train_step(X_batch, y_batch)
print(f"Epoch {epoch}: acc={train_acc.result():.4f}")Subclass keras.layers.Layer. Define sub-layers in __init__ and forward logic in call. Pass training=False through to sub-layers that behave differently at inference (BatchNorm, Dropout). Custom layers are fully composable with the Functional API and serialise correctly.
import tensorflow as tf
from tensorflow import keras
class ResidualBlock(keras.layers.Layer):
def __init__(self, units: int, **kwargs):
super().__init__(**kwargs)
self.dense1 = keras.layers.Dense(units, activation="relu")
self.dense2 = keras.layers.Dense(units)
self.norm = keras.layers.LayerNormalization()
self.add = keras.layers.Add()
def call(self, x, training=False):
residual = x
x = self.dense1(x)
x = self.dense2(x)
x = self.add([x, residual])
return self.norm(x, training=training)
# Use inside any model
inputs = keras.Input(shape=(64,))
x = ResidualBlock(64)(inputs)
x = ResidualBlock(64)(x)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)tf.data is TensorFlow s declarative data pipeline API. It chains transformations lazily and can overlap CPU preprocessing with GPU compute via prefetch(AUTOTUNE). Always prefer it over feeding NumPy arrays for anything beyond small toy datasets.
import tensorflow as tf
AUTOTUNE = tf.data.AUTOTUNE
def parse_example(path, label):
img = tf.io.read_file(path)
img = tf.image.decode_jpeg(img, channels=3)
img = tf.image.resize(img, [224, 224])
img = tf.cast(img, tf.float32) / 255.0
return img, label
train_ds = (
tf.data.Dataset.from_tensor_slices((image_paths, labels))
.shuffle(buffer_size=1000)
.map(parse_example, num_parallel_calls=AUTOTUNE)
.batch(32)
.prefetch(AUTOTUNE) # overlap preprocessing + GPU compute
).shuffle(buffer)Randomly samples from a buffer — set buffer_size ≥ dataset size for true shuffling.
.map(fn, AUTOTUNE)Apply a preprocessing function. AUTOTUNE parallelises across CPU cores automatically.
.prefetch(AUTOTUNE)Prepares the next batch while the GPU processes the current one — eliminates idle GPU time.
.cache()Cache the dataset in memory (or on disk) after the first epoch — great for small datasets with expensive parsing.
.repeat()Repeat the dataset indefinitely — useful when specifying steps_per_epoch instead of epochs.
.batch(n, drop_remainder)Set drop_remainder=True for static shapes — required when exporting to TFLite or TF.js.
Callbacks hook into model.fit() at epoch start/end, batch start/end, and on training end. Always use at least EarlyStopping + ModelCheckpoint in production training runs.
import tensorflow as tf
from tensorflow import keras
callbacks = [
# Stop early and restore best weights
keras.callbacks.EarlyStopping(
monitor="val_loss", patience=5, restore_best_weights=True
),
# Save best checkpoint to disk
keras.callbacks.ModelCheckpoint(
"best_model.keras", monitor="val_loss", save_best_only=True
),
# Reduce LR when plateau
keras.callbacks.ReduceLROnPlateau(
monitor="val_loss", factor=0.5, patience=3, min_lr=1e-6
),
# TensorBoard logging
keras.callbacks.TensorBoard(log_dir="./logs", histogram_freq=1),
]
model.fit(X_train, y_train, epochs=100, validation_split=0.1, callbacks=callbacks)For custom behaviour, subclass keras.callbacks.Callback and override on_epoch_end, on_batch_end, etc. Useful for logging to experiment trackers (W&B, MLflow) or early stopping on custom metrics.
keras.applications ships pretrained image models (MobileNet, EfficientNet, ResNet, etc.) with ImageNet weights. Freeze the base, train a new head, then unfreeze the top layers for fine-tuning at a very low learning rate.
import tensorflow as tf
from tensorflow import keras
# Load pretrained base (ImageNet weights, no top classifier)
base = keras.applications.MobileNetV3Small(
input_shape=(224, 224, 3),
include_top=False,
weights="imagenet",
)
base.trainable = False # freeze base during initial training
# Add custom head
inputs = keras.Input(shape=(224, 224, 3))
x = keras.applications.mobilenet_v3.preprocess_input(inputs)
x = base(x, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dropout(0.2)(x)
outputs = keras.layers.Dense(num_classes, activation="softmax")(x)
model = keras.Model(inputs, outputs)
# Phase 1: train head only
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(train_ds, epochs=10, validation_data=val_ds)
# Phase 2: unfreeze top layers and fine-tune
base.trainable = True
for layer in base.layers[:-20]: # keep early layers frozen
layer.trainable = False
model.compile(
optimizer=keras.optimizers.Adam(1e-5), # very low LR for fine-tuning
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
model.fit(train_ds, epochs=10, validation_data=val_ds)Phase 1 — train head onlyKeep base.trainable=False. Use a normal LR (1e-3 to 3e-4). Converges quickly — the base features are already good.
Phase 2 — fine-tune top layersUnfreeze the last N layers of the base. Use a very low LR (1e-5) to avoid destroying pretrained weights.
base(x, training=False)Pass training=False when the base is frozen — keeps BatchNorm in inference mode even during model.fit().
Preprocessing layersUse keras.applications.mobilenet_v3.preprocess_input inside the model so inference doesn't require separate normalisation.
The .keras format (Keras native) is the recommended save format — it stores architecture, weights, and optimizer state. Use SavedModel for TF Serving. Use TFLite for mobile and edge. Always include preprocessing layers inside the model before exporting so the serving artifact is self-contained.
# Save the full model (architecture + weights + optimizer state)
model.save("my_model.keras") # Keras native format (recommended)
model.save("my_model_savedmodel") # TF SavedModel format (for TF Serving)
# Load
loaded = tf.keras.models.load_model("my_model.keras")
# Export for mobile / edge — TensorFlow Lite
converter = tf.lite.TFLiteConverter.from_saved_model("my_model_savedmodel")
converter.optimizations = [tf.lite.Optimize.DEFAULT] # quantization
tflite_model = converter.convert()
with open("model.tflite", "wb") as f:
f.write(tflite_model)
# Weights only (transfer learning)
model.save_weights("weights.h5")
model.load_weights("weights.h5").keras formatKeras native — stores everything. Best for resuming training or sharing with other Keras users.
SavedModelTF's portable format — works with TF Serving, TF.js converter, and TFLite converter. Language-agnostic.
TFLiteOptimised for mobile and microcontrollers. Supports quantisation (INT8/FP16) to shrink model size by 4×.
Dense(units, activation)Fully connected layer. The building block of MLPs.
Conv2D(filters, kernel_size)2D convolution for image feature extraction.
LSTM(units) / GRU(units)Recurrent layers for sequences. Set return_sequences=True to stack.
MultiHeadAttention(heads, key_dim)Transformer-style attention. Use with positional encoding for sequences.
Embedding(vocab, dim)Learnable lookup table — converts token IDs to dense vectors.
BatchNormalization()Normalises activations per batch. Pass training=True/False correctly — critical for frozen layers.
LayerNormalization()Normalises across features (not batch). Preferred in Transformers and RNNs.
Dropout(rate)Zeros random activations during training. Use training=True/False — it's a no-op at inference.
GlobalAveragePooling2D()Collapses spatial dims to a vector — standard pooling head for CNNs.
Reshape / Flatten / PermuteShape manipulation layers — keep transformations inside the model for correct export.
Define, compile, fit, and predict with Keras.
| Framework | Best at | Typical tasks | Output |
|---|---|---|---|
| NumPy | Fast array math + linear algebra | Numerics, prototyping, preprocessing | ndarray (CPU) |
| Pandas | Tabular ETL + joins + aggregation | Cleaning, feature engineering, analysis | DataFrame / Series |
| scikit-learn | Classical ML + evaluation + pipelines | Baselines, CV, model selection | Estimator / Pipeline |
| PyTorch | Neural nets + custom training loops | Deep learning, research, fine-tuning | Tensors + nn.Module |
| TensorFlow | Keras training + production deployment | Deep learning, serving, mobile/edge | Tensors + Keras Model |
| Hugging Face | Model hub + transformer tooling | Inference, fine-tuning, sharing | Checkpoints + pipelines |