-
Notifications
You must be signed in to change notification settings - Fork 82
TM Connections integration #537
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
Changes from 18 commits
103e8fc
b6a103a
cc476a9
21c315c
cdcf5df
dd45567
34401b4
8607710
f6f029e
14871c9
a25040c
9648ae0
e20c267
24b2c36
abb8f96
cbf0d38
dac487a
47eb418
cccc3e5
8a1e871
e8fb520
37e7a13
023ed97
629db2f
d7ba1a9
60a384e
0188242
c95c177
4672daf
95b8b6d
b5498f3
6e07f06
184ad2c
36ed0ca
be38786
d20c9e8
2f16fb3
5fe3697
425f12c
d3186ed
fc38d89
5b368d8
065b467
24983cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,7 +31,9 @@ using std::string; | |
| using std::vector; | ||
| using namespace htm; | ||
|
|
||
| Connections::Connections(CellIdx numCells, Permanence connectedThreshold, bool timeseries) { | ||
| Connections::Connections(const CellIdx numCells, | ||
| const Permanence connectedThreshold, | ||
| const bool timeseries) { | ||
| initialize(numCells, connectedThreshold, timeseries); | ||
| } | ||
|
|
||
|
|
@@ -75,7 +77,23 @@ void Connections::unsubscribe(UInt32 token) { | |
| eventHandlers_.erase(token); | ||
| } | ||
|
|
||
| Segment Connections::createSegment(CellIdx cell) { | ||
| Segment Connections::createSegment(const CellIdx cell, | ||
| const SegmentIdx maxSegmentsPerCell, | ||
| const UInt32 iteration) { //TODO move iteration to Connections.iteration_ ? | ||
|
|
||
| //limit number of segmets per cell. If exceeded, remove the least recently used ones. | ||
| NTA_ASSERT(maxSegmentsPerCell > 0); | ||
| while (maxSegmentsPerCell > 1 && numSegments(cell) >= maxSegmentsPerCell) { | ||
breznak marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const auto& destroyCandidates = segmentsForCell(cell); | ||
| const auto compareSegmentsByLRU = [&](const Segment a, const Segment b) { | ||
| return (dataForSegment(a).lastUsed < dataForSegment(b).lastUsed); }; | ||
breznak marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const auto leastRecentlyUsedSegment = std::min_element(destroyCandidates.cbegin(), | ||
| destroyCandidates.cend(), compareSegmentsByLRU); | ||
|
|
||
| destroySegment(*leastRecentlyUsedSegment); | ||
| } | ||
|
|
||
| //proceed to create a new segment | ||
| Segment segment; | ||
| if (!destroyedSegments_.empty() ) { //reuse old, destroyed segs | ||
| segment = destroyedSegments_.back(); | ||
|
|
@@ -84,17 +102,14 @@ Segment Connections::createSegment(CellIdx cell) { | |
| NTA_CHECK(segments_.size() < std::numeric_limits<Segment>::max()) << "Add segment failed: Range of Segment (data-type) insufficinet size." | ||
| << (size_t)segments_.size() << " < " << (size_t)std::numeric_limits<Segment>::max(); | ||
| segment = static_cast<Segment>(segments_.size()); | ||
| segments_.push_back(SegmentData()); | ||
| const SegmentData& segmentData = SegmentData(cell, iteration); | ||
| segments_.push_back(segmentData); | ||
| segmentOrdinals_.push_back(0); | ||
| } | ||
|
|
||
| SegmentData &segmentData = segments_[segment]; | ||
| segmentData.numConnected = 0; | ||
| segmentData.cell = cell; | ||
|
|
||
| CellData &cellData = cells_[cell]; | ||
| segmentOrdinals_[segment] = nextSegmentOrdinal_++; | ||
| cellData.segments.push_back(segment); | ||
| cellData.segments.push_back(segment); //assign the new segment to its mother-cell | ||
|
|
||
| for (auto h : eventHandlers_) { | ||
| h.second->onCreateSegment(segment); | ||
|
|
@@ -103,12 +118,13 @@ Segment Connections::createSegment(CellIdx cell) { | |
| return segment; | ||
| } | ||
|
|
||
|
|
||
| Synapse Connections::createSynapse(Segment segment, | ||
| CellIdx presynapticCell, | ||
| Permanence permanence) { | ||
| // Get an index into the synapses_ list, for the new synapse to reside at. | ||
| Synapse synapse; | ||
| if (!destroyedSynapses_.empty() ) { | ||
| if (!destroyedSynapses_.empty() ) { //TODO implement some capping for mem footprint for destroyed synapses & segments | ||
| synapse = destroyedSynapses_.back(); | ||
| destroyedSynapses_.pop_back(); | ||
| } else { | ||
|
|
@@ -144,14 +160,14 @@ Synapse Connections::createSynapse(Segment segment, | |
| return synapse; | ||
| } | ||
|
|
||
| bool Connections::segmentExists_(Segment segment) const { | ||
| bool Connections::segmentExists_(const Segment segment) const { | ||
| const SegmentData &segmentData = segments_[segment]; | ||
| const vector<Segment> &segmentsOnCell = cells_[segmentData.cell].segments; | ||
| return (std::find(segmentsOnCell.begin(), segmentsOnCell.end(), segment) != | ||
| segmentsOnCell.end()); | ||
| } | ||
|
|
||
| bool Connections::synapseExists_(Synapse synapse) const { | ||
| bool Connections::synapseExists_(const Synapse synapse) const { | ||
| const SynapseData &synapseData = synapses_[synapse]; | ||
| const vector<Synapse> &synapsesOnSegment = | ||
| segments_[synapseData.segment].synapses; | ||
|
|
@@ -181,7 +197,8 @@ void Connections::removeSynapseFromPresynapticMap_( | |
| preSegments.pop_back(); | ||
| } | ||
|
|
||
| void Connections::destroySegment(Segment segment) { | ||
|
|
||
| void Connections::destroySegment(const Segment segment) { | ||
| NTA_ASSERT(segmentExists_(segment)); | ||
| for (auto h : eventHandlers_) { | ||
| h.second->onDestroySegment(segment); | ||
|
|
@@ -210,7 +227,8 @@ void Connections::destroySegment(Segment segment) { | |
| destroyedSegments_.push_back(segment); | ||
| } | ||
|
|
||
| void Connections::destroySynapse(Synapse synapse) { | ||
|
|
||
| void Connections::destroySynapse(const Synapse synapse) { | ||
| NTA_ASSERT(synapseExists_(synapse)); | ||
| for (auto h : eventHandlers_) { | ||
| h.second->onDestroySynapse(synapse); | ||
|
|
@@ -259,7 +277,8 @@ void Connections::destroySynapse(Synapse synapse) { | |
| destroyedSynapses_.push_back(synapse); | ||
| } | ||
|
|
||
| void Connections::updateSynapsePermanence(Synapse synapse, | ||
|
|
||
| void Connections::updateSynapsePermanence(const Synapse synapse, | ||
| Permanence permanence) { | ||
| permanence = std::min(permanence, maxPermanence ); | ||
| permanence = std::max(permanence, minPermanence ); | ||
|
|
@@ -311,30 +330,15 @@ void Connections::updateSynapsePermanence(Synapse synapse, | |
| } | ||
| } | ||
|
|
||
| const vector<Segment> &Connections::segmentsForCell(CellIdx cell) const { | ||
| return cells_[cell].segments; | ||
| } | ||
|
|
||
| Segment Connections::getSegment(CellIdx cell, SegmentIdx idx) const { | ||
| return cells_[cell].segments[idx]; | ||
| } | ||
|
|
||
| const vector<Synapse> &Connections::synapsesForSegment(Segment segment) const { | ||
| NTA_ASSERT(segment < segments_.size()) << "Segment out of bounds! " << segment; | ||
| return segments_[segment].synapses; | ||
| } | ||
|
|
||
| CellIdx Connections::cellForSegment(Segment segment) const { | ||
| return segments_[segment].cell; | ||
| } | ||
|
|
||
| SegmentIdx Connections::idxOnCellForSegment(Segment segment) const { | ||
| SegmentIdx Connections::idxOnCellForSegment(const Segment segment) const { | ||
| const vector<Segment> &segments = segmentsForCell(cellForSegment(segment)); | ||
| const auto it = std::find(segments.begin(), segments.end(), segment); | ||
| NTA_ASSERT(it != segments.end()); | ||
| return (SegmentIdx)std::distance(segments.begin(), it); | ||
| } | ||
|
|
||
|
|
||
| void Connections::mapSegmentsToCells(const Segment *segments_begin, | ||
| const Segment *segments_end, | ||
| CellIdx *cells_begin) const { | ||
|
|
@@ -347,17 +351,6 @@ void Connections::mapSegmentsToCells(const Segment *segments_begin, | |
| } | ||
| } | ||
|
|
||
| Segment Connections::segmentForSynapse(Synapse synapse) const { | ||
| return synapses_[synapse].segment; | ||
| } | ||
|
|
||
| const SegmentData &Connections::dataForSegment(Segment segment) const { | ||
| return segments_[segment]; | ||
| } | ||
|
|
||
| const SynapseData &Connections::dataForSynapse(Synapse synapse) const { | ||
| return synapses_[synapse]; | ||
| } | ||
|
|
||
| bool Connections::compareSegments(const Segment a, const Segment b) const { | ||
| const SegmentData &aData = segments_[a]; | ||
|
|
@@ -391,6 +384,7 @@ void Connections::reset() | |
| currentUpdates_.clear(); | ||
| } | ||
|
|
||
|
|
||
| void Connections::computeActivity( | ||
| vector<SynapseIdx> &numActiveConnectedSynapsesForSegment, | ||
| const vector<CellIdx> &activePresynapticCells) | ||
|
|
@@ -443,15 +437,19 @@ void Connections::computeActivity( | |
| void Connections::adaptSegment(const Segment segment, | ||
| const SDR &inputs, | ||
| const Permanence increment, | ||
| const Permanence decrement) | ||
| const Permanence decrement, | ||
| const bool pruneZeroSynapses) | ||
| { | ||
| const auto &inputArray = inputs.getDense(); | ||
|
|
||
| if( timeseries_ ) { | ||
| previousUpdates_.resize( synapses_.size(), 0.0f ); | ||
| currentUpdates_.resize( synapses_.size(), 0.0f ); | ||
| previousUpdates_.resize( synapses_.size(), minPermanence ); | ||
| currentUpdates_.resize( synapses_.size(), minPermanence ); | ||
| } | ||
|
|
||
| for( const auto synapse : synapsesForSegment(segment) ) { | ||
| const auto& synapses = synapsesForSegment(segment); | ||
| for( size_t i = 0; i < synapses.size(); i++) { | ||
| const auto synapse = synapses[i]; | ||
| const SynapseData &synapseData = dataForSynapse(synapse); | ||
|
|
||
| Permanence update; | ||
|
|
@@ -461,28 +459,34 @@ void Connections::adaptSegment(const Segment segment, | |
| update = -decrement; | ||
| } | ||
|
|
||
| //prune permanences that reached zero | ||
| if (pruneZeroSynapses && synapseData.permanence + update < htm::minPermanence + htm::Epsilon) { | ||
| destroySynapse(synapse); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prune "dead" synapses that reached 0.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ctrl-z-9000-times how is #466 with biological plausibility? I like how the competition helps keep synapses better balanced, but I don't see a natural mechanism. For this reason I started question
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems plausible to me, that dendrites could have a desired number of synapses and that they could enforce this preference. |
||
| prunedSyns_++; //for statistics | ||
| i--; // do not advance `i`, as `destroySynapse` just modified inplace the synapses_, so now a `synapses_[i]` | ||
| // is the "next" synapse. | ||
| continue; | ||
| } | ||
|
|
||
| //update synapse, but for TS only if changed | ||
| if(timeseries_) { | ||
| if( update != previousUpdates_[synapse] ) { | ||
| updateSynapsePermanence(synapse, synapseData.permanence + update); | ||
| } | ||
| currentUpdates_[ synapse ] = update; | ||
| } else { | ||
| updateSynapsePermanence(synapse, synapseData.permanence + update); | ||
| } | ||
| } | ||
| else { | ||
| for( const auto synapse : synapsesForSegment(segment) ) { | ||
| const SynapseData &synapseData = dataForSynapse(synapse); | ||
|
|
||
| Permanence permanence = synapseData.permanence; | ||
| if( inputArray[synapseData.presynapticCell] ) { | ||
| permanence += increment; | ||
| } else { | ||
| permanence -= decrement; | ||
| } | ||
|
|
||
| updateSynapsePermanence(synapse, permanence); | ||
| } | ||
| //destroy segment if it is empty | ||
| if(synapses.empty()) { | ||
| destroySegment(segment); | ||
breznak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| prunedSegs_++; //statistics | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Called for under-performing Segments (can have synapses pruned, etc.). After | ||
| * the call, Segment will have at least segmentThreshold synapses connected, so | ||
|
|
@@ -585,6 +589,9 @@ void Connections::destroyMinPermanenceSynapses( | |
|
|
||
|
|
||
| namespace htm { | ||
| /** | ||
| * print statistics in human readable form | ||
| */ | ||
| std::ostream& operator<< (std::ostream& stream, const Connections& self) | ||
| { | ||
| stream << "Connections:" << std::endl; | ||
|
|
@@ -625,9 +632,9 @@ std::ostream& operator<< (std::ostream& stream, const Connections& self) | |
|
|
||
| for( const auto syn : segData.synapses ) { | ||
| const auto &synData = self.dataForSynapse( syn ); | ||
| if( synData.permanence == minPermanence ) | ||
| if( synData.permanence <= minPermanence + Epsilon ) | ||
| { synapsesDead++; } | ||
| else if( synData.permanence == maxPermanence ) | ||
| else if( synData.permanence >= maxPermanence - Epsilon ) | ||
| { synapsesSaturated++; } | ||
| } | ||
| } | ||
|
|
@@ -636,7 +643,7 @@ std::ostream& operator<< (std::ostream& stream, const Connections& self) | |
| potentialMean = potentialMean / self.numSegments(); | ||
| connectedMean = connectedMean / self.numSegments(); | ||
|
|
||
| stream << " Segments on Cell Min/Mean/Max " | ||
| stream << " Segments on Cell Min/Mean/Max " //TODO print std dev too | ||
| << segmentsMin << " / " << segmentsMean << " / " << segmentsMax << std::endl; | ||
| stream << " Potential Synapses on Segment Min/Mean/Max " | ||
| << potentialMin << " / " << potentialMean << " / " << potentialMax << std::endl; | ||
|
|
@@ -645,6 +652,10 @@ std::ostream& operator<< (std::ostream& stream, const Connections& self) | |
|
|
||
| stream << " Synapses Dead (" << (Real) synapsesDead / self.numSynapses() | ||
| << "%) Saturated (" << (Real) synapsesSaturated / self.numSynapses() << "%)" << std::endl; | ||
| stream << " Synapses pruned (" << (Real) self.prunedSyns_ / self.numSynapses() | ||
| << "%) Segments pruned (" << (Real) self.prunedSegs_ / self.numSegments() << "%)" << std::endl; | ||
| stream << " Buffer for destroyed synapses: " << self.destroyedSynapses_.size() << " \t buffer for destr. segments: " | ||
| << self.destroyedSegments_.size() << std::endl; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you help me make "not completely artificial" test where synapses & segments are heavily pruned?
This is what I get on Hotgym -> not really used at all.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should be able to make it prune synapses by changing the data set. For example train it on sine waves and then move to saw tooth waves. I think it's normal and good that it does not prune very many synapses / segments. Segments should really only be pruned when the HTM reaches capacity.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
would be nice to have some test on "switching contexts", where we observe some more synapse pruning shortly after the change.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| return stream; | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.