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

Learning unmodulated #642

Merged
merged 10 commits into from
Jun 19, 2015
Merged

Learning unmodulated #642

merged 10 commits into from
Jun 19, 2015

Conversation

hunse
Copy link
Collaborator

@hunse hunse commented Feb 4, 2015

This makes it so that we connect directly to LearningRules instead of making modulatory connections, as discussed in #632. It fixes that problem.

The second commit switches the sign of the PES error, so that it is actually an error (i.e. the actual value minus the target value). This means that the PES learning rule moves in the opposite direction of the error, as one would expect (e.g., if the output is too large, make it smaller). This makes a lot more sense to me, and hopefully to everyone else as well. Now seemed like a good time to do it, because the changes from the first commit will require that everyone re-write their learning code anyway.

EDIT: this also addresses #366.

@drasmuss
Copy link
Member

I keep going back and forth on the error sign switch. It makes for a pretty sharp discontinuity with previous models. Also, the PES rule is essentially just a renaming of the delta rule, and the delta rule is usually written the way the PES rule is now (i.e. target - actual).

But you're right that in that case, referring to the feedback signal as an "error" is kind of odd. What we probably should have done is named it "feedback" instead of "error" or something. I don't know whether it's better to change our nomenclature to match the implementation, or change the implementation to match the nomenclature.

@celiasmith
Copy link
Contributor

Every rule is essentially just a renaming of the delta rule ;)

Daniel Rasmussen wrote:

I keep going back and forth on the error sign switch. It makes for a
pretty sharp discontinuity with previous models. Also, the PES rule is
essentially just a renaming of the delta rule, and the delta rule is
usually written the way the PES rule is now (i.e. |target - actual|).

But you're right that in that case, referring to the feedback signal
as an "error" is kind of odd. What we probably should have done is
named it "feedback" instead of "error" or something. I don't know
whether it's better to change our nomenclature to match the
implementation, or change the implementation to match the nomenclature.


Reply to this email directly or view it on GitHub
#642 (comment).

@drasmuss
Copy link
Member

In that it's all just gradient descent, yeah, although some are more similar than others 😉. And in general with gradient descent the convention is to use the error signal as in this PR, which is the main argument for the swap I think. It's probably more likely that people used to things like backprop would be confused by the swapped error in the current implementation, as @hunse suggests.

@hunse
Copy link
Collaborator Author

hunse commented May 19, 2015

Do people have comments on this, or is it ready to go?

@tbekolay
Copy link
Member

I'll review this shortly. I'm considering the error signal sign flip uncontested!

@tbekolay tbekolay self-assigned this May 22, 2015
@@ -83,10 +83,6 @@ def validate_ops(sets, ups, incs):
for node in sets:
assert len(sets[node]) == 1, (node, sets[node])

# -- assert that only one op updates any particular view
for node in ups:
assert len(ups[node]) == 1, (node, ups[node])
Copy link
Member

Choose a reason for hiding this comment

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

Hm, is this OK? Why did we have this restriction in the first place?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I couldn't think of a good reason to have it. The original reason may have just been because we always used updates for filters, in which case the signal in the filter being updated should only ever be updated once.

When you get into multiple updates per step, there is a possibility that the order of updates matters. I was going to say that that's not a problem if the updates are just increments, but that's not true. Any learning rule that depends on the magnitude of the connection weights will act differently if it's computed before or after another rule modifying the same weights. Maybe this is something we need to consider? We should at least be able to make this deterministic within a model, so I don't think it's too big of an issue.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I think this is something we need to consider... I'm sure the difference (in this case) is pretty small, but it could be large in some cases. I'm not sure how we'd order ops that both update the same signal... So yeah, I'd be keen to keep this assertion in and figure out another way to do these updates. I think the PreserveValue op from before was pretty good.

@tbekolay
Copy link
Member

This was a bit old, so I gave it a rebase. Looking good!

