2. Quickstart

This page walks you through the initial steps to becoming productive with Norse. We will cover how to

  • Work with neuron state

  • Work with Norse without time

  • Work with Norse with recurrence

  • Work with Norse with time

If you are entirely unfamiliar with spiking neural networks (SNNs) we recommend you skim our page that introduces the topic: Introduction to spikes.

2.1. Running off-the-shelf code

If you just want to get started we recommend our collection of Jupyter Notebook Tutorials. They can be run online on Google Colab.

Additionally, we provide a set of tasks that you can run right after installing Norse. One of the most common experiments is the MNIST classification task. Norse achieve on-par performance with modern non-spiking networks:

python -m norse.task.mnist

Please refer to Running Tasks for more tasks and detailed information on how to run them.

2.2. Building neural networks with state

If you would like to build your own models with Norse you need to know that neurons contain state. In practice, that meant that neurons in Norse outputs two things: a spike tensor and the neuron state. Norse initialises all the necessary state for you in the beginning, but you need to carry the state onwards. If you do not, the state will always be zero, the neuron will never spike and your neurons will be forever dead!

import torch
import norse

cell = norse.torch.LIFCell()
data = torch.ones(1)
spikes, state = cell(data)

The next time you call the cell, you need to pass in that state. Otherwise you will get the exact same output

spikes, state = cell(data, state)

Note: This is similar to PyTorch’s RNN module if you are looking for inspiration.

2.3. Using Norse neurons with time

Similar to PyTorch’s Sequential, Norse’s neuron models can be chained in a network. Unfortunately, this does not work with neurons for the same reason that it does not work with PyTorch’s own RNNs: state. Instead, Norse offers a SequentialState module that ties stateful modules together:

import torch
import norse

model = SequentialState(
    torch.nn.Linear(10, 5),
    norse.torch.LIFCell(),
    torch.nn.Linear(5, 1)
)

data = torch.ones(8, 10) # (batch, input)
out, state = model(data) # (8, 1) output shape

2.4. Using recurrence in Norse

All neuron modules have Cell and RecurrentCell classes. The Cell class applied above simply works as a feed-forward activation of the neuron, while the RecurrentCell also contains linear and recurrent weights (we are weighing both the input and the recurrent spikes). For that reason, we need to inform the module what shape it needs to take, since we have to initialize weights to match the desired input/output shape.

The RecurrentCell classes work out of the box and can be plugged directly into the above code. Note that we have the same input/output shape, but that it could easily be different:

import torch
import norse

model = SequentialState(
    torch.nn.Linear(10, 5),
    norse.torch.LIFRecurrentCell(5, 5),
    torch.nn.Linear(5, 1)
)

data = torch.ones(8, 10) # (batch, input)
out, state = model(data) # (8, 1) output shape

You can do the same for other neuron types like the LSNN, LIFAdEx, etc.

2.5. Using Norse in time

The above ``XCell``s follow the abstraction from PyTorch where the cells are “simple” activation functions that is applied once. However, neurons exist in time and will need to be given at least a few timesteps of input before something interesting happens (like a spike).

The network above (the one without time) works perfectly well with time, and you can easily wrap it with a for loop. However, it’s also possible to run each module individually in time.

In Norse, we model this time aspect by removing the Cell suffix from the model. So the a LIFCell in time will simply be called LIF. Similarly, a LIFRecurrentCell in time will simply be called LIFRecurrent.

The regular Torch modules also need to run in time. For that, we added a module to lift PyTorch modules into the time domain (that is, simply run them once for every timestep).

Taken together, we get the following:

import torch
import norse

model = SequentialState(
    norse.Lift(torch.nn.Linear(10, 5)),
    norse.LSNNRecurrent(5, 5),
    norse.Lift(torch.nn.Linear(5, 1))
)
data = torch.ones(100, 8, 10) # (time, batch, input)
out, state = model(data)

This covers the most basic way to apply Norse. More information can be found Introduction to spikes, Working with Norse and Learning with spikes.