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.
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.
$ 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
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.
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
$ rm seed_smoke.py