CARLsim
3.1.3
CARLsim: a GPU-accelerated SNN simulator
|
In this tutorial, we will learn how to use the SimpleWeightTuner utility, which is a piece of software that allows us to tune weight values of a specific connection (i.e., a collection of synapses) on-the-fly, so that a specific neuron group fires at a predefined target firing rate.
We will assume a three-layer feedforward network (with a single hidden layer), whose weights we would like to tune so that both hidden layer and output layer fire at some specific (but different) firing rate. In order to achieve this, we will first tune the weights from the input layer to the hidden layer, based on observed hidden layer activity, and then tune the weights from the hidden layer to the output layer, based on observed output layer activity.
This tutorial assumes that you know how to configure a CARLsim network (by setting up groups and connections).
The complete source code explained in this tutorial can be found in doc/source/tutorial/5_simple_weight_tuner
and consists of the following files and folders:
Makefile
: The Makefile used to compile the C++ source filemain_simple_weight_tuner.cpp
: A C++ source file that implements the network in CARLsimresults/
: A directory that will contain all metadata and results generated by the simulationdoc
: A directory that contains this Doxygen source code )In order to compile and run the source code (on Linux), open a terminal, navigate to doc/source/tutorial/5_simple_weight_tuner
, and type:
Suppose we are given a feedforward network of a certain topology, and we are interested in finding the synaptic weight values that lead to neuronal firing at a specific target firing rate.
The most straightfoward approach is to hand-tune the weights; by starting at some reasonable weight value, observing network's output activity to some stimulus, and then varying the weight value by hand until some target firing rate is reached. Although this approach might lead to a valid solution, it might also be a time-consuming and tedious procedure. An alternative approach would be to perform local search on the domain of valid weight values using the Parameter Tuning Interface (see Chapter 10: ECJ), but specifying a usable fitness function and performing the search might actually take more time than simple hand-tuning.
In this tutorial, we will take an intermediate approach. The SimpleWeightTuner utility automates the procedure of manual weight tuning by repeatedly updating the weights according to some observed output activity. The idea of the utility is pretty simple (inspired by the bisection method): If a given weight leads to an output firing rate below the target firing rate, increase the weight by a certain step size. As soon as the chosen weight leads to activity above some target firing rate, change the sign of the weight and cut its magnitude in half. Repeat until activity is within some error margin of the target firing rate, or a maximum number of iteration steps was reached.
Consider the following three-layer feedforward network:
Instead of hand-tuning the weights for c0
and c1
until groups gHid
and gOut
fire at some desired firing rate, we will automate the tuning using SimpleWeightTuner:
Here we define that gHid
should fire at 27.4Hz, and gOut
should fire at 42.8Hz, with an error margin of +- 0.015Hz (arbitrary values).
We create two instances of class SimpleWeightTuner and pass to it a pointer to the above created CARLsim network (sim
) as well as the termination conditions: The algorithm will terminate either if the absolute error between observed firing rate and target firing rate is smaller than some error margin (errorMarginHz
), or upon reaching the maximum number of iterations (maxIter
).
The goal is to first tune the weights of connection ID c1
, which are the weights connecting the input layer gIn
to the hidden layer gHid
, until the hidden layer fires at 27.4Hz, and then to tune the weights of connection ID c2
, which are the weights connecting the hidden layer to the ouput layer gOut
, until the ouput layer fires at 42.8Hz. For this, we pass the connection ID and initial weight to SimpleWeightTuner::setConnectionToTune. SimpleWeightTuner::setTargetFiringRate then specifies which group should fire at which rate. Note that the connection and group specified here are completely independent from each other.
All that is left to do is to execute the algorithm until finished:
Each while loop will repeatedly run the CARLsim network for a short amount of time (default: 1s), during which spikes of the group specified in SimpleWeightTuner::setTargetFiringRate are recorded. At the end of each iteration, the observed firing rate is compared to the target firing rate, and weights are updated accordingly (using CARLsim::biasWeights). This procedure is repeated until one of the termination condition is reached (indicated by SimpleWeightTuner::done returning value true
), at which point we are done.
We could now retrieve the tuned weights using a ConnectionMonitor or save the entire network state to file via CARLsim::saveSimulation so that it can later be reloaded using CARLsim::loadSimulation. Alternatively, we could just run the network a bit more to convince ourselves that the groups fire at the correct firing rate:
The complete code produces the following console output: