EasyOptimization

RMSProp Optimizer (Single Update Step)

Optimization

Easy

Problem

Implement one update step of the RMSProp optimizer. Given current parameters, gradients, and running squared gradient accumulator, return updated parameters and accumulator.

Step 1: Update Running Average

s_t = \beta \cdot s_{t-1} + (1 - \beta) \cdot g_t^2

Step 2: Parameter Update

w_t = w_{t-1} - \frac{\eta}{\sqrt{s_t + \varepsilon}} \cdot g_t

Where: w = parameters, g = gradients, s = squared gradient accumulator, η = learning rate, β = decay factor, ε = stability constant

Theory

AdaGrad was a breakthrough: it gave each parameter its own learning rate by dividing by the accumulated squared gradients. But AdaGrad's accumulator only grows, so the effective learning rate eventually shrinks to near zero and the model stops learning.

RMSProp (Root Mean Square Propagation) fixes this with one simple change: use a decaying average instead of a sum.

Geoffrey Hinton proposed RMSProp in a Coursera lecture in 2012. It was never published in a formal paper, yet it became one of the most widely used optimizers.


The Key Change: Exponential Decay

AdaGrad's accumulator sums all past squared gradients:

G_t = G_{t-1} + g_t^2 \quad \text{(only grows)}

RMSProp replaces this with an exponentially decaying average:

s_t = \beta \cdot s_{t-1} + (1 - \beta) \cdot g_t^2

Then the parameter update uses the same formula as AdaGrad:

w_t = w_{t-1} - \frac{\eta}{\sqrt{s_t + \epsilon}} \cdot g_t

The only difference is s_t (decaying average) instead of G_t (ever-growing sum).


Understanding the Exponential Average

The exponential average is a "leaky" accumulator. To see why, expand a few steps with \beta = 0.9:

Each past gradient gets multiplied by \beta one more time at each step:

The effective window is approximately \frac{1}{1-\beta} steps:


Why This Fixes AdaGrad's Problem

The crucial difference: the effective learning rate can increase again.

With AdaGrad:

With RMSProp:

This means the model can keep making meaningful progress throughout training, even if the character of the gradients changes over time.


A Detailed Example

Parameters: w = [1.0, 2.0], accumulators: s = [0.0, 0.0], \eta = 0.01, \beta = 0.9

Step 1 with gradient g = [0.5, 2.0]:

Update accumulators:

Effective learning rates:

Update parameters:

Both parameters moved by approximately 0.032, even though parameter 2's gradient was 4x larger. RMSProp equalized the step sizes.

Step 2 with gradient g = [0.5, 0.1] (parameter 2's gradient dropped):

Update accumulators:

Effective learning rates:

After many more steps with small gradients for parameter 2, s_2 would shrink toward 0.1 \times 0.01 = 0.001, and its effective learning rate would recover. With AdaGrad, it would have stayed at 0.4 + 0.01 = 0.41 forever.


Choosing Beta

The decay rate \beta controls the memory length:


RMSProp in the Optimizer Family

RMSProp sits in a clear lineage:

If you set \beta_1 = 0 in Adam (no momentum) and remove bias correction, you get RMSProp.


Where RMSProp Is Used

Examples

Example 1

Input
w = [1.0, 2.0], g = [0.2, -0.4], s = [0.0, 0.0], lr = 0.1, beta = 0.9, eps = 1e-8
Output
([0.683773, 2.316228], [0.004, 0.016])
Explanation
The squared-gradient accumulator is updated first, then each parameter uses its own scaled step.

Example 2

Input
w = [5.0], g = [0.0], s = [0.1], lr = 0.1, beta = 0.9, eps = 1e-8
Output
([5.0], [0.09])

Example 3

Input
w = [[1.0, 2.0]], g = [[0.1, 0.2]], s = [[0.01, 0.04]], lr = 0.1, beta = 0.9, eps = 1e-8
Output
([[0.9, 1.9]], [[0.01, 0.04]])

Hints

  1. Convert w, g, and s to NumPy arrays before computing the accumulator update.
  2. Use g * g for squared gradients and np.sqrt(new_s) in the parameter update.

Requirements

Constraints

Starter Code

import numpy as np

def rmsprop_step(
    w: list,
    g: list,
    s: list,
    lr: float = 0.001,
    beta: float = 0.9,
    eps: float = 1e-8,
) -> tuple[list, list]:
    """
    Returns (new_w, new_s) with the same shapes as the inputs.
    """
    # Write code here
    pass

Test Cases

CaseMatches
First stepExample 1public
Zero gradientExample 2public
2D inputExample 3public