Only two things I think before merge. First is the all-important changelog entry. I also had a thought about the modulator parameter... rather than completely remove all mentions of it here, how about for 2.1.x, if someone tries to set modulator to True, we raise a ValueError explaining the new API for learning rules, or at least pointing them to this PR (or a doc page on the learning API or something)? Definitely we will have some people upgrade Nengo without reading the changelog, so I think it'll be kinder to tell them about this change when we know it's happening (setting modulatory=True).

@hunse
Copy link
Collaborator Author

hunse commented May 24, 2015

Adding that error sounds like a good idea.

@arvoelke
Copy link
Contributor

Updated the changelog and made it so that you get

Parameter is no longer supported; please refer to pull-request #642: modulatory connections removed

This introduced a new AbandonedParam parameter. My only concern is that this should not encourage people to abandon parameters liberally and destroy backwards compatibility.

@arvoelke
Copy link
Contributor

Should #643 be implemented here too, in order to contain these rather significant changes to PES to a single PR? (Note: The work is done, I just need to know where to attach)

@neworderofjamie
Copy link

Hello Nengoers, I'm trying to restore my SpiNNaker version of PES to it's former glory and the learning rate seems to have been scaled somehow. In the old learn_communications_channel example it was 1 and now it's 1e-6 - How has the calculation of the PES update changed?

THANKYOU!

@drasmuss
Copy link
Member

It was probably this change #527, where things were changed to scale with dt. I know we had also talked about changing the learning rate to scale with the number of neurons in the pre-synaptic population (#643), but I'm not sure if that actually happened yet?

@neworderofjamie
Copy link

The implementation I was working on at last years summer school multiplied the learning rate by dt (in seconds) - Is that the scaling in question or something more cunning involved?

@drasmuss
Copy link
Member

