Readings: Serial Working Memory; Associative Memory
The neural integrator is an example of an attractor network
The neural integrator can be thought of as a line attractor as in a)
Note: The hill and ball analogy doesn't work anymore.
This is an oscillator. Technically a nonlinear one, since it should be stable (the Simple Harmonic Oscillator is linear). Let's build a stable oscillator.
%pylab inline
import nengo
from nengo.utils.ensemble import response_curves
from nengo.dists import Uniform
model = nengo.Network('Oscillator')
freq = .25
scale = 1.1
N=300
with model:
stim = nengo.Node(lambda t: [.5,.5] if .1<t<.12 else [0,0])
inhib = nengo.Node(lambda t: [-1]*N if .8<t<.82 else [0]*N)
osc = nengo.Ensemble(N, dimensions=2, intercepts=Uniform(.3,1))
def feedback(x):
return scale*x[0]+freq*x[1], -freq*x[0]+scale*x[1]
nengo.Connection(osc, osc, function=feedback)
nengo.Connection(inhib, osc.neurons)
nengo.Connection(stim, osc)
osc_p = nengo.Probe(osc, synapse=.01)
WARNING: pylab import has clobbered these variables: ['seed'] `%matplotlib` prevents importing * from pylab and numpy
from nengo_gui.ipython import IPythonViz
IPythonViz(model, "configs/nonlinear_oscillator.py.cfg")
sim = nengo.Simulator(model)
sim.run(1)
x, A = response_curves(osc, sim)
figure(figsize=(4,2))
plot(x, A)
xlabel('x')
ylabel('firing rate (Hz)')
figure(figsize=(12,4))
subplot(1,2,1)
plot(sim.trange(), sim.data[osc_p]);
xlabel('Time (s)')
ylabel('State value')
subplot(1,2,2)
plot(sim.data[osc_p][:,0],sim.data[osc_p][:,1])
xlabel('$x_0$')
ylabel('$x_1$');
Building finished in 0:00:01. Simulating finished in 0:00:01.
%pylab inline
#A 1D integrator with a few hundred neurons works great
import nengo
from nengo.utils.functions import piecewise
model = nengo.Network(label='1D Line Attractor', seed=5)
N = 300
tau = 0.01
with model:
stim = nengo.Node(piecewise({.3:[1], .5:[0] }))
neurons = nengo.Ensemble(N, dimensions=1)
nengo.Connection(stim, neurons, transform=tau, synapse=tau)
nengo.Connection(neurons, neurons, synapse=tau)
stim_p = nengo.Probe(stim)
neurons_p = nengo.Probe(neurons, synapse=.01)
sim = nengo.Simulator(model)
sim.run(4)
t=sim.trange()
plot(t, sim.data[stim_p], label = "stim")
plot(t, sim.data[neurons_p], label = "position")
legend(loc="best");
Populating the interactive namespace from numpy and matplotlib Building finished in 0:00:01.
WARNING: pylab import has clobbered these variables: ['piecewise'] `%matplotlib` prevents importing * from pylab and numpy
Simulating finished in 0:00:01.
#Need lots of neurons to get reasonable performance in higher D
import nengo
from nengo.utils.functions import piecewise
model = nengo.Network(label='2D Plane Attractor', seed=4)
N = 2000 #100
tau = 0.01
with model:
stim = nengo.Node(piecewise({.3:[1, -1], .5:[0, 0] }))
neurons = nengo.Ensemble(N, dimensions=2)
nengo.Connection(stim, neurons, transform=tau, synapse=tau)
nengo.Connection(neurons, neurons, synapse=tau)
stim_p = nengo.Probe(stim)
neurons_p = nengo.Probe(neurons, synapse=.01)
sim = nengo.Simulator(model)
sim.run(4)
t=sim.trange()
plot(t, sim.data[stim_p], label = "stim")
plot(t, sim.data[neurons_p], label = "position")
legend(loc="best");
Building finished in 0:00:01. Simulating finished in 0:00:01.
#Note that the representation saturates at the radius
with model:
stim.output = piecewise({.2:[1, -1], 1.2:[0, 0] })
sim = nengo.Simulator(model)
sim.run(4)
t=sim.trange()
plot(t, sim.data[stim_p], label = "stim")
plot(t, sim.data[neurons_p], label = "position");
Building finished in 0:00:01. Simulating finished in 0:00:02.
model = nengo.Network(label='Ensemble Array', seed=123)
N = 300
tau = 0.01
with model:
stim = nengo.Node(piecewise({.3:[1, -1], .5:[0, 0] }))
neurons = nengo.networks.EnsembleArray(N, n_ensembles=2)
nengo.Connection(stim, neurons.input, transform=tau, synapse=tau)
nengo.Connection(neurons.output, neurons.input, synapse=tau)
stim_p = nengo.Probe(stim)
neurons_p = nengo.Probe(neurons.output, synapse=.01)
sim = nengo.Simulator(model)
sim.run(4)
t=sim.trange()
plot(t, sim.data[stim_p], label = "stim")
plot(t, sim.data[neurons_p], label = "position");
Building finished in 0:00:01. Simulating finished in 0:00:02.
from nengo_gui.ipython import IPythonViz
IPythonViz(model, "configs/ensemble_array.py.cfg")
#Note that the representation saturates at the radius
with model:
stim.output = piecewise({.2:[1, -1], 1.2:[0, 0] })
sim = nengo.Simulator(model)
sim.run(4)
t=sim.trange()
plot(t, sim.data[stim_p], label = "stim")
plot(t, sim.data[neurons_p], label = "position");
import nengo
from nengo import spa
def color_input(t):
if t < 0.15:
return 'BLUE'
elif 1.0 < t < 1.15:
return 'GREEN'
elif 1.7 < t < 1.85:
return 'RED'
else:
return '0'
model = spa.SPA(label="HighD Working Memory", seed=5)
dimensions = 32
with model:
model.color_in = spa.Buffer(dimensions=dimensions)
model.mem = spa.Memory(dimensions=dimensions, subdimensions=4,
synapse=0.1, neurons_per_dimension=50)
# Connect the buffers
cortical_actions = spa.Actions(
'mem = color_in'
)
model.cortical = spa.Cortical(cortical_actions)
model.inp = spa.Input(color_in=color_input)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.03)
color_in = nengo.Probe(model.color_in.state.output)
mem = nengo.Probe(model.mem.state.output)
sim = nengo.Simulator(model)
sim.run(3.)
plt.figure(figsize=(10, 10))
vocab = model.get_default_vocab(dimensions)
plt.subplot(2, 1, 1)
plt.plot(sim.trange(), model.similarity(sim.data, color_in))
plt.legend(model.get_output_vocab('color_in').keys, fontsize='x-small')
plt.ylabel("color")
plt.subplot(2, 1, 2)
plt.plot(sim.trange(), model.similarity(sim.data, mem))
plt.legend(fontsize='x-small')
plt.legend(model.get_output_vocab('color_in').keys, fontsize='x-small')
plt.ylabel("memory")
plt.xlabel("time [s]");
//anaconda/lib/python2.7/site-packages/matplotlib/axes/_axes.py:519: UserWarning: No labelled objects found. Use label='...' kwarg on individual plots. warnings.warn("No labelled objects found. "
from nengo_gui.ipython import IPythonViz
IPythonViz(model, "configs/simple_spa_wm.py.cfg")
# model.all_ensembles has all the neurons
model.all_ensembles[2].n_neurons
200
You can see interference effects here
If you recalled things from this memory, you'd probably have a 'recency effect'
This is seen in human memory, but so is primacy
import nengo
from nengo import spa
def color_input(t):
if t < 0.15:
return 'BLUE'
elif 1.0 < t < 1.15:
return 'GREEN'
elif 1.7 < t < 1.85:
return 'RED'
else:
return '0'
model = spa.SPA(label="HighD Working Memory", seed=5)
dimensions = 32
with model:
model.color_in = spa.Buffer(dimensions=dimensions)
model.mem = spa.Memory(dimensions=dimensions, subdimensions=4,
synapse=0.1, neurons_per_dimension=50,
tau=-.2)
# Connect the buffers
cortical_actions = spa.Actions(
'mem = color_in'
)
model.cortical = spa.Cortical(cortical_actions)
model.inp = spa.Input(color_in=color_input)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.03)
color_in = nengo.Probe(model.color_in.state.output)
mem = nengo.Probe(model.mem.state.output)
from nengo_gui.ipython import IPythonViz
IPythonViz(model, "configs/simple_spa_wm_primacy.py.cfg")
sim = nengo.Simulator(model)
sim.run(3.)
plt.figure(figsize=(10, 10))
vocab = model.get_default_vocab(dimensions)
plt.subplot(2, 1, 1)
plt.plot(sim.trange(), model.similarity(sim.data, color_in))
plt.legend(model.get_output_vocab('color_in').keys, fontsize='x-small')
plt.ylabel("color")
plt.subplot(2, 1, 2)
plt.plot(sim.trange(), model.similarity(sim.data, mem))
plt.legend(fontsize='x-small')
plt.legend(model.get_output_vocab('color_in').keys, fontsize='x-small')
plt.ylabel("memory")
plt.xlabel("time [s]");
An ordered list is a very simple kind of structure
To represent structures, we can use what we learned last time: vector binding
In this case, we can do something like:
$Pos_0\circledast Item_0 + Pos_1\circledast Item_1 + ...$
Arbitrary list lengths
This model can actually do lots more as well
A slight variation does free recall
You'll notice that it relies on a 'clean-up'
Often worried about how to learn these
How to compute these kinds of mappings with the NEF/SPA?
Can do it in one layer with a feedforward approach
We've seen several techniques that will make this easy
import nengo
from nengo import spa
seed=1
np.random.seed(seed)
model = spa.SPA("Associative Memory", seed=seed)
D = 32
vocab = spa.Vocabulary(D)
vocab.parse('BLUE+GREEN+RED')
noise_RED = vocab.parse("RED").v + .2*np.random.randn(D)
noise_RED = noise_RED/np.linalg.norm(noise_RED)
noise_GREEN = vocab.parse("GREEN").v + .2*np.random.randn(D)
noise_GREEN = noise_GREEN/np.linalg.norm(noise_GREEN)
def memory_input(t):
if t < 0.2:
return vocab.parse("BLUE").v
elif .2 < t < .5:
return noise_RED
elif .5 < t < .8:
return vocab.parse("RED").v
elif .8 < t < 1:
return noise_GREEN
else:
return vocab.parse("0").v
with model:
stim = nengo.Node(output=memory_input, label='input')
model.am = spa.AssociativeMemory(vocab)
nengo.Connection(stim, model.am.input)
in_p = nengo.Probe(stim)
out_p = nengo.Probe(model.am.output, synapse=0.03)
from nengo_gui.ipython import IPythonViz
IPythonViz(model, "configs/simple_cleanup.py.cfg")
sim = nengo.Simulator(model)
sim.run(1)
t = sim.trange()
figure(figsize=(10,10))
plt.subplot(2, 1, 1)
plt.plot(t, spa.similarity(sim.data[in_p], vocab))
plt.ylabel("Input")
plt.ylim(top=1.1)
plt.legend(vocab.keys, loc='best')
plt.subplot(2, 1, 2)
plt.plot(t, nengo.spa.similarity(sim.data[out_p], vocab))
plt.ylabel("Output")
plt.legend(vocab.keys, loc='best');
%pylab inline
import nengo
from nengo import spa
def color_input(t):
if t < 0.25:
return 'RED'
elif t < 0.5:
return 'BLUE'
else:
return '0'
def shape_input(t):
if t < 0.25:
return 'CIRCLE'
elif t < 0.5:
return 'SQUARE'
else:
return '0'
def cue_input(t):
if t < 0.5:
return '0'
sequence = ['0', 'CIRCLE', 'RED', '0', 'SQUARE', 'BLUE']
idx = int(((t - 0.5) // (1. / len(sequence))) % len(sequence))
return sequence[idx]
seed=1
model = spa.SPA(label="Simple question answering", seed=seed)
dimensions = 32
vocab = model.get_default_vocab(dimensions)
vocab.parse('BLUE+RED+CIRCLE+SQUARE')
with model:
model.color_in = spa.Buffer(dimensions=dimensions)
model.shape_in = spa.Buffer(dimensions=dimensions)
model.conv = spa.Memory(dimensions=dimensions, subdimensions=4, synapse=0.4)
model.cue = spa.Buffer(dimensions=dimensions)
model.out = spa.Buffer(dimensions=dimensions)
model.am = spa.AssociativeMemory(vocab, threshold=0.1)
model.inp = spa.Input(color_in=color_input, shape_in=shape_input, cue=cue_input)
# Connect the buffers
cortical_actions = spa.Actions(
'conv = color_in * shape_in',
'out = conv * ~cue',
'am = out'
)
model.cortical = spa.Cortical(cortical_actions)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.03)
color_in = nengo.Probe(model.color_in.state.output)
shape_in = nengo.Probe(model.shape_in.state.output)
cue = nengo.Probe(model.cue.state.output)
conv = nengo.Probe(model.conv.state.output)
out = nengo.Probe(model.out.state.output)
clean = nengo.Probe(model.am.output)
WARNING: pylab import has clobbered these variables: ['seed', 'piecewise'] `%matplotlib` prevents importing * from pylab and numpy
from nengo_gui.ipython import IPythonViz
IPythonViz(model, "configs/binding_with_memory.py.cfg")
sim = nengo.Simulator(model)
sim.run(3.)
plt.figure(figsize=(12, 10))
plt.subplot(6, 1, 1)
plt.plot(sim.trange(), model.similarity(sim.data, color_in))
plt.legend(model.get_output_vocab('color_in').keys, fontsize='x-small')
plt.ylabel("color")
plt.subplot(6, 1, 2)
plt.plot(sim.trange(), model.similarity(sim.data, shape_in))
plt.legend(model.get_output_vocab('shape_in').keys, fontsize='x-small')
plt.ylabel("shape")
plt.subplot(6, 1, 3)
plt.plot(sim.trange(), model.similarity(sim.data, cue))
plt.legend(model.get_output_vocab('cue').keys, fontsize='x-small')
plt.ylabel("cue")
plt.subplot(6, 1, 4)
for pointer in ['RED * CIRCLE', 'BLUE * SQUARE']:
plt.plot(sim.trange(), vocab.parse(pointer).dot(sim.data[conv].T), label=pointer)
plt.legend(fontsize='x-small')
plt.ylabel("convolved")
plt.subplot(6, 1, 5)
plt.plot(sim.trange(), spa.similarity(sim.data[out], vocab))
plt.legend(model.get_output_vocab('out').keys, fontsize='x-small')
plt.ylabel("Output")
plt.xlabel("time [s]");
plt.subplot(6, 1, 6)
plt.plot(sim.trange(), spa.similarity(sim.data[clean], vocab))
plt.legend(model.get_output_vocab('am').keys, fontsize='x-small')
plt.ylabel("Cleaned Up Output")
plt.xlabel("time [s]");