Skip to content

21. Gradient Clipping

import math
import os

import torch
from torch_judge import check

torch.set_default_device(os.environ.get("TORCH_DEVICE", "cpu"))


def clip_grad_norm(parameters: list[torch.Tensor], max_norm: float) -> float:
    # To clip the gradients, we first compute the total norm of the gradients across all parameters.
    # It it exceeds the specified max_norm, we scale down the gradients by max_norm/total_norm
    # to ensure that the total norm of the gradients is at most max_norm.
    total_norm = math.sqrt(sum(p.grad.norm() ** 2 for p in parameters if p.grad is not None))

    if total_norm > max_norm:
        # Scale the gradients to have a norm of max_norm
        scale = max_norm / (total_norm + 1e-6)
        for p in parameters:
            if p.grad is not None:
                p.grad.mul_(scale)

    return total_norm


if __name__ == "__main__":
    p = torch.randn(100, requires_grad=True)
    # Multiply by a large number to simulate a large gradient
    # then backpropagate to compute the gradient
    (p * 10).sum().backward()
    print("Before:", p.grad.norm().item())

    orig = clip_grad_norm([p], max_norm=1.0)

    print("After: ", p.grad.norm().item())
    print("Original norm:", orig)

    check("gradient_clipping")