Skip to content

← Optimizers & Training step 7 of 8

Easy End-to-End

Gradient Clipping

Norm clipping limits the total magnitude of a gradient update while keeping its direction. Treat every tensor in the input list as part of one long vector, even when the tensors have different shapes.

Your task

Implement clip_gradients(gradients, max_norm). The input is a list of floating-point gradient tensors and a finite, nonnegative norm limit. Compute the original global L2 norm:

global_norm = sqrt(sum of every squared element in every gradient tensor)

If global_norm > max_norm, multiply every tensor by the same factor max_norm / global_norm. Otherwise, leave its values unchanged. Preserve the inputs and the order and shapes of the returned tensors. Do not update model parameters.

Return a dictionary with:

  • "clipped_gradients": the list of resulting tensors.
  • "global_norm": the original norm as a Python float, rounded to seven decimal places.

Use the unrounded norm for the comparison and scaling. Round only the reported scalar. Do not report the smaller norm after clipping.

One norm, one scale

For gradients [3] and [4], the global norm is 5. With max_norm = 2.5, scale both by 0.5, returning [1.5] and [2]. Report global_norm = 5.0.

Clipping each tensor separately would give a different result. Clamping individual elements to a range such as [-max_norm, max_norm] is also a different operation. Global norm clipping uses one scale for all elements, including negative ones.

Boundary cases

If the norm equals the limit, no clipping is needed. All-zero gradients have norm zero and remain zero, including when max_norm = 0; avoid a 0 / 0 calculation. A positive norm with max_norm = 0 yields zero gradients. An empty list has norm zero and returns an empty list.

Clipping can limit a large update, but it does not by itself guarantee stable training or fix nonfinite gradients. This exercise uses finite values whose squared sums fit in the calculation dtype.