AI & Machine Learning Cheatsheet
Neural Networks
Use this AI & Machine Learning reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
What is a Neural Network?
A neural network is a parameterized function composed of many simple transformations, organized in layers. It can approximate any continuous function given sufficient width or depth (Universal Approximation Theorem).
The key insight: stack many linear transformations (matrix multiplications) interspersed with nonlinear activations. The nonlinearities allow the network to learn arbitrarily complex mappings.
The Neuron (Perceptron)
A single neuron computes:
z = Σⱼ wⱼxⱼ + b = wᵀx + b
a = σ(z)
where σ is a nonlinear activation function. Without σ, a stack of linear layers collapses to a single linear layer — the whole network is as expressive as one matrix multiplication.
Activation Functions
| Activation | Formula | Range | Vanishing Grad | Common Use |
|---|---|---|---|---|
| Sigmoid | 1/(1+e⁻ˣ) | (0,1) | Yes, saturates | Binary output |
| Tanh | (eˣ−e⁻ˣ)/(eˣ+e⁻ˣ) | (−1,1) | Yes, saturates | RNN hidden states |
| ReLU | max(0, x) | [0,∞) | Partial (dead neurons) | Deep nets (default) |
| Leaky ReLU | max(αx, x), α small | (−∞,∞) | No | Avoid dead neurons |
| ELU | x if x>0 else α(eˣ−1) | (−α,∞) | No | Smooth alternative |
| GELU | x·Φ(x) | (−0.17,∞) | No | Transformers, BERT |
| Swish | x·σ(x) | (−0.28,∞) | No | EfficientNet |
| Softmax | exp(xₖ)/Σ exp(xⱼ) | (0,1), sums to 1 | — | Multiclass output |
ReLU is the most common hidden-layer activation. Its gradient is 1 when x > 0 and 0 otherwise — avoids vanishing gradient, but "dead neurons" (never activated) can occur with bad initialization or high learning rate.
import torch import torch.nn as nn import torch.nn.functional as F # As modules relu = nn.ReLU() gelu = nn.GELU() # As functions x_relu = F.relu(x) x_gelu = F.gelu(x)
Fully Connected (Dense) Layer
The most fundamental building block:
y = σ(Wx + b)
- W ∈ ℝ^(out×in): weight matrix
- b ∈ ℝ^out: bias vector
- Parameters = out × in + out
linear = nn.Linear(in_features=784, out_features=256, bias=True) # Parameter count: 784*256 + 256 = 200,960
Multilayer Perceptron (MLP)
Stack of fully connected layers with activations:
x → [Linear → BatchNorm → Activation → Dropout] × L → Linear → output
class MLP(nn.Module): def __init__(self, input_dim, hidden_dims, output_dim, dropout=0.3): super().__init__() dims = [input_dim] + hidden_dims + [output_dim] layers = [] for i in range(len(dims) - 1): layers.append(nn.Linear(dims[i], dims[i+1])) if i < len(dims) - 2: # no BN/activation on last layer layers.append(nn.BatchNorm1d(dims[i+1])) layers.append(nn.GELU()) layers.append(nn.Dropout(dropout)) self.net = nn.Sequential(*layers) def forward(self, x): return self.net(x) model = MLP(input_dim=128, hidden_dims=[256, 256], output_dim=10)
Backpropagation in Detail
Given a loss L, backprop computes gradients using the chain rule:
∂L/∂wᵢⱼ = ∂L/∂aₗ · ∂aₗ/∂zₗ · ∂zₗ/∂wᵢⱼ
For each layer l (going backward): 1. Receive ∂L/∂aₗ from the layer above 2. Compute ∂L/∂zₗ = ∂L/∂aₗ ⊙ σ'(zₗ) 3. Compute ∂L/∂Wₗ = ∂L/∂zₗ · aₗ₋₁ᵀ and ∂L/∂bₗ = ∂L/∂zₗ 4. Pass ∂L/∂aₗ₋₁ = Wₗᵀ · ∂L/∂zₗ to the previous layer
Modern frameworks (PyTorch, JAX, TensorFlow) do this automatically via automatic differentiation.
Loss Functions
| Problem | Loss | Formula |
|---|---|---|
| Binary classification | Binary cross-entropy | −[y log p + (1−y) log(1−p)] |
| Multiclass | Categorical cross-entropy | −Σₖ yₖ log pₖ |
| Regression | MSE | (y − ŷ)² |
| Regression (robust) | MAE | |y − ŷ| |
| Regression (robust+smooth) | Huber | MSE if |r|<δ, else MAE |
| Object detection | Focal loss | −(1−pₜ)^γ log pₜ — down-weights easy negatives |
| Embeddings | Contrastive / Triplet | Pulls similar pairs together |
# Classification loss_fn = nn.CrossEntropyLoss() # expects logits (before softmax) loss = loss_fn(logits, y_true) # y_true: class indices # Regression loss_fn = nn.MSELoss() loss_fn = nn.SmoothL1Loss(beta=1.0) # Huber loss
Network Depth vs. Width
| Dimension | Effect |
|---|---|
| More width (hidden units) | Smoother decision boundaries; diminishing returns |
| More depth (layers) | Hierarchical feature composition; more expressive; harder to train |
Deep networks can represent certain functions exponentially more efficiently than shallow ones. But depth introduces training challenges (vanishing gradients, optimization difficulty).
Residual Connections (Skip Connections)
The key innovation that enabled training very deep networks (ResNet, 2015):
y = F(x, {Wᵢ}) + x
The skip connection adds the input directly to the block's output. Gradients flow through the shortcut path — this virtually eliminates vanishing gradients and allows training of 100+ layer networks.
class ResidualBlock(nn.Module): def __init__(self, dim): super().__init__() self.block = nn.Sequential( nn.Linear(dim, dim), nn.GELU(), nn.Linear(dim, dim) ) self.norm = nn.LayerNorm(dim) def forward(self, x): return self.norm(x + self.block(x)) # pre-norm or post-norm variant
Normalization Layers
| Method | Normalizes over | When to Use |
|---|---|---|
| Batch Norm (BN) | Batch dimension | CNNs, MLP (batch_size ≥ 32) |
| Layer Norm (LN) | Feature dimension | Transformers, RNNs (any batch size) |
| Instance Norm | Spatial dimensions per sample | Style transfer |
| Group Norm | Groups of channels | Small batches, object detection |
| RMS Norm | Feature dim, no mean subtraction | Modern LLMs (Llama, etc.) |
Regularization in Neural Networks
| Technique | How | When |
|---|---|---|
| L2 weight decay | λ Σθ² penalty | Always; use AdamW |
| Dropout | Zero neurons with prob p | FC layers, Transformer |
| Dropblock | Zero contiguous blocks | CNNs |
| Label smoothing | Replace hard targets with soft: 0 → ε/K, 1 → 1−ε | Classification |
| Mixup | Train on convex combinations of (x₁,y₁) and (x₂,y₂) | CV, tabular |
| Stochastic depth | Randomly drop entire layers | Very deep nets (ViT) |
# Label smoothing loss_fn = nn.CrossEntropyLoss(label_smoothing=0.1) # Mixup (simplified) lam = np.random.beta(0.4, 0.4) mixed_x = lam * x1 + (1 - lam) * x2 mixed_y = lam * y1 + (1 - lam) * y2
Building and Training a Network in PyTorch
import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset # Data X, y = torch.randn(1000, 64), torch.randint(0, 10, (1000,)) dataset = TensorDataset(X, y) loader = DataLoader(dataset, batch_size=64, shuffle=True) # Model model = MLP(64, [128, 128], 10) device = "cuda" if torch.cuda.is_available() else "cpu" model = model.to(device) # Loss and optimizer criterion = nn.CrossEntropyLoss() optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20) # Training loop for epoch in range(20): model.train() total_loss = 0 for xb, yb in loader: xb, yb = xb.to(device), yb.to(device) optimizer.zero_grad() logits = model(xb) loss = criterion(logits, yb) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() total_loss += loss.item() scheduler.step() print(f"Epoch {epoch+1}: loss={total_loss/len(loader):.4f}") # Inference model.eval() with torch.no_grad(): preds = model(X_test.to(device)).argmax(dim=1)
Parameter Count and Memory
For a model with parameters θ: - Storage (FP32): 4 bytes per parameter → 1B parameters = 4 GB - Training memory ≈ 4× weights (weights + gradients + Adam states × 2) - Inference memory ≈ 1–2× weights
def count_params(model): total = sum(p.numel() for p in model.parameters()) trainable = sum(p.numel() for p in model.parameters() if p.requires_grad) print(f"Total: {total:,} | Trainable: {trainable:,}") count_params(model)
Universal Approximation Theorem
A neural network with at least one hidden layer with a non-polynomial activation function and sufficient width can approximate any continuous function on a compact subset of ℝⁿ to arbitrary precision.
Key caveats: - Says nothing about how many neurons are needed (could be exponential) - Says nothing about whether gradient descent finds the approximation - Expressiveness ≠ learnability
Depth helps: deep networks can represent certain functions with exponentially fewer neurons than shallow networks.
Common Neural Network Architectures
| Architecture | Input Type | Key Mechanism | Use Case |
|---|---|---|---|
| MLP | Tabular vectors | Fully connected layers | General tabular data |
| CNN | Grid (images) | Shared filters + pooling | Computer vision |
| RNN / LSTM | Sequences | Recurrent hidden state | Time series (classical) |
| Transformer | Sequences | Self-attention | NLP, vision, multimodal |
| GNN | Graphs | Message passing | Social networks, molecules |
| Autoencoder | Any | Bottleneck compression | Embeddings, anomaly detection |
| GAN | Noise → image | Generator vs. discriminator | Image synthesis |
| Diffusion | Noise → image | Denoising score matching | SOTA image/video generation |