How to set a random seed in PyTorch

Random seeds give PyTorch experiments a repeatable starting point for weight initialization, tensor sampling, data shuffling, and small smoke tests. Setting the same seed before those operations makes debugging and comparison runs easier because the random inputs begin from the same state.

PyTorch, Python, and NumPy keep separate random number generators. torch.manual_seed() seeds PyTorch random number generators on all devices, while random.seed() and np.random.seed() cover code paths that use Python's standard random module or NumPy's legacy global generator.

A fixed seed narrows randomness; it does not guarantee identical results across PyTorch releases, hardware backends, CPU versus GPU execution, or nondeterministic kernels. Use deterministic operation controls when backend determinism matters, and pass a dedicated torch.Generator to operations that should have an isolated random stream.

Steps to set PyTorch random seeds:

  1. Create a smoke script that resets Python, NumPy, and PyTorch seeds before random work.
    seed_smoke.py
    import random
     
    import numpy as np
    import torch
     
     
    SEED = 20260707
     
     
    def set_seed(seed):
        random.seed(seed)
        np.random.seed(seed)
        torch.manual_seed(seed)
     
     
    def run_once():
        set_seed(SEED)
     
        model = torch.nn.Linear(4, 2)
        torch_values = torch.randn(4)
        numpy_values = np.random.rand(3)
        python_values = [random.random() for _ in range(3)]
     
        generator = torch.Generator().manual_seed(SEED)
        local_order = torch.randperm(8, generator=generator)
     
        return {
            "weight": model.weight.detach().flatten()[:4],
            "torch": torch_values,
            "numpy": numpy_values,
            "python": python_values,
            "order": local_order,
        }
     
     
    first = run_once()
    second = run_once()
     
    torch_match = torch.equal(first["torch"], second["torch"])
    weights_match = torch.equal(first["weight"], second["weight"])
    numpy_match = np.array_equal(first["numpy"], second["numpy"])
    python_match = first["python"] == second["python"]
     
    print(f"seed: {SEED}")
    print(f"torch values match: {torch_match}")
    print(f"model weights match: {weights_match}")
    print(f"numpy values match: {numpy_match}")
    print(f"python values match: {python_match}")
    print(f"local generator order: {first['order'].tolist()}")
    print(f"runs match: {all([torch_match, weights_match, numpy_match, python_match])}")

    The helper covers PyTorch random draws, Python's random module, and NumPy's legacy global generator. Code that creates NumPy Generator objects with np.random.default_rng() should pass the seed when the generator is created.

  2. Run the smoke script and verify that every match line reports True.
    $ python3 seed_smoke.py
    seed: 20260707
    torch values match: True
    model weights match: True
    numpy values match: True
    python values match: True
    local generator order: [1, 2, 7, 3, 6, 0, 4, 5]
    runs match: True
  3. Move the seed helper to the top of the project entrypoint before model, tensor, dataset, loader, or augmentation creation.
    SEED = 20260707
     
    set_seed(SEED)
     
    model = torch.nn.Linear(4, 2)
    inputs = torch.randn(8, 4)

    Calling the helper after a model, sampler, loader, or augmentation object has already consumed random numbers does not rewind the random choices that already happened.

  4. Use a dedicated torch.Generator when one operation needs its own repeatable random stream.
    split_generator = torch.Generator().manual_seed(SEED)
    order = torch.randperm(8, generator=split_generator)

    APIs such as random_split() and DataLoader can accept a generator so their random order does not depend only on the global RNG state.
    Related: How to randomly split a dataset in PyTorch

  5. Remove the temporary smoke script after copying the seed block into project code.
    $ rm seed_smoke.py