I am trying to implement unsupervised learning in a model using memristors as synapses by adapting the BCM learning rule. I have already successfully done the same for supervised learning by adapting PES.
I think I have the needed understanding of the theory down but I wonder if my model may not be working because of some strange effects I can see in Nengo; to be precise, my post
population seems to be firing before it should.
My ensembles are set up as such:
nengo.Connection( inp, pre )
nengo.Connection( pre.neurons, learn[ :pre_nrn ], synapse=0.005 )
nengo.Connection( post.neurons, learn[ pre_nrn: ], synapse=0.005 )
nengo.Connection( learn, post.neurons, synapse=None )
with learn
being a Node()
object implementing my weight calculation. As you can see, I have a synapse of tau=0.005
on both the connection from pre
and post
(even though it’s None
in the Nengo implementation, I have to add a delay here to avoid cycles) to learn
.
The full code can be found here and here.
learn
implements the following learning rule:
def mBCM( self, t, x ):
input_activities = x[ :self.input_size ]
output_activities = x[ self.input_size: ]
theta = self.theta_filter.filt( output_activities )
alpha = self.learning_rate
# function \phi( a, \theta ) that is the moving threshold
update = alpha * output_activities * (output_activities - theta)
if self.logging:
self.history.append( np.sign( update ) )
self.save_state()
# squash spikes to False (0) or True (100/1000 ...) or everything is always adjusted
spiked_pre = np.array( np.rint( input_activities ), dtype=bool )
spiked_post = np.array( np.rint( output_activities ), dtype=bool )
# we only need to update the weights for the neurons that spiked so we filter
if spiked_pre.any() and spiked_post.any():
for j in np.nditer( np.where( spiked_post ) ):
for i in np.nditer( np.where( spiked_pre ) ):
self.weights[ j, i ] = self.memristors[ j, i ].pulse( update[ j ],
value="conductance",
method="same"
)
# calculate the output at this timestep
return np.dot( self.weights, input_activities )
If I’m understanding things correctly, this should mean that I will see the first neural activities from pre
in mBCM()
at t=0.006
(i.e., after 5 timesteps) and the first ones from post
around t=0.0012
.
What I’m seeing instead is the following; at t=0.005
everything is as expected:
but at
t=0.006
I instantly see that post
is also firing:The result of applying my rule is that the post
neurons are always firing, could it be connected to this phenomenon I’m seeing?
Should I be specifying some initial weights like in the tutorial?