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

Boosting cleanup #544

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3a6ab04
SP: use log boosting
breznak Jul 4, 2019
b250f30
SP: updateInhArea() only needed for local inhibition
breznak Jul 4, 2019
f167d55
SP updateInhibitionRadius_() returns UInt and is const
breznak Jul 4, 2019
a459829
SP try boosting completely removed
breznak Jul 4, 2019
5e9abd7
SP: bumpUpWeakColumns move all related timekeeping
breznak Jul 4, 2019
fa10af5
cleanup SP test
breznak Jul 4, 2019
ba4b61d
cleanup SP test
breznak Jul 4, 2019
1be7dcd
SP: use log boosting
breznak Jul 4, 2019
824e738
fix bug in a test
breznak Jul 4, 2019
a23ee49
SP fix boosting if strength == 0.0
breznak Jul 4, 2019
5749587
Merge branch 'log_boosting_fn' into boosting_experiments
breznak Jul 4, 2019
171ceb2
SP updateBoostFactors_() move time-keeping logic inside
breznak Jul 4, 2019
e3246ea
SP remove unused calculateOverlapPct
breznak Jul 4, 2019
27029d8
Merge branch 'sp_rm_calculateOverlapPct' into boosting_experiments
breznak Jul 4, 2019
144c95f
SP fix inhibition return desired number of active WIP
breznak Jul 4, 2019
f61d3bb
Merge branch 'master_community' into boosting_experiments
breznak Jul 6, 2019
5810e21
Revert "SP: use log boosting"
breznak Jul 6, 2019
7bfb7bc
remove work for other PR
breznak Jul 6, 2019
0e6a63f
SP move inhibitionRadius_ updates to inhibition function
breznak Jul 6, 2019
d5c6cc6
Revert "SP move inhibitionRadius_ updates to inhibition function"
breznak Jul 6, 2019
03432c9
comments
breznak Jul 6, 2019
32c70fe
fix test compiles
breznak Jul 6, 2019
57015ec
update inh radius on learn=true only
breznak Jul 6, 2019
528cb20
review feedback: remove unnecessary assert
breznak Jul 6, 2019
7585b1f
Merge branch 'master_community' into boosting_cleanup
breznak Jul 9, 2019
6353d8b
rm unused variable
breznak Jul 9, 2019
36b7c48
cleanup
breznak Jul 9, 2019
553b4e7
Merge branch 'master_community' into boosting_cleanup
breznak Jul 12, 2019
9e4024e
small fixups
breznak Jul 12, 2019
8dfd565
Merge branch 'master_community' into boosting_cleanup
breznak Jul 13, 2019
efaa061
Merge branch 'master_community' into boosting_cleanup
breznak Jul 16, 2019
4386663
Merge branch 'master_community' into boosting_cleanup
breznak Jul 19, 2019
34ec76c
Merge branch 'master' into boosting_cleanup
dkeeney Jul 25, 2019
a0c2f23
Merge branch 'master_community' into boosting_cleanup
breznak Feb 20, 2020
2462e7e
tidy test
breznak Feb 21, 2020
d0dad9b
Merge remote-tracking branch 'community/boosting_cleanup' into boosti…
breznak Feb 21, 2020
6c7a8be
SP rm unneeded overlaps_
breznak Feb 21, 2020
a72e304
Merge branch 'master' into boosting_cleanup
breznak Feb 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.0.22
v0.2.3
2 changes: 1 addition & 1 deletion src/examples/mnist/MNIST_SP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void setup() {
/* synPermConnected */ 0.5f, //no difference, let's leave at 0.5 in the middle
/* minPctOverlapDutyCycles */ 0.2f, //speed of re-learning?
/* dutyCyclePeriod */ 1402,
/* boostStrength */ 2.0f, // Boosting does help, but entropy is high, on MNIST it does not matter, for learning with TM prefer boosting off (=0.0), or "neutral"=1.0
/* boostStrength */ 7.0f, // Boosting does help, but entropy is high, on MNIST it does not matter, for learning with TM prefer boosting off (=0.0), or "neutral"=1.0
/* seed */ 4u,
/* spVerbosity */ 1u,
/* wrapAround */ true); // does not matter (helps slightly)
Expand Down
4 changes: 2 additions & 2 deletions src/htm/algorithms/Connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,9 @@ vector<SynapseIdx> Connections::computeActivity(const vector<CellIdx> &activePre
}

