How to disable noise in Ensemble?

Hi there!

I want to create a deterministic LIF neuron ensemble.

Model = nengo.Network(label="Network")
with Model:
    L0 = nengo.Ensemble( 2, dimensions=2,noise=None, seed=None)
    probe = nengo.Probe(L0, synapse=None)

sim = nengo.Simulator(Model,0.001)
sim.run(1)
x = sim.data[probe]

However, the output x is noisy. How can I get rid of the noise? Thank you!

Deterministic and noiseless are two different things.

If you want the model to run the exact same way every time you run the model (in other words, running the model deterministically), you need to set the seed.

If you want the approximate the output of X in a noiseless manner with neurons, that’s impossible. The noise of a representation. If you want to approximate the output of X, in a noiseless manner, bypassing neurons, you can use the nengo.Direct() neuron to simulate the population in direct mode.

Hi @myyim, welcome to the forum!

A Nengo model can look noisy in lots of ways that are still deterministic. In your model, for example, the only random elements are the neuron properties, which could be made deterministic by setting an integer seed on the ensemble.

Your model looks noisy because a LIF neuron is a spiking neuron, which means that it approximates values by spiking. That means that when you look at the output of the ensemble as a whole, it’s always going to be, to a certain extent, “noisy” looking because you’re trying to figure out what’s happening at one moment in time which might be when a cell is spiking, or might be right after it’s spiked. We smooth things out with filtering so that we have some estimate of what’s going on, but it’s always a noisy estimate. The synapse argument defines those filters; probing a spiking ensemble with synapse=None is always going to be very very noisy.

The above explains why any value is never exactly that value. However, in your model, you also have no input, so the value should just be 0, which can be easily represented by the cells not spiking. Well, there is an additional detail, which is that we also inject a background bias current to mimic the background firing rate of biological cells receiving no synaptic input. Again, this is completely deterministic, but gives the appearance of noise despite there being no input.

Fortunately, if you don’t want to deal with the oddities of spiking neurons, you can simply use a rate-based neuron model. Try this:

Model = nengo.Network(label="Network")
with Model:
    L0 = nengo.Ensemble( 2, dimensions=2,noise=None, seed=None, neuron_type=nengo.LIFRate())
    probe = nengo.Probe(L0, synapse=None)

sim = nengo.Simulator(Model,0.001)
sim.run(1)
x = sim.data[probe]

You will now see that the output, x, is always the same. It is still non-zero, because we still inject a bias current, and since there are only 2 neurons in your ensemble, the bias makes the representation significantly far from [0, 0]. You can increase the number of neurons to keep the representation closer to [0, 0]:

Model = nengo.Network(label="Network")
with Model:
    L0 = nengo.Ensemble(100, dimensions=2,noise=None, seed=None, neuron_type=nengo.LIFRate())
    probe = nengo.Probe(L0, synapse=None)

sim = nengo.Simulator(Model,0.001)
sim.run(1)
x = sim.data[probe]

You can also play around with the neuron properties such that they do not receive bias current, or only negative bias current. See the tuning curves example for details on that.

Also, as @Seanny123 mentioned, you can also use the nengo.Direct() neuron type to bypass any kind of neural encoding / decoding, and instead use Nengo as a general dynamical systems simulator:

Model = nengo.Network(label="Network")
with Model:
   L0 = nengo.Ensemble( 2, dimensions=2,noise=None, seed=None, neuron_type=nengo.Direct())
   probe = nengo.Probe(L0, synapse=None)

sim = nengo.Simulator(Model,0.001)
sim.run(1)
x = sim.data[probe]

In this case, you will see that x is always exactly [0, 0].

Thank you very much for your reply, @tbekolay and @Seanny123

I want to train a spiking network of LIF neurons to learn some simple tasks, and like to start with the noiseless case with some deterministic input (some sinusoidal drive for example). That’s why I would like to get rid of the noise. From the documentation, each LIF neuron in nengo.Ensemble has an associated encoder. It seems that the LIF model in nengo.Ensemble is not the same as the canonical LIF neuron model derived from the RC circuit.

I can see why it would appear that way, but I can assure you that Nengo’s LIF neuron is the canonical LIF neuron. You can look at the code for the LIF step function for full details.

The reason it looks different is because nengo.Ensemble does a lot more than just simulate neurons. An ensemble is designed to represent a value with those neurons such that you can compute dynamic functions with that representation. If the ensemble contained 20 identical LIF neurons, it would have no hope of representing a value because all the neuron would do the same thing, meaning that you have no more information than if you had 1 LIF neuron. In order to get the kind of diversity that you need to represent and transform information, we need to make each neuron slightly different. As you know, the LIF neuron model is well known, so we can’t change the neuron model. And ideally, we would do something that works for any neuron model, not just LIFs. So what we do is:

  1. Inject some bias current at all times (this shifts the tuning curve left or right)
  2. Scale the input current by some scaling factor (this makes the slope of the tuning curve steeper or shallow)

Just by doing these two things, we are able to represent information reliably. Note that in none of the above do I mention anything about noise. We do not inject noise unless you deliberately ask Nengo for noise.

If you want to do things with LIF neurons that are not necessarily about representing information, and you instead want to treat the LIF neurons simply as neurons, you can set the ensemble to:

  1. Inject 0 bias current
  2. Scale the input by 1.

Doing both of these things will result in what you consider canonical LIF neurons. Here’s how to do it with your code:

import nengo

Model = nengo.Network(label="Network")
with Model:
    L0 = nengo.Ensemble(2, dimensions=2,
                        encoders=nengo.dists.Choice([[1, 1]]),
                        gain=nengo.dists.Choice([2.]), 
                        bias=nengo.dists.Choice([0.]))
    probe = nengo.Probe(L0, synapse=None)

sim = nengo.Simulator(Model,0.001)
sim.run(1)
x = sim.data[probe]
print(x)

This will print out all zeros, as expected. If you look at the tuning curves, they’re identical:

import matplotlib.pyplot as plt
plt.plot(*nengo.utils.ensemble.response_curves(L0, sim))
plt.ylabel("Firing rate (Hz)")
plt.xlabel("Input current")

tmp

What kinds of simple tasks are you looking to train the LIF neurons to do?

1 Like

Thank you @tbekolay. So actually the noise is due to the non-zero bias. What is the difference between noise and bias in nengo.Ensemble?

I want to build a network composing of identical LIF neurons and train the network by updating the connection weights, so the function of the network will be defined by the weights.

For bias, the same constant scalar current is injected in the cell at every timepoint. For noise, the current injected in the cell at every timepoint is randomly sampled from a given distribution (usually Gaussian, but can be any distribution).

Cool; we have some learning examples using an error-minimization rule called PES. I’m not sure if these examples would work if the neurons are identical… my intuition is that it will not, but it would be interesting to try nonetheless.