TensorFlow - Keras basics
Overview
TensorFlow is Google's open-source numerical computation library optimized for machine learning, while Keras is a high-level neural network API that runs on top of TensorFlow (and other backends). As of TensorFlow 2.x, tf.keras is the official high-level API integrated directly into TensorFlow.
WHY this matters: ~95% of production deep learning uses frameworks. Writing backprop by hand is educational but impractical. TensorFlow handles the computational graph, device placement, and optimization so you focus on architecture.

Core Concepts
1. Tensors: The Fundamental Data Structure
Shape hierarchy:
- Rank 0: scalar (single number)
- Rank 1: vector (1D array)
- Rank 2: matrix (2D array)
- Rank 3+: higher-order tensors (images, videos, batches)
WHY immutable? Allows TensorFlow to optimize the computation graph—once defined, operations can be compiled and paralelized without worying about side effects.
import tensorflow as tf
# Creating tensors
scalar = tf.constant(42) # Shape: ()
vector = tf.constant([1, 2, 3]) # Shape: (3,)
matrix = tf.constant([[1, 2], [3, 4]]) # Shape: (2, 2)
batch_images = tf.zeros((32, 28, 28, 1)) # Shape: (batch, height, width, channels)Element-wise operations
c = a + b # 6.0, 8.0, [10.0, 12.0]] d = a * b # Element-wise multiplication
Matrix operations
e = tf.matmul(a, b) # Matrix multiplication f = tf.transpose(a) # Transpose
**Why this step?** Each operation creates a new tensor node in the computation graph. TensorFlow tracks dependencies for automatic differentiation.
### 2. Building Models: Three Approaches
#### Sequential API (Simplest)
> [!definition] Sequential Model
> A ==Sequential model== is a linear stack of layers where each layer has exactly one input tensor and one output tensor.
**DERIVATION from first principles:**
A neural network is a composition of functions: $y = f_n(f_{n-1}(...f_2(f_1(x))))$
In code, this becomes:
```python
from tensorflow import keras
model = keras.Sequential([
keras.layers.Dense(128, activation='relu', input_shape=(784,)), # f₁
keras.layers.Dropout(0.2), # regularization
keras.layers.Dense(64, activation='relu'), # f₂
keras.layers.Dense(10, activation='softmax') # f₃ (output)
])
WHY this structure?
input_shape=(784,): First layer needs to know input dimensions (flattened 28×28 image)Dense(128): Fully connected layer with 128 neurons → learns784×128 + 128 = 100,480 parametersactivation='relu': Non-linearity (without it, stacking layers = single linear transformation)Dropout(0.2): Randomly zeros20% of activations during training → prevents overfittingsoftmax: Converts logits to probability distribution over 10 classes
where is the input (batch_size × n), is the activation function.
Derivation: Each neuron computes , vectorized as matrix multiplication for efficiency.
Functional API (Flexible)
inputs = keras.Input(shape=(784,))
x = keras.layers.Dense(128, activation='relu')(inputs)
x = keras.layers.Dropout(0.2)(x)
x = keras.layers.Dense(64, activation='relu')(x)
outputs = keras.layers.Dense(10, activation='softmax')(x)
model = keras.Model(inputs=inputs, outputs=outputs)WHY use this? Enables complex architectures:
# Multi-input example
input1 = keras.Input(shape=(128,))
input2 = keras.Input(shape=(64,))
x1 = keras.layers.Dense(64)(input1)
x2 = keras.layers.Dense(64)(input2)
merged = keras.layers.concatenate([x1, x2])
output = keras.layers.Dense(1)(merged)
model = keras.Model(inputs=[input1, input2], outputs=output)Model Subclassing (Most Control)
class CustomModel(keras.Model):
def __init__(self):
super().__init__()
self.dense1 = keras.layers.Dense(128, activation='relu')
self.dropout = keras.layers.Dropout(0.2)
self.dense2 = keras.layers.Dense(10, activation='softmax')
def call(self, inputs, training=False):
x = self.dense1(inputs)
if training: # Only apply dropout during training
x = self.dropout(x, training=training)
return self.dense2(x)WHY subclass? Dynamic computation graphs, custom training loops, research experimentation.
3. Training Pipeline: Compile → Fit → Evaluate
- Loss function : Measures prediction error
- Optimizer: Updates parameters
- Metrics: Monitors performance (accuracy, F1, etc.)
DERIVATION of gradient descent: Starting from Taylor expansion:
For small , moving opposite to gradient decreases loss.
# Compile: Define learning algorithm
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss=keras.losses.SparseCategoricalCrossentropy(),
metrics=['accuracy']
)WHY these choices?
- Adam optimizer: Adaptive learning rates per parameter (combines momentum + RMSprop)
- SparseCategoricalCrossentropy: For integer labels (0-9) instead of one-hot vectors
- Accuracy metric: % of correct predictions (more interpretable than loss)
Load data
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
Preprocessing
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0 # Flatten & normalize x_test = x_test.reshape(-1, 784).astype('float32') / 255.0
Build model
model = keras.Sequential([ keras.layers.Dense(128, activation='relu', input_shape=(784,)), keras.layers.Dropout(0.2), keras.layers.Dense(10, activation='softmax') ])
Compile
model.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'] )
Train
history = model.fit( x_train, y_train, batch_size=32, epochs=5, validation_split=0.2, # Use 20% of training data for validation verbose=1 )
Evaluate
test_loss, test_acc = model.evaluate(x_test, y_test) print(f"Test accuracy: {test_acc:.4f}")
**Step-by-step WHY:**
1. **Reshape to (6000, 784):** Dense layers expect flat vectors, not 2D images
2. **Divide by 255:** Normalizes pixels to [0,1] range → faster convergence (gradients better scaled)
3. **batch_size=32:** Process 32 examples at a time → balance between memory and gradient noise
4. **epochs=5:** One epoch = one pass through entire training set
5. **validation_split=0.2:** Monitor overfitting by tracking loss on unseen data
### 4. Key Operations
> [!formula] Common Layer Types
**Dense (Fully Connected):**
$$\mathbf{y} = \sigma(\mathbf{W}\mathbf{x} + \mathbf{b})$$
Parameters: $n_{in} \times n_{out} + n_{out}$
**Dropout:**
During training: $\mathbf{y}_i = \begin{cases} 0 & \text{with probability } p \\ \frac{\mathbf{x}_i}{1-p} & \text{otherwise} \end{cases}$
**WHY divide by $(1-p)$?** Maintains expected value: $\mathbb{E}[\mathbf{y}_i] = \mathbb{E}[\mathbf{x}_i]$ (inverted dropout)
**Softmax (output layer for classification):**
$$\text{softmax}(\mathbf{z})_i = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}}$$
**Derivation:** We want outputs that:
1. Sum to 1 (probability distribution)
2. Are positive
3. Preserve ordering (larger logit → larger probability)
Exponential satisfies (2) and (3), normalization satisfies (1).
## Common Patterns
### Data Pipeline
```python
# Create tf.data.Dataset for efficient loading
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(1000).batch(32).prefetch(tf.data.AUTOTUNE)
# Use in training
model.fit(train_dataset, epochs=5)
WHY this approach?
shuffle: Randomizes order → prevents model from learning sequence patternsbatch: Groups examples → efficient GPU computationprefetch: Loads next batch while training current → hides I/O latency
Callbacks
callbacks = [
keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),
keras.callbacks.ModelCheckpoint('best_model.h5', save_best_only=True),
keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=2)
]
model.fit(x_train, y_train, epochs=100, callbacks=callbacks)WHY use callbacks?
- EarlyStopping: Stops training when validation loss stops improving (prevents overfitting)
- ModelCheckpoint: Saves best model during training (not just the last epoch)
- ReduceLROnPlateau: Decreases learning rate when stuck → helps escape plateaus
WHY it feels right: The network should learn the appropriate scale through training.
The problem: Large input values → large gradients → unstable training or exploding gradients. Optimizer learning rates are tuned for normalized inputs.
The fix:
x = x / 255.0 # Scale to [0, 1]
# OR
x = (x - mean) / std # Standardize to zero mean, unit varianceSteel-man: If you carefully tune the learning rate and weight initialization for the specific input scale, unnormalized data can work—but it's fragile and slows convergence.
But labels are integers: [3, 7, 2 ...]
**WHY it feels right:** "Categorical" sounds right for categories.
**The problem:** `categorical_crossentropy` expects shape (batch_size, num_classes) with one-hot encoding. Integer labels have shape (batch_size,).
**The fix:**
```python
# For integer labels: use sparse version
model.compile(loss='sparse_categorical_crossentropy', ...)
# OR one-hot encode labels first
y_train_onehot = keras.utils.to_categorical(y_train, num_classes=10)
model.compile(loss='categorical_crossentropy', ...)
The problem: Dropout randomly zeros neurons → stochastic predictions. At test time, we want deterministic, averaged predictions.
What Keras does automatically:
- Training: Dropout active (with scaling)
- Inference (
model.predict(),model.evaluate()): Dropout disabled
Manual control:
# Force training mode
predictions = model(x, training=True) # Dropout active
# Force inference mode
predictions = model(x, training=False) # Dropout disabledRecall Explain to a 12-year-old
Imagine you're building a robot brain to recognize handwritten numbers. TensorFlow is like a special workshop with really fast tools (GPUs) that can do millions of calculations per second. But the tools are complicated—you'd need to write hundreds of lines of code just to add two matrices together and remember how to update them.
That's where Keras comes in—it's like a simple instruction manual. Instead of saying "take this28×28 grid of pixels, flatten it into784 numbers, multiply by this matrix, add these numbers, apply this squishing function..." you just say Dense(128, activation='relu') and Keras handles all the details.
Training a model is like teaching:
- Show the robot lots of examples ("this is a 3", "this is a 7")
- It makes guesses and you tell it when it's wrong
- It adjusts its "brain connections" (weights) to do better next time
- After seeing thousands of examples, it gets really good at recognizing new numbers it's never seen
The batch_size is like studying in groups—instead of learning from one example at a time (slow!), you show it32 examples together and update based on the average mistake. Way faster!
"A baby CALF learns to walk" → A model learns to predict
Connections
- 3.1.01-Gradient-Descent - TensorFlow computes gradients automatically
- 3.2.03-Backpropagation - What happens inside
model.fit() - 3.3.01-Neural-Network-Basics - The math behind
Denselayers - 3.07-Activation-Functions - Why we use ReLU, softmax
- 3.3.09-PyTorch-basics - Alternative framework (more Pythonic)
- 3.4.05-Overfitting-Regularization - Why we use Dropout, validation split
- 4.2.01-CNN-Architecture - Keras for image models (Conv2D layers)
#flashcards/ai-ml
What is a tensor in TensorFlow? :: A multi-dimensional array with uniform data type, immutable, that flows through computational graphs. Generalizes scalars (rank 0), vectors (rank 1), matrices (rank 2) to higher dimensions.
What are the three model-building APIs in Keras?
What does model.compile() do?
Why normalize input data (x/255)?
What's the difference between categorical_crossentropy and sparse_categorical_crossentropy?
Why use validation_split in model.fit()?
What does batch_size control?
Why is Dropout disabled during inference?
What does the softmax activation do?
What's the purpose of EarlyStopping callback?
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
TensorFlow aur Keras ko samajhne ke liye ek simple analogy use karte hain: TensorFlow ek powerful engine hai (jaise car ka engine) jo sari heavy mathematical computations handle karta hai—automatic differentiation, GPU acceleration, distributed computing. Lekin directly TensorFlow use karna thoda complex ho sakta hai, isliye Keras aa gayi as a user-friendly interface (jaise car ka steering wheel aur pedals). TensorFlow 2.x mein, Keras ab directly integrated hai as tf.keras, toh apko best of both worlds milta hai—power + simplicity.
Core concept hai tensor, jo basically multi-dimensional array hai. Ek image ko 3D tensor ke roop mein represent karte hain (height × width × channels). Model bane ke liye Keras mein teen tarike hain: Sequential (sabse simple, layers ka seedha stack), Functional (complex architectures ke liye jaise multi-input models), aur Subclassing (full control chahiye toh). Jab model ban gaya, toh compile karte hain—matlab optimizer choose karo (Adam best hai usually), loss function (SparseCategoricalCrossentropy integers labels ke liye), aur metrics (accuracy). Phir fit() call karte hain jo training loop chalata hai: batch-by-batch data process karo, gradients compute karo, weights update karo. Validation split use karke overfitting detect kar sakte ho.
Sabse common mistakes: data normalize karna bhoolna (pixels ko 255 se divide karo), galat loss function use karna (categorical vs sparse), aur dropout ko test time pe active rakhna (automatic disable hota hai Keras mein). Practice mein MNIST jaise simple dataset se shuru karo—28×28 grayscale digits ko classify karna. Dense layers stack karo, dropout dalo overfitting rokne ke liye, aur softmax output pe for probability distribution. Callbacks jaise EarlyStopping bahut useful hain—automatically training rok deta hai jab validation loss improve nahi ho raha.
Real power tab ati hai jab aap large-scale projects pe kaam karte ho: tf.data pipelines for efficient data loading, custom training loops for research, distributed training across multiple GPUs. Lekin beginning mein basics master karo—tensor operations, layer types, training workflow. Ek baar ye clear ho gaye, toh complex architectures (CNs, RNNs, Transformers) implement karna easy ho jayega kyunki underlying process same rehti hai: build model, compile with optimizer/loss, fit on data, evaluate performance.