PyTorch models for RNNs commonly used in computational neuroscience.
PyTorch nn modules and associated utilities for recurrent rate network models and spiking network models commonly used in computational neuroscience.
model = RateModel(recurrent, readin=None, readout=None, f='tanh', eta=1, rho_recurrent=1, rho_input=1, rho_output=1,
bias_recurrent=True, bias_output=True, Network_Type='R')
y = model(x, Nt = None, initial_state = 'zero', return_time_series = True, store_hidden_history = True)
The shape of x can either be:
(batch_size, Nt, N_recurrent)
or
(batch_size, N_recurrent)
In the second case, x interpreted as time-constant (same input at each time) and Nt must be passed into the forward pass.
In the first case, Nt is inferred from x and should not be passed.
Here, Nt is the number of time steps and N_recurrent is the number of hidden units in the recurrent layer.
If return_time_series==True then the output has shape
(batch_size, Nt, N_output)
If return_time_series==False then the output has shape
(batch_size, N_output)
A standard RNN can be created using
model = RateModel(N, Nx)
where N is the number of hidden units and Nx is the input size. This will create an RNN that is nearly equivalent a standard PyTorch
RNN (model = torch.nn.RNN(N,Nx)).
A forward pass can then be computed by
y = model(x)
where the input, x, should have shape (batch_size, Nt, Nx) and Nt is the number of time steps. Then y will have shape (batch_size, Nt, N) and it will be defined by the dynamics
where
For another example (simulating continuous time dynamics), see RateModelExamples.ipynb
There are two types of rate network models. The first is R-Type, which obey
with output from the network defined by
and the second is Z-Type, which obey
with output from the network defined by
Here,
Under default settings,
Note that taking
or
Taking
A model object is created by:
model = RateModel(recurrent, readin=None, readout=None, f='tanh', eta=1, rho_recurrent=1, rho_input=1, rho_output=1,
bias_recurrent=True, bias_output=True, Network_Type='R')
We next describe the different arguments to RateModel
recurrent determines the recurrent weight matrix. There are two options:
If recurrent is an integer then this integer is interpreted as the number of hidden dimensions. A weight matrix, rho_recurrent/sqrt(N)
If recurrent is a square matrix (2-dim PyTorch tensor) then this is interpreted as the recurrent weight matrix and the number of hidden dimensions is inferred from its size.
readin determines the input/readin weight matrix. There are three options:
If readin is None (default) then there is no input layer. Effectively,
If readin is an integer then this integer is interpreted as the number of input dimensions, rho_input/sqrt(Nx)
If readin is a matrix (2-dim PyTorch tensor) then this is interpreted as the input weight matrix, (N,Nx)
readout determines the output/readout matrix. Specifically, the output from a forward pass is defined by
for R-Type networks and
for Z-Type networks. Here
There are three options:
If readout is None (default) then there is no readout applied. Effectively,
If readout is an integer then this integer is interpreted as the number of output dimensions, rho_output/sqrt(N)
If readout is a matrix (2-dim PyTorch tensor) then this is interpreted as the readout weight matrix, (Nout,N)
f is the activation function or f-I curve used for the network. There are two options:
If f is one of the following strings: 'relu', 'tanh', or 'id' then the corresponding activation function is used (where 'id' is the identity)
If f is a function then this function is used.
These parameters determine the standard deviation of the weight matrices when weight matrices are generated during initialization (see description of recurrent, readin, and readout above).
If readin is a matrix or is None, then rho_input. Similarly for recurrent and output.
These determine whether a bias is used in the recurrent layer and the output layer. Note that bias in the recurrent layer is equivalent to bias in the input layer, so there is no bias_input.
If bias_recurrent=False (default) then no bias is used (bias_output.
If Network_Type=='R' then an R-Type network is used. If Network_Type=='Z' then a Z-type network is used.
A forward pass is completed by calling:
y = model(x, Nt = None, initial_state = 'zero', return_time_series = True, store_hidden_history = True)
x is the input to the network. It should be a 3-dimensional or 2-dimensional tensor.
If x is 3-dimensional then it is interpreted as the input (batch_size, Nt, Nx) where Nt is the number of time steps.
If x is 2-dimensional then it is interpreted as a time-constant input (batch_size, Nx) and the number of timesteps must be passed in as Nt
An integer representing the number of time steps if x is 2-dimensional. If x is 3-dimensional, then Nt should be None.
Determines the initial hidden state. There are three options:
If initial_state=='zero' then the hidden state is initialized to zeros.
If initial_state=='keep' then the network will try to use the curent value of the hidden state as the initial condition. If the current value is None or has inconsistent batch size, then zero will be used instead.
If initial_state is a 2-dimensional array then it this array is used as the initial state. Therefore, it must have shape (batch_size, N)
A flag determining whether a time series is returned from the forward pass or just the last state of the network is returned.
If return_time_series==True then y has shape (batch_size, Nt, Nout)
If return_time_series==False then y has shape (batch_size, Nout) because only the final state is returned.
store_hidden_history
A flag to determine whether the keep track of the time history of the hidden state.
If store_hidden_history==True then the time series of hidden states will be stored in self.hidden_state_history
If store_hidden_history==False then self.hidden_state_history will be set to None and will not be tracked.
The output from the network.
If return_time_series==True, this will be a 3-dimensional array with shape (batch_size, Nt, Nout) representing
the time series of the output.
If return_time_series==False, this will be a 2-dimensional array with shape (batch_size, Nout) representing
the final state of the output.
self.recurrent_layer, self.input_layer, and self.output_layer are nn.Linear layers that implement the linear
terms represented by
If readin==False then self.input_layer=nn.Identity().
If readout==False then self.output_layer=nn.Identity().
Hidden states
self.hidden_state stores the current value of the hidden state, at the final time point of the last forward pass.
self.hidden_state_history stores the time series of hidden states from the previous forward pass when store_hidden_history==True.
When store_hidden_history==False, the history is not stored and hidden_state_history=None
self.N_recurrent, self.N_input, self.N_output store the widths of each layer.
If readin==False then self.N_input==self.N_recurrent
If readout==False then self.N_output==self.N_recurrent
Most of the inputs to __init__ (for example, eta) are stored as member variables under the same name (for example, self.eta) just as they are passed in.
The only exceptions are:
self.readin and self.readout are stored only as flags (True or False).
self.f is stored as a function, even when it is passed in as a string.
The input recurrent is not stored.
Very similar to RateModel except Conv2d layers replace Linear layers.
model = Conv2dRateModel(rec_channels, rec_kernel_size, in_channels=None, readin_kernel_size=None, out_channels=None, readout_kernel_size=None, f='tanh', eta=1,
bias_recurrent=False, bias_output=False, readin_padding='same', readin_stride=1, readout_padding='same', readout_stride=1, Network_Type='R'):
y = model(self, x, Nt = None, initial_state = 'auto', return_time_series = True, store_hidden_history = True)
The shape of x can either be:
(batch_size, Nt, in_channels, in_width, in_height)
or
(batch_size, in_channels, in_width, in_height)
In the second case, x interpreted as time-constant (same input at each time) and Nt must be passed into the forward pass.
In the first case, Nt is inferred from x and should not be passed.
If return_time_series==True then the output has shape
(batch_size, Nt, out_channels, out_width, out_height)
If return_time_series==False then the output has shape
(batch_size, out_channels, out_width, out_height)
The model is identical to RateModel except that the nn.Linear layers are replaced by
nn.Conv2d layers. Mathematically, RateModel (see description of RateModel above)
are replaced by multi-channel 2d convolutional operators.
Here, we explain only parts of Conv2dRateModel that are different from RateModel. Please see the documentation for
RateModel for everything else.
A model object is created by:
model = Con2dRateModel(rec_channels, rec_kernel_size, in_channels=None, readin_kernel_size=None, out_channels=None, readout_kernel_size=None, f='tanh', eta=1,
bias_recurrent=False, bias_output=False, readin_padding='same', readin_stride=1, readout_padding='same', readout_stride=1, Network_Type='R')
The number of channels in the recurrent layer.
The kernel size of the recurrent convolutional layer.
The number of channels in the input. If in_channels=None then there is no input layer (effectively, rec_channels.
The size of the kernel for the readin convolution. You should use readin_kernel_size=None when in_channels=None
The number of channels in the output. If out_channels=None then there is no output layer (effectively, rec_channels.
The size of the kernel for the readout convolution. You should use readout_kernel_size=None when out_channels=None
The padding and stride for the readin and readout layers.
Note that the recurrent layer always has padding='same' and stride=1 so there is no
option to set the stride and padding for the recurrent layer.
All other inputs to __init__ are the same as for NeuroRNN
A forward pass is completed by calling:
y = model(x, Nt = None, initial_state = 'auto', return_time_series = True, store_hidden_history = True)
All inputs to the forward pass are perfectly analogous to those in NeuroRNN objects.
All member variables are analogous to those in NeuroRNN objects except for
self.in_channels, self.rec_channels, and self.out_channels which are self-explanatory (number of channels in each layer)
model = SpikingModel(recurrent, tausyn, readin=None, NeuronModel='EIF', NeuronParams={})
SimResults = model(x0, dt, x=None, T=None, initial_V='random', initial_Y='zero', dtRecord = None, Tburn = 0, VIRecord = [])
See EISpikingModelExample.ipynb for an example of a randomly connected excitatory-inhibitory network of EIF model neurons.
Simulates a recurrent network of integrate-and-fire neurons. Membrane potentials of the
with the added condition that
where
Synaptic outputs are defined by
which is just a vector of low-pass filtered spike trains. Total inputs are defined by
where self.recurrent, self.input_layer