Skip to content

Commit 0e9d108

Browse files
committed
Merge branch 'master_community' into conn_opt
2 parents 8db6471 + b59f646 commit 0e9d108

File tree

7 files changed

+91
-36
lines changed

7 files changed

+91
-36
lines changed

src/examples/mnist/MNIST_SP.cpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,32 @@
3131
#include <htm/algorithms/SpatialPooler.hpp>
3232
#include <htm/algorithms/SDRClassifier.hpp>
3333
#include <htm/utils/SdrMetrics.hpp>
34+
#include <htm/os/Timer.hpp>
3435

3536
#include <mnist/mnist_reader.hpp> // MNIST data itself + read methods, namespace mnist::
3637
#include <mnist/mnist_utils.hpp> // mnist::binarize_dataset
3738

38-
namespace examples {
3939

4040
using namespace std;
4141
using namespace htm;
4242

4343
class MNIST {
44+
/**
45+
* RESULTS:
46+
*
47+
* Order : score : column dim : #pass : time(s): git commit : comment
48+
* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
49+
* 1/Score: 97.11% (289 / 10000 wrong) : 28x28x16 : 4 : 557 : 1f0187fc6 : epochs help, at cost of time
50+
*
51+
* 2/Score: 96.56% (344 / 10000 wrong) : 28x28x16 : 1 : 142 : 3ccadc6d6
52+
*
53+
* 3/Score: 96.1% (390 / 10000 wrong). : 28x28x30 : 1 : 256 : 454f7a9d8
54+
*
55+
* others/
56+
* Score: 95.35% (465 / 10000 wrong) : 28x28x16 : 2 : 125 : : smaller boosting (2.0)
57+
* -- this will be my working model, reasonable performance/speed ratio
58+
*
59+
*/
4460

4561
private:
4662
SpatialPooler sp;
@@ -51,31 +67,31 @@ class MNIST {
5167

5268
public:
5369
UInt verbosity = 1;
54-
const UInt train_dataset_iterations = 1u;
70+
const UInt train_dataset_iterations = 2u; //epochs somewhat help, at linear time
5571

5672

5773
void setup() {
5874

59-
input.initialize({28 * 28});
60-
columns.initialize({10 * 1000});
75+
input.initialize({28, 28,1});
76+
columns.initialize({28, 28, 8}); //1D vs 2D no big difference, 2D seems more natural for the problem. Speed-----, Results+++++++++; #columns HIGHEST impact.
6177
sp.initialize(
6278
/* inputDimensions */ input.dimensions,
6379
/* columnDimensions */ columns.dimensions,
64-
/* potentialRadius */ 999999u, // No topology, all to all connections.
65-
/* potentialPct */ 0.65f,
66-
/* globalInhibition */ true,
67-
/* localAreaDensity */ 0.05f, // % active bits
80+
/* potentialRadius */ 7, // with 2D, 7 results in 15x15 area, which is cca 25% for the input area. Slightly improves than 99999 aka "no topology, all to all connections"
81+
/* potentialPct */ 0.1f, //we have only 10 classes, and << #columns. So we want to force each col to specialize. Cca 0.3 w "7" above, or very small (0.1) for "no topology". Cannot be too small due to internal checks. Speed++
82+
/* globalInhibition */ true, //Speed+++++++; SDR quality-- (global does have active nearby cols, which we want to avoid (local)); Results+-0
83+
/* localAreaDensity */ 0.1f, // % active bits
6884
/* numActiveColumnsPerInhArea */ -1,
6985
/* stimulusThreshold */ 6u,
70-
/* synPermInactiveDec */ 0.005f,
71-
/* synPermActiveInc */ 0.014f,
72-
/* synPermConnected */ 0.1f,
73-
/* minPctOverlapDutyCycles */ 0.001f,
86+
/* synPermInactiveDec */ 0.002f, //FIXME inactive decay permanence plays NO role, investigate! (slightly better w/o it)
87+
/* synPermActiveInc */ 0.14f, //takes upto 5x steps to get dis/connected
88+
/* synPermConnected */ 0.5f, //no difference, let's leave at 0.5 in the middle
89+
/* minPctOverlapDutyCycles */ 0.2f, //speed of re-learning?
7490
/* dutyCyclePeriod */ 1402,
75-
/* boostStrength */ 7.8f, // Boosting does help
76-
/* seed */ 93u,
91+
/* 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
92+
/* seed */ 4u,
7793
/* spVerbosity */ 1u,
78-
/* wrapAround */ false); // No topology, turn off wrapping
94+
/* wrapAround */ true); // does not matter (helps slightly)
7995

8096
// Save the connections to file for postmortem analysis.
8197
ofstream dump("mnist_sp_initial.connections", ofstream::binary | ofstream::trunc | ofstream::out);
@@ -99,6 +115,8 @@ void train() {
99115
Metrics inputStats(input, 1402);
100116
Metrics columnStats(columns, 1402);
101117

118+
Timer tTrain(true);
119+
102120
for(auto epoch = 0u; epoch < train_dataset_iterations; epoch++) {
103121
NTA_INFO << "epoch " << epoch;
104122
// Shuffle the training data.
@@ -120,11 +138,15 @@ void train() {
120138
if( verbosity && (++i % 1000 == 0) ) cout << "." << flush;
121139
}
122140
if( verbosity ) cout << endl;
123-
}
141+
124142
cout << "epoch ended" << endl;
125143
cout << "inputStats " << inputStats << endl;
126144
cout << "columnStats " << columnStats << endl;
127145
cout << sp << endl;
146+
}
147+
148+
tTrain.stop();
149+
cout << "MNIST train time: " << tTrain.getElapsed() << endl;
128150

129151
// Save the connections to file for postmortem analysis.
130152
ofstream dump("mnist_sp_learned.connections", ofstream::binary | ofstream::trunc | ofstream::out);
@@ -153,14 +175,15 @@ void test() {
153175
if( verbosity && i % 1000 == 0 ) cout << "." << flush;
154176
}
155177
if( verbosity ) cout << endl;
156-
cout << "Score: " << 100.0 * score / n_samples << "% " << endl;
178+
cout << "===========RESULTs=================" << endl;
179+
cout << "Score: " << 100.0 * score / n_samples << "% ("<< (n_samples - score) << " / " << n_samples << " wrong). " << endl;
180+
cout << "SDR example: " << columns << endl;
157181
}
158182

159183
}; // End class MNIST
160-
} // End namespace examples
161184

