Adding noise to network weights in Tensorflow
By: Daan Klijn
Tags: Evolutionary Computation, Python, TensorFlow, Machine Learning

Currently I am working on an implementation of a Neuroevolution algorithm. These type of methods use Evolutionary Algorithms to evolve the weights of a Neural Network. One essential part of evolving these weights is the so called “Mutation” step. During this step Gaussian noise is added to the weights of a Network, which allows for the creation of various mutations of the original network. I realized that I wasn’t sure how one would add this noise to all of the weights in a Tensorflow network. So I figured it would be nice to write a small blog post about it.

Creating a simple network

The first thing I did was create a small network, which I will use to find out how one would add noise to its weights. Below I initialize a small network that has a single Dense with 100 nodes, and that has it’s weights and biases initialized at 0.

from tensorflow.python.keras import Input, Model

from tensorflow.python.keras.layers import Dense input = Input(shape=1)

output = Dense(100, activation="softmax", kernel_initializer='zeros')(input)

model = Model(inputs=input, outputs=output)

To verify that the setup is correctly, let’s print its weights:

print(model.get_weights())

__________________________________________________________________

[array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32), array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)]

Adding noise: method 1

As shown earlier, we can leverage the get_weights method to retrieve the weights of a network. In the same manor set_weight can be used to set the weights of the network. Due to this we can add noise by first retrieving the original weights, followed by setting the weights to their original value plus some noise vector.

from numpy.random import normal def add_noise_1(model):

weights = model.get_weights()

for layer in weights:

noise = normal(loc=0.0, scale=1.0, size=layer.shape)

layer += noise

model.set_weights(weights)

We can now verify that this worked:

add_noise_1(model)

print(model.get_weights())

__________________________________________________________________

[array([[-0.22465831, -0.7272695 , 0.60172313, -0.46081063, 0.7302764 ,

-0.49482158, -0.98451763, -0.17196278, -0.9898952 , -0.01227215]],

dtype=float32), array([ 1.4207387 , 0.7875955 , 0.28213844, 0.36810005, -0.33920723,

0.26612052, -0.1289873 , -0.524746 , 0.5679675 , -1.0648334 ],

dtype=float32)]

Adding noise: method 2

I figured that retrieving the weights, updating them and then setting them again might be sub-optimal. Therefore I hypothesized that adding our noise directly to the networks weights could be faster. Below I implemented a function that directly adds a value to the weights using the assign_add method.

def add_noise_2(model):

for layer in model.trainable_weights:

noise = normal(loc=0.0, scale=1.0, size=layer.shape)

layer.assign_add(noise)

Comparing method 1 and 2

To verify whether there is really a significant difference between the two methods I ran them for a range of different network sizes and compared the runtimes.

As we can see in the plot above, method 2 seems to be significantly faster for small networks, but this difference becomes neglectable when the networks start to get somewhat bigger.

Hope this helps someone!

Daan