I usually set a seed for experiment reproducibility when working with tensorflow. In this case my code is as follows:
class DeepNetworkModel(tf.keras.Model):
def __init__(self,
seed: int,
input_shape: int,
hidden_units: list,
num_actions: int,
batch_norm_input: bool,
batch_norm_hidden: bool,
activation: str,
kernel_initializer: str,
modelname: str = 'Deep Q Network'):
# call the parent constructor
super(DeepNetworkModel, self).__init__(name=modelname)
# set dimensionality of input/output depending on the model
inp_shape = input_shape
out_shape = num_actions
# set random seed
tf.random.set_seed(seed)
# set flag for batch norm as attribute
self.bnflag_input = batch_norm_input
self.batch_norm_hidden = batch_norm_hidden
# In setting input_shape, the batch dimension is not included.
# input layer
self.input_layer = InputLayer(input_shape=inp_shape)
# batch norm layer for inputs
if self.bnflag_input:
self.bnorm_layer = BatchNormalization(center=False,scale=False)
# set of hidden layers
self.hids = []
for i in hidden_units:
self.hids.append(Dense(i, kernel_initializer=kernel_initializer))
# check what type of activation is set
if activation == 'leaky_relu':
leaky_relu = tf.nn.leaky_relu
self.hids.append(Activation(leaky_relu))
elif activation == 'relu6':
relu6 = tf.nn.relu6
self.hids.append(Activation(relu6))
elif activation == 'elu':
elu = tf.nn.elu
self.hids.append(Activation(elu))
else:
self.hids.append(Activation(activation))
if self.batch_norm_hidden:
self.hids.append(BatchNormalization())
# output layer with linear activation by default
self.output_layer = Dense(out_shape)
def call(self,
inputs: Union[np.ndarray or tf.Tensor],
training: bool = True,
store_intermediate_outputs: bool = False):
if store_intermediate_outputs:
# build the input layer
if self.bnflag_input:
z = self.input_layer(inputs)
self.inputs = z
z = self.bnorm_layer(z, training)
self.bninputs = z
else:
z = self.input_layer(inputs)
self.inputs = z
# build the hidden layer
for layer in self.hids:
if 'batch' in layer.name:
z = layer(z, training)
else:
z = layer(z)
layer.out = z
# build the output layer
z = self.output_layer(z)
self.output_layer.out = z
else:
# build the input layer
if self.bnflag_input:
z = self.input_layer(inputs)
z = self.bnorm_layer(z, training)
else:
z = self.input_layer(inputs)
# build the hidden layer
for layer in self.hids:
if 'batch' in layer.name:
z = layer(z, training)
else:
z = layer(z)
# build the output layer
z = self.output_layer(z)
return z
Setting a seed for this simple DNN model, which operations should impact in the optimization? The only step I have in mind are the initialization of the parameters of the network. The randomness involved in selecting the minibatch to perform SGD updated (or whatever else choice of the optimizer) does not matter here, because I am implementing DQN, so that I pass as input a randomly selected batch from the buffer (without setting a seed).
Are there any other way in which I am fixing randomness working with this code? My question is relevant to understand the results of different experiment when I pass different seed as input. For now I suppose that changing the seed will just vary the initialization of the weights, but I want to be sure of that.
I have already read the doc of tensorflow abound random seeds, but it doesn't help much.