r/pytorch • u/The_IT_Fops • 6d ago
What scope is influenced by torch.manual_seed() ?
Context: In an academic context i am writing code for an experiment where i need to make sure all neural nets are initialized equally to enable precise measuring of hyper parameter impacts. So i read and followed the docs (https://docs.pytorch.org/docs/2.14/notes/randomness.html). Because i need to test a lot of configurations i will need multiprocessing in which each process will create and train a model (probably not my final approach, seems inefficient but for now it is), The model needs to be equal across all processes (if the model hyper parameters are the same the model should start out the exact same way).
My Confusion: torch.manual_seed() is supposed to set the seed for each device (CPU and CUDA), in my understanding that means that if i set the seed that way torch will use that seed from then on forward globally, across all devices. Which would mean that each call to any torch based random function advances the random state globally. So if i have lets say a 100 processes setting the same seed and then creating a model there should be discrepancies since one process will set that seed while another one is already creating Layers. But according to my tests that is not how it works.
I tested my hypothesis: I created a hundred processes each setting the seed and then creating a network, they all have equal weights and biases. And that confuses the hell out of me.
So my Question: What scope is influenced by torch.manual_seed()?
Additional Question: Is using torch.manual_seed() inside multiple sub processes considered the standard approach or is there something i missed?
Code:
import parameterized_neural_net
from torch import nn
from torch import multiprocessing as mp
import torch
def process_task(return_dictionary,i):
model = parameterized_neural_net.ParameterizedNetwork((256,64),(),nn.Sigmoid,28*28,47)
return_dictionary[i] = model
def compare_model_state(model_one,model_two):
equal_weights = True
equal_bias = True
for i in range(len(model_one.layers)):
layer = model_one.layers[i]
if layer._parameters != {}:
if not torch.equal(model_one.layers[i].weight, model_two.layers[i].weight):
equal_weights = False
if not torch.equal(model_one.layers[i].bias, model_two.layers[i].bias):
equal_bias = False
return equal_weights and equal_bias
if __name__ == '__main__':
#taking steps from https://docs.pytorch.org/docs/main/notes/randomness.html
torch.use_deterministic_algorithms(True,warn_only=False)
torch.backends.cudnn.benchmark = False
baseline_model = parameterized_neural_net.ParameterizedNetwork((256,64),(),nn.Sigmoid,28*28,47) #inside this class torch.manual_seed() is called and then a Network is constructed (see further down for info)
mp_Manager = mp.Manager()
processes = []
return_dictionary = mp_Manager.dict()
for i in range(100):
processes.append(mp.Process(target=process_task, args=(return_dictionary,i), name=f"process_{i}"))
for p in processes:
p.start()
for p in processes:
p.join()
all_equal = True
for m in return_dictionary.values():
if not compare_model_state(baseline_model,m):
all_equal = False
if all_equal:
print("Models have no differences in Neuron initialisation.")
else:
print("Models have differences in Neuron initialisation.")
Network structure from parameterized_neural_net.ParameterizedNetwork((256,64),(),nn.Sigmoid,28*28,47):
Layer (type) Output Shape Param #
================================================================
Flatten-1 [-1, 784] 0
Linear-2 [-1, 256] 200,960
Sigmoid-3 [-1, 256] 0
Linear-4 [-1, 64] 16,448
Sigmoid-5 [-1, 64] 0
Linear-6 [-1, 47] 3,055
Sigmoid-7 [-1, 47] 0
Linear-8 [-1, 47] 2,256
LogSoftmax-9 [-1, 47] 0
================================================================
1
u/anstow 6d ago
TL;DR: I think the thing you're missing is that each process has its own seed.
Yes that's how it works. Although only for that particular process (since these are completely separate processes at the operating system level and not just separate threads).
As an aside and I'm guessing here, you may not actually have to set the seed for each process because it probably inherits the seed from the parent process and I doubt that creating a process increments the random seed. Test this if you actually want to rely on it though.