TensorTonic
Study Plans
Study Plan
SGD with Momentum over a Pytree
JAX
Easy

Apply one step of stochastic gradient descent with momentum to every array in a parameter pytree. Here the pytrees are nestings of Python dicts and lists whose innermost entries, called leaves, are arrays. The three inputs params, grads and velocity share one structure, and for every leaf you compute

vnew=μ v+g,pnew=p−η vnewv_{\text{new}} = \mu \, v + g, \qquad p_{\text{new}} = p - \eta \, v_{\text{new}}vnew​=μv+g,pnew​=p−ηvnew​

Here p is a leaf of params, g is the matching leaf of grads, v is the matching leaf of velocity, μ\muμ is momentum and η\etaη is learning_rate. The structure can be anything from a single bare array to dicts that hold lists of dicts.

Return a tuple (new_params, new_velocity). Both are pytrees with exactly the structure of params, and every leaf is a JAX array with the shape and dtype of the matching leaf of params.

Examples

Input: params = {"w": [0.5, -1.0], "b": 0.2}, grads = {"w": [0.1, 0.4], "b": -0.5}, velocity = {"w": [0.0, 0.0], "b": 0.0}, learning_rate = 0.1, momentum = 0.9

Output: (new_params, new_velocity) = ({"b": 0.25, "w": [0.49, -1.04]}, {"b": -0.5, "w": [0.1, 0.4]})

Explanation: The velocity starts at zero, so the new velocity equals the gradient. Each parameter then moves against its gradient by a tenth of it, so w[0] becomes 0.5 minus 0.1 times 0.1, which is 0.49, and b becomes 0.2 plus 0.1 times 0.5, which is 0.25.

Input: params = {"dense": {"w": [[1.0, 2.0], [3.0, 4.0]], "b": [0.0, 0.5]}, "scale": 1.0}, grads = {"dense": {"w": [[0.5, -0.5], [1.0, 0.0]], "b": [0.2, -0.2]}, "scale": 0.3}, velocity = {"dense": {"w": [[1.0, 1.0], [-1.0, 0.0]], "b": [0.0, 2.0]}, "scale": -0.5}, learning_rate = 0.05, momentum = 0.5

Output: (new_params, new_velocity) = ({"dense": {"b": [-0.01, 0.46], "w": [[0.95, 2.0], [2.975, 4.0]]}, "scale": 0.9975}, {"dense": {"b": [0.2, 0.8], "w": [[1.0, 0.0], [0.5, 0.0]]}, "scale": 0.05})

Input: params = {"layers": [{"w": [1.0, -1.0], "b": 0.0}, {"w": [2.0], "b": 1.0}]}, grads = {"layers": [{"w": [0.2, 0.2], "b": 1.0}, {"w": [-1.0], "b": 0.5}]}, velocity = {"layers": [{"w": [0.1, -0.1], "b": 0.0}, {"w": [0.4], "b": -0.2}]}, learning_rate = 0.2, momentum = 0.8

Output: (new_params, new_velocity) = ({"layers": [{"b": -0.2, "w": [0.944, -1.024]}, {"b": 0.932, "w": [2.136]}]}, {"layers": [{"b": 1.0, "w": [0.28, 0.12]}, {"b": 0.34, "w": [-0.68]}]})

Hint 1

jax.tree.map(lambda p, v: ..., params, velocity) works leaf by leaf.

Hint 2

Call jax.tree.map twice, once for the new velocity and once for the new params.

Hint 3

A Python float times a bfloat16 array stays bfloat16.

Requirements

  • Use JAX, and walk the trees with jax.tree.map or the other jax.tree_util functions so that any nesting works.
  • Return new containers and leave params, grads and velocity unchanged, including the dicts and lists that hold their leaves.

Constraints

  • params, grads and velocity have identical pytree structures made of dicts and lists, with at least one leaf.
  • Each leaf has between zero and three dimensions, and matching leaves have the same shape.
  • All leaves share one dtype, float32 or bfloat16. Public cases and custom Run inputs use float32.
  • learning_rate is a Python float of at least 0, and momentum is a Python float with 0 ≤ momentum < 1.
  • In the Run panel, a JSON list whose items are all objects is read as a list node of the tree, and any other list or number is read as one array leaf.
Try Similar Problems
Global Norm Gradient ClippingMediumAdamW from ScratchMediumGradient Accumulation with ScanHardFunctional Array UpdateEasyPRNG Key SplittingEasyMasked Mean Without Boolean IndexingEasyValue and Grad of a LossEasyStatic Arguments in jitEasyModel Ensemble with in_axesEasyRotary Position EmbeddingEasyKV Cache Update at a PositionEasyDevice Mesh and Named ShardingEasy

Accepts: object, array, number

Accepts: object, array, number

Accepts: object, array, number

Accepts: number

Accepts: number

