Optimizer Update Rules¶
This page states the gradient-descent variants, update equations, defaults, and implementation references. See optimization algorithms for the reasoning behind the methods and how to choose an optimizer for recommendations.
Batch Gradient Descent¶
Batch gradient descent uses the entire training dataset for each update.
for epoch in range(num_epochs):
gradient = compute_gradient(X_train, y_train, theta)
theta -= learning_rate * gradient
Stochastic Gradient Descent¶
Stochastic gradient descent (SGD) uses one training example at a time.
for epoch in range(num_epochs):
X_shuffled, y_shuffled = shuffle(
X_train, y_train
) # To ensure randomness in data pattern
for i in range(m): # No. of training examples
gradient = compute_gradient(X_shuffled[i], y_shuffled[i], theta)
theta -= learning_rate * gradient
Mini-batch Gradient Descent¶
Mini-batch gradient descent uses small batches of training examples.
for epoch in range(num_epochs):
mini_batches = create_mini_batches(
X_train, y_train, batch_size
) # To ensure random batches
for mini_batch in mini_batches:
X_batch, y_batch = mini_batch
gradient = compute_gradient(X_batch, y_batch, theta)
theta -= learning_rate * gradient
Here \(\mathcal{B}\) is the mini-batch and \(|\mathcal{B}|\) is the batch size.
Suggested batch sizes:
- Small datasets: 32-64 samples.
- Medium datasets: 128-256 samples.
- Large datasets: 256-512 samples.
- Very large datasets: 512-1024 samples.
Powers of 2 work well with GPU memory architecture. The implementation is available in mini_batch.py.
Momentum¶
Momentum updates parameters using a velocity \(v_t\) rather than the current gradient alone.
v = np.zeros_like(theta) # Initialize velocity
for t in range(num_iterations):
gradient = compute_gradient(X_batch, y_batch, theta)
v = beta * v + (1 - beta) * gradient
theta -= learning_rate * v
Typical choices are \(\beta = 0.9\) for a standard setting, \(\beta = 0.99\) for noisier gradients, and \(\beta = 0.5\) for rapidly changing landscapes. See momentum.py.
RMSprop¶
RMSprop adapts the learning rate for each parameter using historical gradient magnitudes.
s = np.zeros_like(theta)
epsilon = 1e-8 # To avoid division by zero
for t in range(num_iterations):
gradient = compute_gradient(X_batch, y_batch, theta)
s = beta * s + (1 - beta) * gradient**2
theta -= learning_rate * gradient / (np.sqrt(s) + epsilon)
Parameters with frequent large gradients receive smaller effective learning rates; parameters with small or rare gradients receive larger ones. \(\beta = 0.999\) is a typical setting. See rmsprop.py.
Adam¶
Adam combines momentum and RMSprop with bias correction.
m = np.zeros_like(theta) # First moment (momentum)
v = np.zeros_like(theta) # Second moment (RMSprop)
epsilon = 1e-8
beta1 = 0.9
beta2 = 0.999
for t in range(1, num_iterations + 1):
gradient = compute_gradient(X_batch, y_batch, theta)
m = beta1 * m + (1 - beta1) * gradient
v = beta2 * v + (1 - beta2) * gradient**2
m_corrected = m / (1 - beta1**t)
v_corrected = v / (1 - beta2**t)
theta -= learning_rate * m_corrected / (np.sqrt(v_corrected) + epsilon)
Default hyperparameters are \(\alpha = 0.001\), \(\beta_1 = 0.9\), \(\beta_2 = 0.999\), and \(\epsilon = 1e^{-8}\). See adam.py.
AdamW and AMSGrad¶
AdamW applies weight decay directly during the parameter update:
# Standard Adam update
theta = theta - learning_rate * m_corrected / (np.sqrt(v_corrected) + epsilon)
# Added weight decay
theta = theta - learning_rate * weight_decay * theta
AMSGrad maintains the maximum of past squared gradients instead of only their exponential average. This addresses theoretical convergence issues in Adam.
Implementation Reference¶
The generated optimizer API reference documents the package implementation. A side-by-side comparison is available in comparison.py.