Deep Learning Optimizer Implementations: SGD, Adam, and AdamW Explained
The harvard-edge/cs249r_book repository provides lightweight, pedagogical implementations of SGD, Adam, and AdamW optimizers that demonstrate the core algorithms driving modern neural network training.
Deep learning optimizer implementations form the backbone of neural network training, determining how model parameters update during backpropagation. The cs249r_book repository from Harvard Edge contains a minimal framework called tinytorch that implements three fundamental optimizers in pure Python. These implementations prioritize clarity over performance, making them ideal for understanding the mathematical mechanics behind stochastic gradient descent and adaptive moment estimation.
The Optimizer Base Class
All deep learning optimizer implementations in the repository inherit from a common abstract base class that standardizes the training interface.
Located in tinytorch/src/07_optimizers/07_optimizers.py (lines 238-300), the Optimizer class defines the shared API used by every concrete implementation:
zero_grad(): Clears accumulated gradients from all parameters before the next backward passstep(): Abstract method that each subclass implements to perform the parameter update_extract_gradient(): Helper that normalizes gradient extraction from eitherTensorobjects or raw NumPy arrays
This abstraction allows the training loop to remain agnostic to the specific optimization algorithm while ensuring consistent gradient handling across different parameter types.
Stochastic Gradient Descent (SGD) Implementation
SGD represents the foundational deep learning optimizer implementation, extending basic gradient descent with optional momentum and weight decay regularization.
Core Algorithm
The implementation resides at lines 537-603 in tinytorch/src/07_optimizers/07_optimizers.py. The SGD class maintains three key hyperparameters:
lr: The learning rate controlling step sizemomentum: Coefficient for exponential moving average of gradients (typically 0.9)weight_decay: L2 regularization coefficient applied to parameters
Velocity Buffers and Update Logic
SGD stores per-parameter velocity buffers (self.momentum_buffers) that accumulate past gradient directions. The update procedure follows this exact sequence:
- Extract the current gradient using
_extract_gradient - Apply weight decay:
grad = grad + weight_decay * param - Update velocity:
velocity = momentum * velocity + grad - Update parameters:
param = param - learning_rate * velocity
This momentum term smooths optimization trajectories, helping escape shallow local minima and accelerating convergence in consistent gradient directions.
Adam Optimizer Implementation
Adam (Adaptive Moment Estimation) represents a more sophisticated deep learning optimizer implementation that maintains per-parameter adaptive learning rates based on gradient history.
Adaptive Moment Estimation Mechanics
Located at lines 834-904 in tinytorch/src/07_optimizers/07_optimizers.py, the Adam class implements adaptive optimization through:
beta1: Exponential decay rate for first moment estimates (mean of gradients, default 0.9)beta2: Exponential decay rate for second moment estimates (uncentered variance, default 0.999)eps: Small constant for numerical stability (typically 1e-8)weight_decay: Optional L2 regularization
Bias Correction and Update Steps
Adam maintains two buffer dictionaries:
m_buffers: First moment estimates (mean gradients)v_buffers: Second moment estimates (mean squared gradients)
The step() method implements the full Adam algorithm:
- Increment global
step_countfor bias correction - For each parameter:
- Extract gradient
- Apply weight decay to gradient (if enabled)
- Update first moment:
m = beta1 * m + (1 - beta1) * grad - Update second moment:
v = beta2 * v + (1 - beta2) * grad * grad - Compute bias-corrected estimates:
m_hat = m / (1 - beta1^t),v_hat = v / (1 - beta2^t) - Update parameter:
param -= lr * m_hat / (sqrt(v_hat) + eps)
This adaptive approach automatically adjusts learning rates per-parameter, making Adam particularly effective for sparse gradients and non-stationary objectives.
AdamW Optimizer Implementation
AdamW modifies the standard Adam deep learning optimizer implementation by decoupling weight decay from the gradient update, fixing a subtle but important regularization bug.
Decoupled Weight Decay
The implementation at lines 1222-1295 in tinytorch/src/07_optimizers/07_optimizers.py introduces the AdamW class. Unlike standard Adam, AdamW applies L2 regularization after the parameter update rather than adding it to the gradient.
This distinction matters because standard Adam's adaptive learning rates interact poorly with weight decay applied to gradients. When weight decay is added to gradients in Adam, the effective regularization strength varies with the historical gradient magnitude, weakening regularization for parameters with large gradients.
Update Procedure
AdamW follows the same moment estimation as Adam but modifies the final parameter update:
- Perform standard Adam moment calculations (without weight decay in the gradient)
- Compute the Adam update direction:
update = lr * m_hat / (sqrt(v_hat) + eps) - Apply the update:
param = param - update - Apply decoupled weight decay:
param = param * (1 - lr * weight_decay)
This ensures that weight decay regularizes all parameters uniformly regardless of their gradient history, typically improving generalization in deep neural networks.
Practical Usage Examples
The following examples demonstrate how to instantiate and use these deep learning optimizer implementations with the tinytorch framework.
SGD with Momentum
from tinytorch.core.tensor import Tensor
from tinytorch.core.optimizers import SGD
# Initialize parameters
weights = Tensor([[0.5, -0.3], [0.8, 0.2]], requires_grad=True)
bias = Tensor([0.0, 0.0], requires_grad=True)
# Create optimizer with momentum and weight decay
optimizer = SGD([weights, bias], lr=0.01, momentum=0.9, weight_decay=0.001)
# Training loop
for epoch in range(100):
# Forward pass (example: linear layer)
output = weights @ input_data + bias
loss = compute_loss(output, targets)
# Backward pass
loss.backward()
# Parameter update
optimizer.step()
optimizer.zero_grad()
Adam Adaptive Optimization
from tinytorch.core.optimizers import Adam
# Adam works well with sparse gradients and varying scales
optimizer = Adam(
model_parameters,
lr=0.001,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0.0
)
# Usage identical to SGD
loss.backward()
optimizer.step()
optimizer.zero_grad()
AdamW for Improved Regularization
from tinytorch.core.optimizers import AdamW
# AdamW applies decoupled weight decay
optimizer = AdamW(
model_parameters,
lr=0.001,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0.01 # Typically higher than Adam's weight_decay
)
# Training loop remains the same
Key Files in the Repository
Understanding the architecture of these deep learning optimizer implementations requires familiarity with the following source files:
| File | Purpose | Location |
|---|---|---|
07_optimizers.py |
Contains the abstract Optimizer base class and concrete implementations of SGD, Adam, and AdamW |
tinytorch/src/07_optimizers/07_optimizers.py |
test_07_optimizers_progressive.py |
Comprehensive test suite validating numerical correctness of optimizer updates | tinytorch/tests/07_optimizers/test_07_optimizers_progressive.py |
tensor.py |
Core tensor implementation supporting requires_grad and gradient storage |
tinytorch/core/tensor.py |
autograd.py |
Automatic differentiation engine that populates Tensor.grad consumed by optimizers |
tinytorch/core/autograd.py |
These files collectively demonstrate how gradient computation, parameter storage, and optimization algorithms integrate within a minimal deep learning framework.
Summary
The cs249r_book repository provides educational deep learning optimizer implementations that clarify the algorithmic foundations of neural network training:
- SGD combines gradient descent with momentum smoothing and L2 regularization, using velocity buffers to stabilize updates across noisy mini-batches.
- Adam introduces adaptive learning rates through first and second moment estimation, automatically adjusting step sizes per-parameter based on historical gradient magnitudes.
- AdamW fixes Adam's regularization behavior by decoupling weight decay from the adaptive gradient updates, applying L2 penalty directly to parameters after the Adam step.
- All implementations inherit from a common
Optimizerbase class that standardizes gradient extraction and parameter management across the tinytorch framework.
Frequently Asked Questions
What is the difference between Adam and AdamW optimizers?
AdamW decouples weight decay from the gradient update, while Adam applies weight decay to the gradient itself. In the standard Adam implementation, weight decay gradients get scaled by the adaptive learning rate, causing parameters with large historical gradients to receive weaker regularization. AdamW applies the L2 penalty directly to the parameters after computing the Adam update, ensuring consistent regularization strength across all parameters regardless of their gradient history.
When should I use SGD instead of Adam for deep learning?
Use SGD with momentum when training large-scale vision models or when you have ample compute time for careful hyperparameter tuning. SGD often achieves better final generalization performance than adaptive methods like Adam, particularly in computer vision tasks with convolutional networks. However, SGD requires more careful learning rate scheduling and longer training times to converge. Adam works better for sparse gradients, NLP tasks, or when you need faster convergence with minimal hyperparameter tuning.
How does momentum improve the SGD optimizer?
Momentum accumulates exponentially decaying past gradients to smooth the optimization trajectory and accelerate convergence. The implementation stores a velocity buffer for each parameter that updates as v = μ * v + grad, where μ is the momentum coefficient (typically 0.9). This velocity term dampens oscillations in high-curvature directions while accelerating progress along consistent gradient directions. The result is faster convergence and reduced sensitivity to noisy mini-batch gradients compared to vanilla gradient descent.
What files contain the core optimizer logic in the cs249r_book repository?
The primary implementation resides in tinytorch/src/07_optimizers/07_optimizers.py, containing the Optimizer base class and concrete SGD, Adam, and AdamW implementations. Supporting infrastructure includes tinytorch/core/tensor.py for parameter storage and gradient tracking, and tinytorch/core/autograd.py for automatic differentiation. The test suite at tinytorch/tests/07_optimizers/test_07_optimizers_progressive.py validates numerical correctness against reference implementations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →