Learning a Linear Transformation

Hi community,

I’m quite new to Nengo and simply trying to comprehend all the examples and also wanted to watch the Summer school videos to become comfortable with the API.

However, as I was experimenting with the examples, I wondered if it was possible to learn a linear transformation in the form of a matrix multiplication (Y = AX) with the PES learning rule.

I thought maybe it could look something like this:

    # -- input and pre popluation
    inp = nengo.Node([1, 2, 3, 4])
    pre = nengo.Ensemble(1,dimensions=4)
    nengo.Connection(inp, pre)
    
    matrix = np.matrix([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])

    # -- post populations
    post_pes = nengo.Ensemble(1, dimensions=4)

    # -- reference population, containing the actual product
    product = nengo.Ensemble(1, dimensions=4)
    nengo.Connection(inp, product, transform=matrix, synapse=None)

    # -- error populations
    error_pes = nengo.Ensemble(1, dimensions=4)
    nengo.Connection(post_pes, error_pes)
    nengo.Connection(product, error_pes, transform=-1)

Hi @SebVol, and welcome to the Nengo forums! :smiley:

Yes, absolutely! The PES learning rule is quite capable of learning linear (and non-linear) transformations, as evidenced by the examples here. As a bit of a teaser, here’s the output of a network I’ve made that learns a randomly shuffled identity matrix (i.e., it just transposes the order of the vector element around). This output is recorded after the network has been trained for 200s, and while this output is being probed, the learning signal has been inhibited, to demonstrate that the network has generalized the learned connection (similar to what is done in this example).

From the plots, we see that the network has done a pretty good job at learning the shuffled identity matrix. :smiley:

You are generally on the right track, although, there are a few pointers I have for you.

  • First, note that in Nengo, by default (if you don’t change the radius value) neural ensembles are optimized to represent values between a certain range. It is typically within the range of a unit hypersphere (i.e., given an vector input, the vector magnitude of said input should be at most 1). While it may be possible for the network to learn with inputs that violate this “constraint” (optimized parameter), for simplicity, you should try to keep the inputs to the ensembles with this in mind.
  • As the dimensionality of the ensembles is increased, the number of neurons you use in the ensemble should increase as well. In some networks, we use a scalar multiplier (e.g., 50 * dim), but for the network I put together, I’m using an exponential multiplier (50 * dim ** 1.5). This is mostly from experience, since I know that more neurons will help the network generalize. It does come at the cost of slowing down the simulation, though.
  • The network you have proposed should work, with a one minor change (apart from the changes I mentioned above). You seem to be missing the connection between pre and post_pes, which is the one where the learning will actually take place on. Apart from that, the network you proposed should work. :smiley:
1 Like

Hi @xchoo,

thanks for your reply.

Unfortunately, I was not at my computer last week and could only work on it today, so sorry for my late reply.

I changed my code to the more practical case, where my input is (13,38) matrix and my output a (13,10) matrix. Because I wanted to use the visualization of the nengo simulation I was running my code directly with nengo and not in a jupyter notebook. However, I was running into some difficulties were my model simply did not learn anything and my output was constant. I can’t figure out why:

with model:
    inp_ens = nengo.Ensemble(n_neurons=500,dimensions=38,radius=10) #define radius for range of input values
    x_pos = 0
    y_pos = 0
    
    def inp_loop(t):
        global x_pos
        temp = x_pos
        #print(temp)
        if x_pos<12:
            x_pos+=1
        else:
            x_pos=0
        return c_mat[temp]
        
    def out_loop(t):
        global y_pos
        temp1 = y_pos
        if y_pos<12:
            y_pos+=1
        else:
            y_pos=0
        return s_mat[temp1]
        
    
    stim = nengo.Node(inp_loop)
    target = nengo.Node(out_loop)
    
    nengo.Connection(stim,inp_ens)
    
    
    output = nengo.Node(None,size_in = 10)
    
    def my_func(x):
        return 0
    
    learn_con = nengo.Connection(inp_ens.neurons,output, transform=np.zeros(shape=(10,500)),
                                learning_rule_type=nengo.PES(learning_rate=0.0001))
                                
    
    error = nengo.Node(None,size_in=10)
    
    nengo.Connection(output,error)
    nengo.Connection(target,error,transform=-1)
    nengo.Connection(error,learn_con.learning_rule)

I used the loops in the code so that it can run endlessly in the simulation.

And I have a follow up questions to the code you provided.
How did you define the time dimension?

Thanks again! :slight_smile: