-
Notifications
You must be signed in to change notification settings - Fork 75
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
Fix Connections segment pruning #601
Conversation
new segmentThreshold arg introduced, as connectedThreshold_ has different, incompatible meaning.
in adaptSegment
moved from being static
to private method
instead of lower_bound, caused err in Debug. Seems faster
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please review when you find time.
Question to consider: make an equivalent of stimulusThreshold_
in Connections? (currently passed as arg to the methods)
@@ -433,7 +429,8 @@ void Connections::adaptSegment(const Segment segment, | |||
const SDR &inputs, | |||
const Permanence increment, | |||
const Permanence decrement, | |||
const bool pruneZeroSynapses) | |||
const bool pruneZeroSynapses, | |||
const UInt segmentThreshold) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these 2 options could be merged together, but I like to keep them separate: first gates synapse pruning, the latter segment pruning.
src/htm/algorithms/Connections.cpp
Outdated
if(!pruneZeroSynapses) { | ||
NTA_ASSERT(segmentThreshold == 0) << "Setting segmentThreshold only makes sense when pruneZeroSynapses is allowed."; | ||
} | ||
if(pruneZeroSynapses and synapses.size() < segmentThreshold) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was the bug I was about, connectedThreshold_ was used incorrectly.
a change I introduced, probably the |
and only keep count of segments that were destroyed. Pros: - simpler code - fixed deterministm for (serialized connections & using pruning) - maybe better cache utilization, as vector<SegmentData> is continuous now Cons: - larger memory utilization, as segments_ and segmentData_ vectors now have holes in it
lower_bound failed on "does not parition", find() is fine, and simpler code.
as a premature optimization, keep only as counter of num destroyed. - cleaner code - faster - but slightly wastes memory (but we only destroy 0.3% synapses on MNIST)
as pruning seems not platform-independent.
the debug code was called after! the segment has been already removed
by setting segmentThreshold param. This should be ON, but there are some issues with platform independent reproducible results
3e4ae13
to
46e4ca5
Compare
for platform-independent build?
add arguments with defaults
after synapse is destroyed by destroySynapse(syn), its data were still accessible! Which could lead to false operations. Add check to dataForSynapse() that fails if synapse has been removed. Detail: synapseExists_() is available, improve its performance to be widely usable (by dataForSynapse()). Tests for create/destroySynapse
if the new permanence is higher that the permanence of the old (same) synapse, update to the higher value.
that only test the functionality of "main" Connections, which are already tested. Done due to changes in the main tests, which would have to be rewritten here as well. The "advanced" tests should cover all the special functionality not part of the main.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkeeney please have a look (even if only at the high-level changes)?
This is an old PR that accumulated many fixes that were quite hard to debug, all tests pass so I assume it's safe to merge.
@@ -163,6 +165,66 @@ def testAdaptShouldDecrementSynapses(self): | |||
self.assertEqual(presynamptic_cells, presynaptic_input_set, "Missing synapses") | |||
|
|||
|
|||
|
|||
def testCreateSynapse(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added new tests
@@ -38,207 +38,6 @@ def _getPresynapticCells(self, connections, segment, threshold): | |||
return set([connections.presynapticCellForSynapse(synapse) for synapse in connections.synapsesForSegment(segment) | |||
if connections.permanenceForSynapse(synapse) >= threshold]) | |||
|
|||
def testAdaptShouldNotRemoveSegments(self): | |||
""" | |||
Test that connections are generated on predefined segments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI @fcr I'm removing the "duplicit" tests for advanced_connections.py
for methods that are not changed by this Connection implementation, and are already tested in the "main" tests. As I made changes to the tests and would have to port them here.
//1. just keep the older (former default) | ||
//2. throw an error (ideally, user should not createSynapse() but rather updateSynapsePermanence()) | ||
//3. create a duplicit new synapse -- NO. This is the only choice that is incorrect! HTM works on binary synapses, duplicates would break that. | ||
//4. update to the max of the permanences (default) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
documented and changed the default strategy for createSynapse()
if such synapse already exists. All four alternatives discussed above,
- former default: leave as is;
- now: update permanence if the new perm would be larger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...impact is not really seen in our unit-tests as none trigger this "duplicit synapse created" branch.
|
||
} else { | ||
//quick method. Relies on hack in destroySynapse() where we set synapseData.permanence == -1 | ||
return synapses_[synapse].permanence != -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code above is quite slow, but is needed and needed in a heavily used dataForSynapse()
, therefore I created this "fast" variant/hack.
segmentData.synapses.cend(), | ||
synapse, | ||
[&](const Synapse a, const Synapse b) -> bool { return dataForSynapse(a).id < dataForSynapse(b).id;} | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just whitespace formatting
@@ -374,7 +378,8 @@ class Connections : public Serializable | |||
* | |||
* @retval Synapse data. | |||
*/ | |||
const SynapseData &dataForSynapse(const Synapse synapse) const { | |||
inline const SynapseData& dataForSynapse(const Synapse synapse) const { | |||
NTA_CHECK(synapseExists_(synapse, true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needed check for dataForSynapse/Segment()
. Otherwise, the data could be accessed even after removing the synapse/segment with destroy*()
.
//the following member must not be serialized (so is set to =0). | ||
//That is because of we serialize only active segments & synapses, | ||
//excluding the "destroyed", so those fields start empty. | ||
//! ar(CEREAL_NVP(destroyedSegments_)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed bug in serialization of connections.
@@ -206,7 +206,7 @@ void TemporalMemory::activatePredictedColumn_( | |||
do { | |||
if (learn) { | |||
connections_.adaptSegment(*activeSegment, prevActiveCells, | |||
permanenceIncrement_, permanenceDecrement_, true); | |||
permanenceIncrement_, permanenceDecrement_, true, minThreshold_); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FIX: TM now honors the "max segments per cell" logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found no problems while doing a high level review.
I have not been following the changes that you are making to the Connections algorithm but I assume they are all good.
Thank you, David!
i've just double checked and the changes to Conn are all fixes, so we're good. |
dataForSynapse()
,dataForSegment()
+ tests