Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursive least-squares learning in Nengo #133

Merged
merged 6 commits into from May 29, 2018
Merged

Recursive least-squares learning in Nengo #133

merged 6 commits into from May 29, 2018

Conversation

arvoelke
Copy link
Owner

@arvoelke arvoelke commented Jan 17, 2018

@psipeter @celiasmith

This shows how to implement recursive least-squares (RLS) as a learning rule in Nengo. The equations come from the Sussillo and Abbott (2009) FORCE paper. See committed notebook for details.

It appears to work extremely well, even with spiking neurons:

simulation

error

gamma

This is learning a communication channel. The error signal is disabled after 1 period of the sine wave, and the network gives the correct answer thereafter. The gamma matrix it finds (see above) is essentially identical to the default computed offline by Nengo. This makes sense given that they are both doing least-squares optimization, but it is still remarkable given we're using spiking neurons, only providing one oscillation, and doing this online.

You can think of this as an alternative to using PES, with the following important differences:

  • PES is greedy (stochastic gradient descent) and overwrites its previous decoders using only local information, while RLS maintains global information to produce an L2-optimal estimate (based on previous knowledge of neural correlations)
  • PES does not need to keep around extra information, but RLS needs to keep n^2 extra variables in memory (in particular, a running estimate of inv(gamma), where gamma = A.T.dot(A) is the exact same matrix computed by Nengo's L2 solvers).

In other words, this should consistently outperform PES, but is not biologically plausible, and requires extra compute / memory. If the online aspect is not required, then just stick to Nengo's default (offline) L2-optimization. I think this will be most useful when doing FORCE-style learning.

@arvoelke
Copy link
Owner Author

Note that, from a Nengo user's perspective, this is as simple as substituting nengo.PES(...) with nengolib.RLS(...). They both take the same parameters, use the same sign on the error signal, and have the same overall effect of minimizing that error signal over time!

@celiasmith
Copy link
Collaborator

celiasmith commented Jan 18, 2018 via email

@tbekolay
Copy link

Wow, this is super cool! Nice work @arvoelke 👍

" def step_simbcm():\n",
" # Note: dt is not used in learning rule\n",
" rP = r.T.dot(P)\n",
" P[...] -= P.dot(np.outer(r, rP)) / (1 + rP.dot(r))\n",
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: P[...] -= np.outer(P.dot(r), rP) / (1 + rP.dot(r)) should be more efficient since it avoids a matrix-multiply.

" # Note: dt is not used in learning rule\n",
" rP = r.T.dot(P)\n",
" P[...] -= P.dot(np.outer(r, rP)) / (1 + rP.dot(r))\n",
" delta[...] = - error * P.dot(r)\n",
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be delta[...] = -np.outer(error, P.dot(r)) to properly handle multi-dimensional error signals.

@arvoelke
Copy link
Owner Author

arvoelke commented May 28, 2018

TODO:

@arvoelke
Copy link
Owner Author

arvoelke commented May 29, 2018

The documentation now includes a side-by-side comparison of PES versus RLS on a scalar spiking communication channel. You won't be able to see this until release, unless you build the docs yourself, and so I've copied it below:

rls_versus_pes

This PR also contains a notebook example that shows how to construct both spiking FORCE and full-FORCE networks in Nengo. Again, the rendered version will be visible upon next release (nengolib>0.4.2).

Repository owner deleted a comment from codecov-io May 29, 2018
Repository owner deleted a comment from codecov-io May 29, 2018
Repository owner deleted a comment from codecov-io May 29, 2018
@codecov-io
Copy link

codecov-io commented May 29, 2018

Codecov Report

Merging #133 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master   #133   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          29     29           
  Lines        1373   1374    +1     
  Branches      157    157           
=====================================
+ Hits         1373   1374    +1
Impacted Files Coverage Δ
nengolib/temporal.py 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f9059ee...57f18b6. Read the comment docs.

@arvoelke arvoelke merged commit 57f18b6 into master May 29, 2018
@arvoelke arvoelke deleted the rls branch May 29, 2018 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants