Optimization Algorithms SGD Adam Rmsprop

# Optimization Algorithms: SGD, Adam & RMSprop

## Introduction & Motivation

Optimizers: update parameters via gradients. SGD: momentum reduces noise; Nesterov accelerates. Adam: adaptive learning rates per parameter; practical default. RMSprop: per-parameter scaling; intermediate between SGD and Adam. Applications: all neural networks; choice impacts convergence speed and final performance.

Motivation: Raw gradient descent slow; momentum/adaptive rates speed convergence, escape saddles.

Applications: Deep learning training, hyperparameter selection.

---

## Core Concepts & Theory

### SGD with Momentum

Velocity accumulates; oscillations dampen; faster convergence.

### Adam (Adaptive Moment Estimation)

First and second moment estimates; per-parameter learning rates; robust.

### RMSprop

Adaptive learning rate via root mean square gradient; prevents overshooting.

---

## Mathematical Formulation

SGD with Momentum:
$$v_t = \beta v_{t-1} + g_t$$
$$ heta_t = heta_{t-1} - \alpha v_t$$

where β ≈ 0.9, α = learning rate.

Adam:
$$m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t$$
$$v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2$$
$$\hat{m}_t = \frac{m_t}{1-\beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1-\beta_2^t}$$
$$ heta_t = heta_{t-1} - \alpha \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}$$

RMSprop:
$$v_t = ho v_{t-1} + (1- ho) g_t^2$$
$$ heta_t = heta_{t-1} - \alpha \frac{g_t}{\sqrt{v_t} + \epsilon}$$

---

## Advanced Theory & Extensions

### AdaBound

Combines adaptive and SGD-like behavior; bridges gap.

### AdamW

Weight decay decoupled from learning rate; better regularization.

### Lookahead

Meta-optimizer wrapper; stabilizes oscillatory optimizers.

---

## Computational Considerations

SGD: O(1) per parameter; minimal overhead.

Adam: O(1) per parameter; maintain two moments.

RMSprop: O(1) per parameter; maintain second moment only.

---

## Practical Implementation Strategies

### Learning Rate Schedule

Warm-up then decay; cosine annealing common.

### Momentum β

Typically 0.9; higher for noisier gradients.

### Adam Defaults

β₁=0.9, β₂=0.999, ε=1e-8; robust across tasks.

---

## Benchmark Datasets & Evaluation

CIFAR-10: SGD with momentum standard; Adam faster initial convergence.

ImageNet: SGD achieves higher final accuracy; Adam popular in practice.

BERT: Adam default; momentum SGD slower for transformers.

---

## Key Challenges & Limitations

### Adam Generalization

Overfits on some tasks; test performance lower than SGD.

### Learning Rate Sensitivity

Adaptive rates reduce tuning burden; still requires decay schedule.

### Computational Overhead

Adam 2× memory (two moments); matters for large models.

---

## Hyperparameter Tuning

Learning rate α: 1e-4 to 1e-2; dataset dependent.

Momentum β: 0.9 standard; 0.95-0.99 for CNNs.

β₂ (Adam): 0.999 standard; rarely tuned.

---

## Real-World Applications & Case Studies

Vision: SGD achieves best final accuracy; Adam faster early.

NLP: Adam standard for BERT, GPT training.

Reinforcement Learning: Adam reduces instability.

---

## Integration with Other Methods

Optimizer + Learning Rate Schedule → coupled dynamics.

Optimizer + Gradient Clipping → stability in RNNs.

---

## Summary & Key Takeaways

Optimization via SGD, Adam, and RMSprop balance convergence speed and final performance through momentum and adaptive learning rates.

Principles:
1. Momentum: velocity reduces noise, accelerates.
2. Adaptive rates: per-parameter scaling; Adam most robust.
3. Learning rate scheduling: decay essential.
4. SGD: best final accuracy; Adam faster initial.
5. Choice task-dependent; Adam safe default.

---

---

## Appendix: Practical Labs

### Lab 1: SGD with Momentum

import torch
import numpy as np

class SGDMomentum:
 def __init__(self, params, lr=0.01, momentum=0.9):
 self.params = params
 self.lr = lr
 self.momentum = momentum
 self.velocities = [torch.zeros_like(p) for p in params]

 def step(self):
 for p, v in zip(self.params, self.velocities):
 if p.grad is None:
 continue
 v.mul_(self.momentum).add_(p.grad, alpha=1)
 p.data.add_(v, alpha=-self.lr)

# Test
np.random.seed(42)
params = [torch.randn(10, 10, requires_grad=True)]
optimizer = SGDMomentum(params, lr=0.01)

for _ in range(5):
 loss = params[0].sum()
 loss.backward()
 optimizer.step()
 optimizer.velocities[0].zero_()
 params[0].grad.zero_()

assert torch.isfinite(params[0]).all(), "Parameters should be finite"
print("✓ SGD momentum working")

if __name__ == "__main__":
 print("Lab 1: SGD - PASSED")

### Lab 2: Adam Optimizer

import torch
import numpy as np

class Adam:
 def __init__(self, params, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
 self.params = params
 self.lr = lr
 self.beta1 = beta1
 self.beta2 = beta2
 self.eps = eps
 self.m = [torch.zeros_like(p) for p in params]
 self.v = [torch.zeros_like(p) for p in params]
 self.t = 0

 def step(self):
 self.t += 1
 for p, m, v in zip(self.params, self.m, self.v):
 if p.grad is None:
 continue
 m.mul_(self.beta1).add_(p.grad, alpha=1-self.beta1)
 v.mul_(self.beta2).add_(p.grad ** 2, alpha=1-self.beta2)

 m_hat = m / (1 - self.beta1 ** self.t)
 v_hat = v / (1 - self.beta2 ** self.t)

 p.data.add_(m_hat / (v_hat.sqrt() + self.eps), alpha=-self.lr)

# Test
np.random.seed(42)
params = [torch.randn(10, 10, requires_grad=True)]
optimizer = Adam(params, lr=0.001)

for _ in range(5):
 loss = params[0].sum()
 loss.backward()
 optimizer.step()
 params[0].grad.zero_()

assert torch.isfinite(params[0]).all(), "Parameters should be finite"
print("✓ Adam working")

if __name__ == "__main__":
 print("Lab 2: Adam - PASSED")

### Lab 3: RMSprop Optimizer

import torch
import numpy as np

class RMSprop:
 def __init__(self, params, lr=0.01, rho=0.99, eps=1e-8):
 self.params = params
 self.lr = lr
 self.rho = rho
 self.eps = eps
 self.v = [torch.zeros_like(p) for p in params]

 def step(self):
 for p, v in zip(self.params, self.v):
 if p.grad is None:
 continue
 v.mul_(self.rho).add_(p.grad ** 2, alpha=1-self.rho)
 p.data.add_(p.grad / (v.sqrt() + self.eps), alpha=-self.lr)

# Test
np.random.seed(42)
params = [torch.randn(10, 10, requires_grad=True)]
optimizer = RMSprop(params, lr=0.01)

for _ in range(5):
 loss = params[0].sum()
 loss.backward()
 optimizer.step()
 params[0].grad.zero_()

assert torch.isfinite(params[0]).all(), "Parameters should be finite"
print("✓ RMSprop working")

if __name__ == "__main__":
 print("Lab 3: RMSprop - PASSED")

### Lab 4: Optimizer Comparison

import torch
import numpy as np

def compare_optimizers(initial_params, n_steps=100):
 """Compare convergence speed of optimizers"""
 results = {}

 for opt_name in ['SGD', 'Adam', 'RMSprop']:
 params = [p.clone().detach().requires_grad_(True) for p in initial_params]

 if opt_name == 'SGD':
 optimizer = torch.optim.SGD(params, lr=0.01, momentum=0.9)
 elif opt_name == 'Adam':
 optimizer = torch.optim.Adam(params, lr=0.001)
 else:
 optimizer = torch.optim.RMSprop(params, lr=0.01)

 losses = []
 for _ in range(n_steps):
 loss = (params[0] ** 2).sum()
 optimizer.zero_grad()
 loss.backward()
 optimizer.step()
 losses.append(loss.item())

 results[opt_name] = losses

 return results

# Test
np.random.seed(42)
initial = [torch.randn(10, 10)]

results = compare_optimizers(initial, n_steps=100)

assert len(results) == 3, "Should compare 3 optimizers"
for opt, losses in results.items():
 assert len(losses) == 100, "Should have 100 steps"
 assert losses[-1] < losses[0], "Loss should decrease"
 assert all(np.isfinite(l) for l in losses), "All losses finite"

print("✓ Optimizer comparison working")

if __name__ == "__main__":
 print("Lab 4: Comparison - PASSED")

Go deeper with CFSGPT

Get AI-powered deep-dives, save terms, and run advanced simulations — free account.

Create Free Account