It's a little bit more complicated, in that what's actually happening is that the output activities are being scaled by 1/dt. So the a(x) in the PES equations are all larger than they were before, so the learning rate needs to be scaled down accordingly (@hunse made the change so he can double check that I'm not saying lies here).

@arvoelke
Copy link
Contributor

@neworderofjamie That sounds right, although the correct way to do it may depend on whether your spikes are scaled by dt (you can see how this was done in #527 as Dan said). Note that the above commit (dc22f16) also divides the effective learning rate by n_neurons in the pre population, and this PR also flips the sign of the error signal. So the final rule is

where kappa is the learning rate (invariant under dt), n is number of neurons in pre, E is the error signal, and a is the filtered pre-synaptic activity.

This PR just needs a final review now and then it should be good to go.

@hunse
Copy link
Collaborator Author

hunse commented May 29, 2015

Can we change AbandonedParam to DeprecatedParam?

Also, did we want to add scaling by number of pre neurons in this PR? Might as well change it now, and revise the learning rates again. EDIT: oh sorry, just saw the commit for that.

- ``LearningRuleType`` objects no longer take modulatory connections.
Instead, a connection should be made directly from the error population
to the learning rule. Also, the error's sign has been flipped for
the PES learning rule to be ``actual - target``.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

"learning rule" -> "learning rule error"

@arvoelke
Copy link
Contributor

Sorry about throwing more onto the pile, but realized that the pre-activity should be filtered. It was just a one line change in the builder, and a new parameter for PES. Note that the parameter order for PES is different from BCM, otherwise all the examples (and possible models that people have already built), need to change their learning rate to a key-word argument.

Addressed @hunse comments. We agree'd that ObsoleteParam is best, since deprecated implies it still works.

@tbekolay
Copy link
Member

This makes sense to me; we don't use any neural info from the post population in decoder PES, so why not. I wonder if we should revisit the modifies attribute in the learning rule types? Right now it's either 'Neurons' 'Ensemble' or both. Maybe it should be 'transform' and 'decoders', since that's what the learning rule actually modifies.

@arvoelke
Copy link
Contributor

+1 to revisiting modifies. Voja modifies encoders, and so my solution in that PR was to have a post_modifies, but makes more sense to say what signals are being modified.

@tbekolay
Copy link
Member

OK, 'Neurons' -> 'transform' and 'Ensemble' -> 'decoders' in 44768d4. I think neither of the last two commits need changelog entries? Only people implementing learning rules would care...

@hunse
Copy link
Collaborator Author

hunse commented Jun 15, 2015

Added a fixup for neuron to ensemble learning connections. This should all be cleaned up in the monolithic-transform branch (it'll need rebasing, though).

@hunse
Copy link
Collaborator Author

hunse commented Jun 17, 2015

Added a test for neuron to ensemble PES learning. I think this should be good to go, @tbekolay.

@tbekolay
Copy link
Member

Great; I'll look this over this afternoon!

@tbekolay
Copy link
Member

The new tests LGTM! @arvoelke mentioned that he wanted to give this a look over too. I'll probably merge tomorrow afternoon unless someone forcibly stops me.

hunse and others added 4 commits June 19, 2015 10:09
Connect the error directly into a learning rule (as discussed in #632),
instead of making a modulatory connection. This is cleaner and clearer.

Learning rules are now built by their parent connections, and since
a connection to a learning rule must added to a network after
the connection containing the learning rule, the learning rule
will always be built when the builder for the error connection into
said learning rule is called.

This fixes #632 (slicing `post` in learned connections). The test
`test_learning_rules.py:test_pes_decoders_multidimensional` has been
updated to test this feature.
The PES rule previously moved in the direction of what it called the
"error" signal, meaning that the error signal had to be the target
value for the learning minus the actual value. This is the opposite
of what people typically refer to as error. This commit changes the
sign of this error so that it is actually an error, and the PES rule
moves in the opposite direction of the error.
This operator is a bit hacky, but after some experimenting, this is
essentially the cleanest way to ensure that all learning operations
are applied correctly.

Increments and reads happen *after* a signal is set. By setting the
transform with PreserveValue, we allow increments to be done by
learning rules. The only other alternative is to update it, which
happens *last*. It's not obvious why one learning rule would be
applied before another, so by using PreserveValue, we do the delta
computations on the previous timestep, and then increment the transform
on the current timestep's 'increments' phase. The transform isn't
updated, but that's okay because it's set.
BCM and Oja calculate their delta, then apply it to the transform.
PES skipped this step and just incremented the transform (or decoders)
directly. By refactoring PES to calculate the delta and then apply it,
we get two big benefits:

1. The order in which learning rules are applied no longer matters;
   the delta calculations are all done before the transform changes,
   since delta calcs are updates and the transform changes are incs.
2. The code for defining a delta signal and applying it to the
   transform can be refactored into the `LearningRule` builder,
   rather than being duplicated in each `LearningRuleType` builder.
   This should make learning rules easier to implement in the future.

As another minor benefit, it's now possible to probe the 'delta' of
each learning rule. This makes it easy to see exactly what each
each learning rule is doing, which is especially useful when you're
using multiple learning rules at the same time.

Also made a few other minor enhancements: better docstrings for
`SimOja` and `SimBCM`, and a note about ordering of connections in
the `Network` builder.
arvoelke and others added 6 commits June 19, 2015 10:19
Previously, we used the type of the pre or post object as a clue to
what the learning rule modifies. This makes it more explicit by saying
what signal the learning rule actually modifies. This will make it
easier to implement learning rules that modify other quantities,
like for example `post.encoders`.
All PES tests now run through a single helper function, making
the code more concise.

I also added a test for neuron to ensemble PES learning, as well
as a test for PES learning when using a weight solver.
@tbekolay
Copy link
Member

OK, this branch is now rebased to master, and the history's been squashed and whatnot. I think this is ready to merge; will do so in an hour or so unless anyone stops me!

@tbekolay tbekolay merged commit ed8a566 into master Jun 19, 2015
@tbekolay tbekolay deleted the learning-unmodulate branch June 19, 2015 16:24
@mundya
Copy link
Contributor

mundya commented Jun 19, 2015

Awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants