<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<br>
Hi Terry,<br>
<br>
thank you very much for all your explanations, and please apologize
that it took so long to respond. Your answer already helps a lot to
understand Nengo a bit better, but please, let me ask some more
questions, because I still don't know much about Nengo.<br>
<br>
First I would like to ask two questions concerning your example: Why
did you choose a 2 dimensional ensemble instead of a 1 dimensional
one for just one angle?<br>
And secondly, you write in your example after the node "angle_to_pt"
was connected to the ensemble "optic_tectum": "You now have neurons
with tuning curves in different directions." If I look at the tuning
curves, they look like a 2 dimensional version of the curves in the
Nengo 1.4 documentation, chapter 1.1.4 and not like the ones in
figure 6 in the Fischer and Pena paper. Therefore, why do I have
tuning curves in different directions now?<br>
<br>
Please, let my say a few more words about the problem I want to
simulate. The work of Fischer and Pena from 2011 will only be one
part of the simulation. I want to simulate the auditory system
starting with two input nodes for the left and right ear with a
sinus function including a phase difference as well as different
amplitudes of the two input signals. The input nodes for the two
ears could look like<br>
# input of right and left ear<br>
input_left = nengo.Node(lambda t: np.sin(frequency*2*math.pi*t))<br>
input_right = nengo.Node(lambda t:
0.8*np.sin(frequency*2*math.pi*t-phase_factor))<br>
<br>
The simulation should process these input signals and should end in
the optic tectum as described for example by Masakazu Konishi in
Scientific American in 1993. To do this, I want to use a simple
model. First, I have to compute the interaural time difference of
this input signals. For this purpose I want to use the delay class
of the Nengo examples. I want to connect the input nodes to an
array of delay nodes with different delays and then the delay nodes
to an array of ensembles. If the shift due to the delay and the
phase factor add up to half the wavelength, the ensemble will only
have a small activity, if they add up to a multiple of the
wavelength, the ensemble will have a large activity. In this way I
can get the phase or the interaural time difference.<br>
<br>
Here an simple example with 2 delay nodes and one ensemble<br>
# delay nodes (currently I am using 40 delay nodes)<br>
delay_1 = Delay(1, timesteps=1) <br>
delay_2 = Delay(1, timesteps=3) <br>
delaynode_l1 = nengo.Node(output=delay_1.step) <br>
delaynode_r1 = nengo.Node(output=delay_2.step) <br>
<br>
# connect input to delay nodes<br>
nengo.Connection(input_left, delaynode_l1)<br>
nengo.Connection(input_right, delaynode_r1) <br>
<br>
# connect the output of the delay nodes to an ensembles
(currently I also have 40 ensembles)<br>
ln = nengo.Ensemble(500, dimensions=1,radius=3)<br>
nengo.Connection(delaynode_l1, ln)<br>
nengo.Connection(delaynode_r1, ln)<br>
<br>
In the end I will have the activities of 40 ensembles. The ensemble
with the largest activity will give an approximation for the phase
and therefor also for the horizontal angle the sound comes from, but
it would be better to compute the average over all ensembles of the
normalized activity times the angles each ensemble represents. <br>
Therefore I think I can't use the ensemble of your example and I
have to compute the average as in your weighted average approach
example. <br>
I hope you know what I mean, because my English is not good and
therefore it is difficult for me to explain, what I want to do. <br>
I don't know if the way I try to compute the interaural time
difference and the angle is a good way to do it. Do you have any
idea how improve this or how to avoid the division in this case?<br>
<br>
<tt><span style="font-size:10pt"></span></tt>One more problem is
that I also want is to compute the interaural level difference for
the vertical angle of the direction the sound comes from. To get
this difference I have to divide the amplitude values. Here I also
have the problem of a division. Could it be that the problem with a
division is the fact that in principle the denominator could be zero
and that then a linear decoding is not working very well?<br>
<br>
In Nengo one can restrict the values of an ensemble with the radius
parameter. Is it possible to restrict the values to a positive
interval to avoid the zero and would this give better results?<br>
<br>
Thank you very much for your help and your patience with me.<br>
<br>
Best regards,<br>
<br>
Peer<br>
<br>
Am 11.02.2016 um 23:47 schrieb Terry Stewart:<br>
<blockquote
cite="mid:CAL0ZbQFAHTqTbnPV8kdtosagN3F2oydcr-e0pMFVs1nn24Z3DQ@mail.gmail.com"
type="cite">
<div dir="ltr">Hello Peer,
<div><br>
</div>
<div>Thank you for the context! That helps a lot. And, it
turns out that with a couple slightly counter-intuitive
tweaks, this sort of model is quite nicely suited for Nengo.</div>
<div><br>
</div>
<div>The main thing we first need is to make tuning curves that
look like Figure 6a of (Fischer and Pena, 2011). These
neurons have preferred directions that range between -100 and
+100 degrees. Preferred directions are called "encoders" in
Nengo/NEF. However, because Nengo allows you to generalize
the idea of preferred directions up to hundreds of dimensions,
we need to be explicit about exactly how we want to represent
an angle.</div>
<div><br>
</div>
<div>The easiest way is to think of the group of neurons (the
optic tectum) as representing a two-dimensional space, and
each neuron has some preferred direction in that space. By
default, Nengo will randomly distribute the neuron's encoders
around that 2-dimensional space, although we can control that
if we want to (e.g. we may want to distribute neurons more
densely in the middle of the space, for example). </div>
<div><br>
</div>
<div>So, the code for making the optic_tectum is:</div>
<div><br>
</div>
<div> optic_tectum = nengo.Ensemble(n_neurons=500,
dimensions=2)<br>
</div>
<div><br>
</div>
<div>Now, however, we want to provide input to that set of
neurons. We want that input to be an angle, and we want that
angle to stimulate the neurons in a consistent way (i.e. when
the input is an angle of 0, the neurons with preferred
direction vectors near 0 should be stimulated). We can do
this with one Node for the input angle, and one Node to do the
conversion into the represented space:</div>
<div><br>
</div>
<div> stim_angle = nengo.Node([0])<br>
</div>
<div>
<div> </div>
<div> import numpy as np</div>
<div> def convert_angle(t, x):</div>
<div> return np.sin(x[0]), np.cos(x[0])</div>
<div> angle_to_pt = nengo.Node(convert_angle, size_in=1)</div>
<div> nengo.Connection(stim_angle, angle_to_pt,
synapse=None)</div>
</div>
<div><br>
</div>
<div>Now we connect that input into the optic_tectum</div>
<div><br>
</div>
<div> nengo.Connection(angle_to_pt, optic_tectum)<br>
</div>
<div><br>
</div>
<div>You now have neurons with tuning curves in different
directions. If you plot the spikes from the optic_tectum as
you change the input stim_angle, you should see this
behaviour. Also, it's important to note that these neurons in
Nengo have much more variety in their tuning curves (heights
and widths) than the perfectly regular tuning curves done in
Figure 6a. We tend to keep a large degree of variability in
our neurons. You can control the width with the intercepts
parameter, if you're interested in doing that.</div>
<div><br>
</div>
<div>So all of that above was just to get the stimulus and
neuron parameters set up right to give the sorts of tuning
curves we want in the optic tectum. Now we come to the part
where we want to decode out from these tuning curves what the
currently represented angle is. In (Fischer and Pena, 2011),
they do this: "The population vector is obtained by averaging
the preferred direction vectors of neurons in the population,
weighted by the firing rates of the neurons". This is one way
of decoding neural activity, but it's not the only way.</div>
<div><br>
</div>
<div>In Nengo, when you ask it to compute a function, it finds
the optimal way of weighting the outputs of neural activity to
approximate that function. Furthermore, it does this with a
fixed set of linear weights -- i.e. it does not require that
division step in the weighted average. So let's try it the
standard Nengo way, and then compare that to the weighted
average approach.</div>
<div><br>
</div>
<div>To do it the standard Nengo way, we define a function that
goes from the represented 2-D space and decodes out the angle:</div>
<div><br>
</div>
<div>
<div> decoded_angle = nengo.Node(None, size_in=1) # a
place to store the result</div>
<div> </div>
<div> # a function to map from 2-d space to an angle</div>
<div> def decode_angle(x):</div>
<div> return np.arctan2(x[0], x[1])</div>
<div> </div>
<div> # make the connection</div>
<div> nengo.Connection(optic_tectum, decoded_angle,
function=decode_angle)<br>
</div>
</div>
<div><br>
</div>
<div>This tells Nengo to find the optimal set of connection
weights from the neurons to decode out the angle. That is, it
finds the vector d such that sum(a_i * d_i) best approximates
the input angle.</div>
<div><br>
</div>
<div>If you try running this, it works okay, but we we can make
it do better. In particular, right now Nengo is trying to
approximate the function across the whole 2-D space, but we
only need it to be good at a particular range of points. If
we tell Nengo to just focus on those points, it gets much
better:</div>
<div><br>
</div>
<div>
<div> decoded_angle = nengo.Node(None, size_in=1)</div>
<div> </div>
<div> def decode_angle(x):</div>
<div> return np.arctan2(x[0], x[1])</div>
<div><br>
</div>
<div> # define the set of points </div>
<div>
<div> def make_pt():</div>
<div> theta = np.random.uniform(-2, 2)</div>
<div> return [np.sin(theta), np.cos(theta)]</div>
</div>
<div> pts = [make_pt() for i in range(1000)]<br>
</div>
<div> </div>
<div> nengo.Connection(optic_tectum, decoded_angle,
function=decode_angle, eval_points=pts)</div>
</div>
<div><br>
</div>
<div>If you run this and plot the decoded_angle you'll see it
very closely follows the stim_angle.</div>
<div><br>
</div>
<div>However, it'd be good to also do the weighted average
approach, so we can compare the two. Doing that requires us
to to know the angles for all the neurons, and do a weighted
average of those angles (weighted by activity). To do that,
we explicitly define the angles for the neurons, and then do
the math in a Node:</div>
<div><br>
</div>
<div> N = 500</div>
<div><br>
</div>
<div>
<div> def make_pt():</div>
<div> theta = np.random.uniform(-2, 2)</div>
<div> return [np.sin(theta), np.cos(theta)]</div>
<div> </div>
<div> # generate random preferred direction vectors</div>
<div> encoders = np.array([make_pt() for i in range(N)])</div>
<div> </div>
</div>
<div>
<div> optic_tectum = nengo.Ensemble(n_neurons=N,
dimensions=2, encoders=encoders)</div>
</div>
<div><br>
</div>
<div> # compute the angle for each preferred direction vector</div>
<div>
<div> angles = np.arctan2(encoders[:,0], encoders[:,1])</div>
<div><br>
</div>
<div> # compute the weighted average</div>
<div> def weighted_average(t, a):</div>
<div> total = np.sum(a)</div>
<div> if total == 0:</div>
<div> return 0</div>
<div> return np.sum(a*angles) / total</div>
<div> </div>
<div> computed = nengo.Node(weighted_average, size_in=N)</div>
<div> nengo.Connection(optic_tectum.neurons, computed,
synapse=None)</div>
</div>
<div><br>
</div>
<div>Now we can plot "computed" (the approach used in (Fischer
and Pena, 2011)) and compare it to "decoded_angle" (the
default approach used in Nengo). In this case, the Nengo
approach is more accurate, and it doesn't require any
division! </div>
<div><br>
</div>
<div>Here's a script that should let you directly compare the
two approaches:</div>
<div><br>
</div>
<div>-------------------</div>
<div>
<div>import nengo</div>
<div>import numpy as np</div>
<div><br>
</div>
<div>N = 500 # the number of neurons</div>
<div><br>
</div>
<div>model = nengo.Network()</div>
<div>with model:</div>
<div> stim_angle = nengo.Node([0]) # the input angle</div>
<div> </div>
<div> # convert the angle into a 2-D space</div>
<div> def convert_angle(t, x):</div>
<div> return np.sin(x[0]), np.cos(x[0])</div>
<div> angle_to_pt = nengo.Node(convert_angle, size_in=1)</div>
<div> nengo.Connection(stim_angle, angle_to_pt,
synapse=None)</div>
<div> </div>
<div> # make a point in 2-D space that is at random angle</div>
<div> def make_pt():</div>
<div> theta = np.random.uniform(-2, 2)</div>
<div> return [np.sin(theta), np.cos(theta)]</div>
<div> </div>
<div> # the preferred direction vectors for the neurons</div>
<div> encoders = np.array([make_pt() for i in range(N)])</div>
<div> </div>
<div> # create the group of neurons</div>
<div> optic_tectum = nengo.Ensemble(n_neurons=N,
dimensions=2, encoders=encoders)</div>
<div> </div>
<div> nengo.Connection(angle_to_pt, optic_tectum)</div>
<div> </div>
<div> </div>
<div> ### Standard Nengo/NEF Approach</div>
<div> </div>
<div> # decode out the angle in the optimal Nengo/NEF
approach</div>
<div> decoded_angle = nengo.Node(None, size_in=1)</div>
<div> </div>
<div> # function that the neural connections should
approximate</div>
<div> def decode_angle(x):</div>
<div> return np.arctan2(x[0], x[1])</div>
<div> </div>
<div> # define the set of values over which the approximate
should be good</div>
<div> pts = [make_pt() for i in range(1000)]</div>
<div> </div>
<div> nengo.Connection(optic_tectum, decoded_angle,
function=decode_angle, eval_points=pts)</div>
<div> </div>
<div><br>
</div>
<div> ### Weighted average approach</div>
<div><br>
</div>
<div> # determine the angles for each neuron</div>
<div> angles = np.arctan2(encoders[:,0], encoders[:,1])</div>
<div> # compute the weighted sum</div>
<div> def weighted_average(t, a):</div>
<div> total = np.sum(a)</div>
<div> if total == 0:</div>
<div> return 0</div>
<div> return np.sum(a*angles) / total</div>
<div> computed = nengo.Node(weighted_average, size_in=N)</div>
<div> </div>
<div> nengo.Connection(optic_tectum.neurons, computed,
synapse=None)</div>
<div> </div>
<div> ---------</div>
</div>
<div><br>
</div>
<div>So, the main differences between the Nengo/NEF approach
rather than the weighted average approach are:</div>
<div> - The Nengo/NEF approach gives a more accurate result</div>
<div> - The Nengo/NEF approach handles variability in the tuning
curves</div>
<div> - The Nengo/NEF approach does not require division (which
is complicated to biologically justify)</div>
<div><br>
</div>
<div>In any case, while it's certainly possible to do the
weighted average approach (using that "computed" Node defined
above) in Nengo, we tend not to. But it'd be interesting to
do a more rigorous direct comparison in this case.</div>
<div><br>
</div>
<div>Notice also that, right now, the neurons are being
optimized to just figure out what the input angle is. If you
also want it to take into account some sort of bayesian prior,
then all you have to do is put that into the decode_angle
function. This lets you implement a wide variety of possible
priors.</div>
<div><br>
</div>
<div>Does that help? What I've presented here is definitely a
very different way of thinking about things than is taken in
(Fischer and Pena, 2011). But hopefully I've shown a) how to
implement their approach in Nengo, and b) that there are other
ways of decoding information that are a little bit more
flexible. </div>
<div><br>
</div>
<div>Let me know if that helps, and thank you again for the
question!</div>
<div><br>
</div>
<div>Terry</div>
<div><br>
</div>
</div>
</blockquote>
</body>
</html>