162185
int main(int argc, char **argv) {
163-
examples::MNIST m;
186+
MNIST m;
164187
m.setup();
165188
m.train();
166189
m.test();

src/htm/algorithms/SpatialPooler.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ void SpatialPooler::initialize(
432432
overlapDutyCycles_.assign(numColumns_, 0); //TODO make all these sparse or rm to reduce footprint
433433
activeDutyCycles_.assign(numColumns_, 0);
434434
minOverlapDutyCycles_.assign(numColumns_, 0.0);
435-
boostFactors_.assign(numColumns_, 1);
435+
boostFactors_.assign(numColumns_, 1.0); //1 is neutral value for boosting
436436
overlaps_.resize(numColumns_);
437437
boostedOverlaps_.resize(numColumns_);
438438

@@ -493,6 +493,10 @@ void SpatialPooler::compute(const SDR &input, const bool learn, SDR &active) {
493493

494494
void SpatialPooler::boostOverlaps_(const vector<SynapseIdx> &overlaps, //TODO use Eigen sparse vector here
495495
vector<Real> &boosted) const {
496+
if(boostStrength_ < htm::Epsilon) { //boost ~ 0.0, we can skip these computations, just copy the data
497+
boosted.assign(overlaps.begin(), overlaps.end());
498+
return;
499+
}
496500
for (UInt i = 0; i < numColumns_; i++) {
497501
boosted[i] = overlaps[i] * boostFactors_[i];
498502
}
@@ -748,6 +752,16 @@ void SpatialPooler::updateBoostFactors_() {
748752
}
749753

750754

755+
void applyBoosting_(const UInt i,
756+
const Real targetDensity,
757+
const vector<Real>& actualDensity,
758+
const Real boost,
759+
vector<Real>& output) {
760+
if(boost < htm::Epsilon) return; //skip for disabled boosting
761+
output[i] = exp((targetDensity - actualDensity[i]) * boost); //TODO doc this code
762+
}
763+
764+
751765
void SpatialPooler::updateBoostFactorsGlobal_() {
752766
Real targetDensity;
753767
if (numActiveColumnsPerInhArea_ > 0) {
@@ -760,9 +774,9 @@ void SpatialPooler::updateBoostFactorsGlobal_() {
760774
} else {
761775
targetDensity = localAreaDensity_;
762776
}
763-
764-
for (UInt i = 0; i < numColumns_; ++i) {
765-
boostFactors_[i] = exp((targetDensity - activeDutyCycles_[i]) * boostStrength_);
777+
778+
for (UInt i = 0; i < numColumns_; ++i) {
779+
applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_);
766780
}
767781
}
768782

@@ -785,8 +799,7 @@ void SpatialPooler::updateBoostFactorsLocal_() {
785799
}
786800

787801
const Real targetDensity = localActivityDensity / numNeighbors;
788-
boostFactors_[i] =
789-
exp((targetDensity - activeDutyCycles_[i]) * boostStrength_);
802+
applyBoosting_(i, targetDensity, activeDutyCycles_, boostStrength_, boostFactors_);
790803
}
791804
}
792805

@@ -806,7 +819,7 @@ void SpatialPooler::calculateOverlap_(const SDR &input,
806819

807820

808821
void SpatialPooler::inhibitColumns_(const vector<Real> &overlaps,
809-
vector<UInt> &activeColumns) const {
822+
vector<CellIdx> &activeColumns) const {
810823
Real density = localAreaDensity_;
811824
if (numActiveColumnsPerInhArea_ > 0) {
812825
UInt inhibitionArea =

src/htm/algorithms/SpatialPooler.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class SpatialPooler : public Serializable
107107
that each column can potentially be connected to every input
108108
bit. This parameter defines a square (or hyper square) area: a
109109
column will have a max square potential pool with sides of
110-
length (2 * potentialRadius + 1).
110+
length `(2 * potentialRadius + 1)`, rounded to fit into each dimension.
111111
112112
@param potentialPct The percent of the inputs, within a column's
113113
potential radius, that a column can be connected to. If set to
@@ -186,7 +186,8 @@ class SpatialPooler : public Serializable
186186
likely to oscillate.
187187
188188
@param boostStrength A number greater or equal than 0, used to
189-
control boosting strength. No boosting is applied if it is set to 0.
189+
control boosting strength.
190+
No boosting is applied if it is set to 0.0, (runs faster due to skipped code).
190191
The strength of boosting increases as a function of boostStrength.
191192
Boosting encourages columns to have similar activeDutyCycles as their
192193
neighbors, which will lead to more efficient use of columns. However,
@@ -915,7 +916,7 @@ class SpatialPooler : public Serializable
915916
columns.
916917
*/
917918
void inhibitColumns_(const vector<Real> &overlaps,
918-
vector<UInt> &activeColumns) const;
919+
vector<CellIdx> &activeColumns) const;
919920

920921
/**
921922
Perform global inhibition.

src/htm/algorithms/TemporalMemory.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ void TemporalMemory::activateCells(const SDR &activeColumns, const bool learn) {
433433
sparse, identity,
434434
activeSegments_, columnForSegment,
435435
matchingSegments_, columnForSegment)) {
436-
Segment column;
436+
CellIdx column;
437437
vector<Segment>::const_iterator activeColumnsBegin, activeColumnsEnd,
438438
columnActiveSegmentsBegin, columnActiveSegmentsEnd,
439439
columnMatchingSegmentsBegin, columnMatchingSegmentsEnd;
@@ -599,9 +599,12 @@ SDR TemporalMemory::cellsToColumns(const SDR& cells) const {
599599
auto correctDims = getColumnDimensions(); //nD column dimensions (eg 10x100)
600600
correctDims.push_back(static_cast<CellIdx>(getCellsPerColumn())); //add n+1-th dimension for cellsPerColumn (eg. 10x100x8)
601601

602-
NTA_CHECK(cells.dimensions == correctDims)
602+
NTA_CHECK(cells.dimensions.size() == correctDims.size())
603603
<< "cells.dimensions must match TM's (column dims x cellsPerColumn) ";
604604

605+
for(size_t i = 0; i<correctDims.size(); i++)
606+
NTA_CHECK(correctDims[i] == cells.dimensions[i]);
607+
605608
SDR cols(getColumnDimensions());
606609
auto& dense = cols.getDense();
607610
for(const auto cell : cells.getSparse()) {

src/htm/types/Sdr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ namespace htm {
318318
NTA_CHECK( ( 1 + fractionNoise) * getSparsity() <= 1. );
319319

320320
const UInt num_move_bits = (UInt) std::round( fractionNoise * getSum() );
321-
const vector<UInt> turn_off = rng.sample(getSparse(), num_move_bits);
321+
const auto& turn_off = rng.sample(getSparse(), num_move_bits);
322322

323323
auto& dns = getDense();
324324

src/htm/utils/GroupBy.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ static KeyType minFrontKey(KeyType frontrunner, Iterator0 begin0,
7777
KeyType ret = frontrunner;
7878

7979
if (begin0 != end0) {
80-
ret = std::min(ret, keyFn0(*begin0));
80+
ret = std::min(ret, static_cast<KeyType>(keyFn0(*begin0)));
8181
}
8282

8383
if (begin1 != end1) {

src/test/unit/algorithms/SpatialPoolerTest.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,20 +1676,35 @@ TEST(SpatialPoolerTest, getOverlaps) {
16761676

16771677
vector<Real> boostFactors = {1.0f, 2.0f, 3.0f};
16781678
sp.setBoostFactors(boostFactors.data());
1679+
sp.setBoostStrength(0.0f); //default, effectively disables boosting
16791680

16801681
SDR input( {5});
16811682
input.setDense(vector<UInt>{1, 1, 1, 1, 1});
16821683
SDR activeColumns( {3} );
16831684
activeColumns.setDense(vector<UInt>{0, 0, 0});
16841685
sp.compute(input, true, activeColumns);
16851686

1687+
//overlaps (not boosted)
16861688
const auto &overlaps = sp.getOverlaps();
16871689
const vector<SynapseIdx> expectedOverlaps = {0, 3, 5};
16881690
EXPECT_EQ(expectedOverlaps, overlaps);
16891691

1690-
const vector<Real> &boostedOverlaps = sp.getBoostedOverlaps();
1691-
const vector<Real> expectedBoostedOverlaps = {0.0f, 6.0f, 15.0f};
1692-
EXPECT_EQ(expectedBoostedOverlaps, boostedOverlaps);
1692+
//boosted overlaps, but boost strength=0.0
1693+
const auto& boostedOverlaps = sp.getBoostedOverlaps();
1694+
const vector<Real> expectedBoostedOverlaps = {0.0f, 3.0f, 5.0f}; //same as orig above (but float)
1695+
EXPECT_EQ(expectedBoostedOverlaps, boostedOverlaps) << "SP with boost strength " << sp.getBoostStrength() << " must not change boosting ";
1696+
1697+
//boosted overlaps, but boost strength=2.0
1698+
//recompute
1699+
sp.setBoostFactors(boostFactors.data());
1700+
sp.setBoostStrength(2.0f);
1701+
1702+
activeColumns.setDense(vector<UInt>{0, 0, 0});
1703+
sp.compute(input, true, activeColumns);
1704+
1705+
const auto& boostedOverlaps2 = sp.getBoostedOverlaps();
1706+
const vector<Real> expectedBoostedOverlaps2 = {0.0f, 6.0f, 15.0f};
1707+
EXPECT_EQ(expectedBoostedOverlaps2, boostedOverlaps2) << "SP with boost strength " << sp.getBoostStrength() << " must change boosting ";
16931708
}
16941709

16951710
TEST(SpatialPoolerTest, ZeroOverlap_NoStimulusThreshold_GlobalInhibition) {

0 commit comments

Comments
 (0)