You must run your code first.
SGD with Momentum over a Pytree

SGD with Momentum over a Pytree

JAX
Easy

Apply one step of stochastic gradient descent with momentum to every array in a parameter pytree. Here the pytrees are nestings of Python dicts and lists whose innermost entries, called leaves, are arrays. The three inputs params, grads and velocity share one structure, and for every leaf you compute

vnew=μ v+g,pnew=p−η vnewv_{\text{new}} = \mu \, v + g, \qquad p_{\text{new}} = p - \eta \, v_{\text{new}}vnew​=μv+g,pnew​=p−ηvnew​

Here p is a leaf of params, g is the matching leaf of grads, v is the matching leaf of velocity, μ\muμ is momentum and η\etaη is learning_rate. The structure can be anything from a single bare array to dicts that hold lists of dicts.

Return a tuple (new_params, new_velocity). Both are pytrees with exactly the structure of params, and every leaf is a JAX array with the shape and dtype of the matching leaf of params.

Examples

Input: params = {"w": [0.5, -1.0], "b": 0.2}, grads = {"w": [0.1, 0.4], "b": -0.5}, velocity = {"w": [0.0, 0.0], "b": 0.0}, learning_rate = 0.1, momentum = 0.9

Output: (new_params, new_velocity) = ({"b": 0.25, "w": [0.49, -1.04]}, {"b": -0.5, "w": [0.1, 0.4]})

Explanation: The velocity starts at zero, so the new velocity equals the gradient. Each parameter then moves against its gradient by a tenth of it, so w[0] becomes 0.5 minus 0.1 times 0.1, which is 0.49, and b becomes 0.2 plus 0.1 times 0.5, which is 0.25.

Input: params = {"dense": {"w": [[1.0, 2.0], [3.0, 4.0]], "b": [0.0, 0.5]}, "scale": 1.0}, grads = {"dense": {"w": [[0.5, -0.5], [1.0, 0.0]], "b": [0.2, -0.2]}, "scale": 0.3}, velocity = {"dense": {"w": [[1.0, 1.0], [-1.0, 0.0]], "b": [0.0, 2.0]}, "scale": -0.5}, learning_rate = 0.05, momentum = 0.5

Output: (new_params, new_velocity) = ({"dense": {"b": [-0.01, 0.46], "w": [[0.95, 2.0], [2.975, 4.0]]}, "scale": 0.9975}, {"dense": {"b": [0.2, 0.8], "w": [[1.0, 0.0], [0.5, 0.0]]}, "scale": 0.05})

Input: params = {"layers": [{"w": [1.0, -1.0], "b": 0.0}, {"w": [2.0], "b": 1.0}]}, grads = {"layers": [{"w": [0.2, 0.2], "b": 1.0}, {"w": [-1.0], "b": 0.5}]}, velocity = {"layers": [{"w": [0.1, -0.1], "b": 0.0}, {"w": [0.4], "b": -0.2}]}, learning_rate = 0.2, momentum = 0.8

Output: (new_params, new_velocity) = ({"layers": [{"b": -0.2, "w": [0.944, -1.024]}, {"b": 0.932, "w": [2.136]}]}, {"layers": [{"b": 1.0, "w": [0.28, 0.12]}, {"b": 0.34, "w": [-0.68]}]})

Hint 1

jax.tree.map(lambda p, v: ..., params, velocity) works leaf by leaf.

Hint 2

Call jax.tree.map twice, once for the new velocity and once for the new params.

Hint 3

A Python float times a bfloat16 array stays bfloat16.

Requirements

  • Use JAX, and walk the trees with jax.tree.map or the other jax.tree_util functions so that any nesting works.
  • Return new containers and leave params, grads and velocity unchanged, including the dicts and lists that hold their leaves.

Constraints

  • params, grads and velocity have identical pytree structures made of dicts and lists, with at least one leaf.
  • Each leaf has between zero and three dimensions, and matching leaves have the same shape.
  • All leaves share one dtype, float32 or bfloat16. Public cases and custom Run inputs use float32.
  • learning_rate is a Python float of at least 0, and momentum is a Python float with 0 ≤ momentum < 1.
  • In the Run panel, a JSON list whose items are all objects is read as a list node of the tree, and any other list or number is read as one array leaf.
Try Similar Problems
Global Norm Gradient ClippingMediumAdamW from ScratchMediumGradient Accumulation with ScanHardFunctional Array UpdateEasyPRNG Key SplittingEasyMasked Mean Without Boolean IndexingEasyValue and Grad of a LossEasyStatic Arguments in jitEasyModel Ensemble with in_axesEasyRotary Position EmbeddingEasyKV Cache Update at a PositionEasyDevice Mesh and Named ShardingEasy

Accepts: object, array, number

Accepts: object, array, number

Accepts: object, array, number

Accepts: number

Accepts: number

You must run your code first.