// Iterate through all connected synapses.
for (const auto& cell : activePresynapticCells) {
for (const auto cell : activePresynapticCells) {
if (connectedSegmentsForPresynapticCell_.count(cell)) {
for(const auto& segment : connectedSegmentsForPresynapticCell_.at(cell)) {
for(const auto segment : connectedSegmentsForPresynapticCell_.at(cell)) {
++numActiveConnectedSynapsesForSegment[segment];
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/htm/algorithms/Connections.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ class Connections : public Serializable
* An output vector for active potential synapse counts per segment.
*
* @param activePresynapticCells
* Active cells in the input.
* Active cells in the input as a sparse indices.
*
* @param bool learn : enable learning updates (default true)
*
Expand Down
143 changes: 86 additions & 57 deletions src/htm/algorithms/SpatialPooler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ void SpatialPooler::initialize(
connections_.raisePermanencesToThreshold( (Segment)i, stimulusThreshold_ );
}

updateInhibitionRadius_();
inhibitionRadius_ = updateInhibitionRadius_();

if (spVerbosity_ > 0) {
printParameters();
Expand All @@ -453,14 +453,22 @@ void SpatialPooler::initialize(
const vector<SynapseIdx> SpatialPooler::compute(const SDR &input, const bool learn, SDR &active) {
input.reshape( inputDimensions_ );
active.reshape( columnDimensions_ );

updateBookeepingVars_(learn);

//boosting
//must be done before inhibition
const auto& overlaps = connections_.computeActivity(input.getSparse(), learn);
boostOverlaps_(overlaps, boostedOverlaps_); //TODO consider removal, @1

boostOverlaps_(overlaps, boostedOverlaps_);

auto &activeVector = active.getSparse();
//inhibition
//update inhibition radius if it's time, only changes in local inh
auto& activeVector = active.getSparse();
if(!globalInhibition_ and isUpdateRound_() and learn) {
Copy link
Member Author

Choose a reason for hiding this comment

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

this PR breaks some tests (results). I think because of this change, now correct: update only IF: local & isUpdate & learning. Compare with former: https://github.com/htm-community/htm.core/pull/544/files#diff-75db947f594e8a477099a6c8bb86daa0L475

inhibitionRadius_ = updateInhibitionRadius_();
}
inhibitColumns_(boostedOverlaps_, activeVector);

// Notify the active SDR that its internal data vector has changed. Always
// call SDR's setter methods even if when modifying the SDR's own data
// inplace.
Expand All @@ -469,13 +477,9 @@ const vector<SynapseIdx> SpatialPooler::compute(const SDR &input, const bool lea

if (learn) {
adaptSynapses_(input, active);
updateDutyCycles_(overlaps, active);
bumpUpWeakColumns_();
updateBoostFactors_();
if (isUpdateRound_()) {
updateInhibitionRadius_();
updateMinDutyCycles_();
}
//boosting
bumpUpWeakColumns_(overlaps); //TODO suggest removal, low impact, long time
updateBoostFactors_(active); //helper for @1, computed after inh
}

return overlaps;
Expand Down Expand Up @@ -565,11 +569,9 @@ vector<Real> SpatialPooler::initPermanence_(const vector<UInt> &potential, //TOD
}


void SpatialPooler::updateInhibitionRadius_() {
if (globalInhibition_) {
inhibitionRadius_ =
*max_element(columnDimensions_.cbegin(), columnDimensions_.cend());
return;
UInt SpatialPooler::updateInhibitionRadius_() const {
if (globalInhibition_) { //always const for global inh
return *max_element(columnDimensions_.cbegin(), columnDimensions_.cend());
}

Real connectedSpan = 0.0f;
Expand All @@ -581,7 +583,8 @@ void SpatialPooler::updateInhibitionRadius_() {
const Real diameter = connectedSpan * columnsPerInput;
Real radius = (diameter - 1) / 2.0f;
radius = max((Real)1.0, radius);
inhibitionRadius_ = UInt(round(radius));

return UInt(round(radius));
}


Expand Down Expand Up @@ -626,26 +629,6 @@ void SpatialPooler::updateMinDutyCyclesLocal_() {
}


void SpatialPooler::updateDutyCycles_(const vector<SynapseIdx> &overlaps,
SDR &active) {

// Turn the overlaps array into an SDR. Convert directly to flat-sparse to
// avoid copies and type convertions.
SDR newOverlap({ numColumns_ });
auto &overlapsSparseVec = newOverlap.getSparse();
for (UInt i = 0; i < numColumns_; i++) {
if( overlaps[i] != 0 )
overlapsSparseVec.push_back( i );
}
newOverlap.setSparse( overlapsSparseVec );

const UInt period = std::min(dutyCyclePeriod_, iterationNum_);

updateDutyCyclesHelper_(overlapDutyCycles_, newOverlap, period);
updateDutyCyclesHelper_(activeDutyCycles_, active, period);
}


Real SpatialPooler::avgColumnsPerInput_() const {
const size_t numDim = max(columnDimensions_.size(), inputDimensions_.size());
Real columnsPerInput = 0.0f;
Expand Down Expand Up @@ -702,13 +685,40 @@ void SpatialPooler::adaptSynapses_(const SDR &input,
}


void SpatialPooler::bumpUpWeakColumns_() {
void SpatialPooler::bumpUpWeakColumns_(const std::vector<SynapseIdx>& overlaps) {
for (UInt i = 0; i < numColumns_; i++) {
// skip columns (segments) that are already performing OK
if (overlapDutyCycles_[i] >= minOverlapDutyCycles_[i]) {
continue;
}
//bump the weak
connections_.bumpSegment( i, synPermBelowStimulusInc_ );
}

//do updates:

// update overlap duty cycles (each round)
updateDutyCyclesOverlaps_(overlaps);

//update minOverlapDutyCycles_ (on update round only)
if (isUpdateRound_()) {
updateMinDutyCycles_();
}
}


void SpatialPooler::updateDutyCyclesOverlaps_(const vector<SynapseIdx>& overlaps) {
SDR newOverlap({ numColumns_ });
auto &overlapsSparseVec = newOverlap.getSparse();

for (UInt i = 0; i < numColumns_; i++) {
if( overlaps[i] > Epsilon )
overlapsSparseVec.push_back( i );
}
newOverlap.setSparse( overlapsSparseVec );

const UInt period = std::min(dutyCyclePeriod_, iterationNum_);
updateDutyCyclesHelper_(overlapDutyCycles_, newOverlap, period);
}


Expand All @@ -734,7 +744,19 @@ void SpatialPooler::updateDutyCyclesHelper_(vector<Real> &dutyCycles,
}


void SpatialPooler::updateBoostFactors_() {
void SpatialPooler::updateBoostFactors_(const SDR& active) {
if(boostStrength_ < htm::Epsilon) return; //skip for disabled boosting

/**
Updates the duty cycles for each column. The ACTIVITY duty cycles is
a moving average of the frequency of activation for each column.

@param active A SDR of active columns which survived inhibition
@param period
*/
const UInt period = std::min(dutyCyclePeriod_, iterationNum_);
updateDutyCyclesHelper_(activeDutyCycles_, active, period);

if (globalInhibition_) {
updateBoostFactorsGlobal_();
} else {
Expand All @@ -749,15 +771,14 @@ void applyBoosting_(const UInt i,
const Real boost,
vector<Real>& output) {
if(boost < htm::Epsilon) return; //skip for disabled boosting
output[i] = exp((targetDensity - actualDensity[i]) * boost); //TODO doc this code
output[i] = exp((targetDensity - actualDensity[i]) * boost); //exponential boosting, default for Numenta
//output[i] = log(actualDensity[i]) / log(targetDensity); //log boosting
breznak marked this conversation as resolved.
Show resolved Hide resolved
}


void SpatialPooler::updateBoostFactorsGlobal_() {
const Real targetDensity = localAreaDensity_;

for (UInt i = 0; i < numColumns_; ++i) {
applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_);
applyBoosting_(i, localAreaDensity_, activeDutyCycles_, boostStrength_, boostFactors_);
}
}

Expand Down Expand Up @@ -797,9 +818,7 @@ void SpatialPooler::inhibitColumns_(const vector<Real> &overlaps,
vector<CellIdx> &activeColumns) const {
const Real density = localAreaDensity_;

if (globalInhibition_ ||
inhibitionRadius_ >
*max_element(columnDimensions_.begin(), columnDimensions_.end())) {
if (globalInhibition_) {
inhibitColumnsGlobal_(overlaps, density, activeColumns);
} else {
inhibitColumnsLocal_(overlaps, density, activeColumns);
Expand All @@ -808,30 +827,38 @@ void SpatialPooler::inhibitColumns_(const vector<Real> &overlaps,


void SpatialPooler::inhibitColumnsGlobal_(const vector<Real> &overlaps,
Real density,
vector<UInt> &activeColumns) const {
const Real density,
SDR_sparse_t &activeColumns) const {
NTA_ASSERT(!overlaps.empty());
NTA_ASSERT(density > 0.0f && density <= 1.0f);

activeColumns.clear();
const UInt numDesired = (UInt)(density * numColumns_);
const UInt numDesired = static_cast<UInt>(density * numColumns_);
NTA_CHECK(numDesired > 0) << "Not enough columns (" << numColumns_ << ") "
<< "for desired density (" << density << ").";
// Sort the columns by the amount of overlap. First make a list of all of the
// column indexes.
activeColumns.reserve(numColumns_);

int same_overlap = 0;
for(UInt i = 0; i < numColumns_; i++)
activeColumns.push_back(i);
// Compare the column indexes by their overlap.
auto compare = [&overlaps](const UInt &a, const UInt &b) -> bool
{return (overlaps[a] == overlaps[b]) ? a > b : overlaps[a] > overlaps[b];}; //for determinism if overlaps match (tieBreaker does not solve that),
//otherwise we'd return just `return overlaps[a] > overlaps[b]`.
auto compare = [&overlaps, &same_overlap](const UInt &a, const UInt &b) -> bool
{
if (overlaps[a] == overlaps[b]) {
same_overlap++;
return a > b; //but we also need this for deterministic results
} else {
return overlaps[a] > overlaps[b]; //this is the main "sort columns by overlaps"
}
};

// Do a partial sort to divide the winners from the losers. This sort is
// faster than a regular sort because it stops after it partitions the
// elements about the Nth element, with all elements on their correct side of
// the Nth element.
std::nth_element(
std::nth_element(
activeColumns.begin(),
activeColumns.begin() + numDesired,
activeColumns.end(),
Expand All @@ -844,16 +871,18 @@ void SpatialPooler::inhibitColumnsGlobal_(const vector<Real> &overlaps,
while( !activeColumns.empty() &&
overlaps[activeColumns.back()] < stimulusThreshold_)
activeColumns.pop_back();
//FIXME not numDesired
}


void SpatialPooler::inhibitColumnsLocal_(const vector<Real> &overlaps,
Real density,
const Real density,
vector<UInt> &activeColumns) const {

activeColumns.clear();

// Tie-breaking: when overlaps are equal, columns that have already been
// selected are treated as "bigger".
// selected are treated as "bigger". //TODO move this idea to the sort/comparison logic
vector<bool> activeColumnsDense(numColumns_, false);

for (UInt column = 0; column < numColumns_; column++) {
Expand Down Expand Up @@ -903,8 +932,8 @@ void SpatialPooler::inhibitColumnsLocal_(const vector<Real> &overlaps,
}
}

const UInt numActive = (UInt)(0.5f + (density * (numNeighbors + 1)));
if (numBigger < numActive) {
const UInt numDesired = static_cast<UInt>(std::ceil(density * (numNeighbors + 1)));
if (numBigger < numDesired) {
activeColumns.push_back(column);
activeColumnsDense[column] = true;
}
Expand Down
Loading