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

Hotgym predictor, anomaly tests #675

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Example usage:
TODO
)");

py::enum_<TemporalMemory::ANMode>(m, "ANMode")
py::enum_<TemporalMemory::ANMode>(m, "ANMode") //TODO currently htm.bindings.algorithms.ANMode, make ANMode part of algorithms.TemporalMemory
Copy link
Member Author

Choose a reason for hiding this comment

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

nit, how would it be possible to make enum ANMode exposed as a part of htm.bindings.algorithms.TemporalMemory.ANMode, and not the current ..bindings.algorithms.ANMode ?

.value("DISABLED", TemporalMemory::ANMode::DISABLED)
.value("RAW", TemporalMemory::ANMode::RAW)
.value("LIKELIHOOD", TemporalMemory::ANMode::LIKELIHOOD)
Expand Down Expand Up @@ -372,6 +372,8 @@ R"()");
"Anomaly score updated with each TM::compute() call. "
);

py_HTM.def("setAnomalyMode", &HTM_t::setAnomalyMode);
Copy link
Member Author

Choose a reason for hiding this comment

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

defined, compiles, but not found in py test?!


py_HTM.def("__str__",
[](HTM_t &self) {
std::stringstream buf;
Expand Down
21 changes: 20 additions & 1 deletion bindings/py/tests/algorithms/temporal_memory_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,30 @@ def testPredictiveCells(self):
_print("activeCols:"+str(len(activeColumnsA.sparse)))
_print("activeCells:"+str(len(tm.getActiveCells().sparse)))
_print("predictiveCells:"+str(len(predictiveCellsSDR.sparse)))


def testAnomaly(self):
"""test convergence of TM and quality of anomaly methods"""
from htm.bindings.algorithms import ANMode
tm = TM(columnDimensions=[2048], anomalyMode=ANMode.RAW) #FIXME likelihood is always 0.5?! .LIKELIHOOD)

modes = [ANMode.RAW, ANMode.LIKELIHOOD, ANMode.LOGLIKELIHOOD]
for mod in modes: #this block test convergence of TM and anomaly score for select mode
#FIXME why not visible from bidngings? tm.setAnomalyMode(mod)
Copy link
Member Author

Choose a reason for hiding this comment

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

setAnomalyMode not found in bindings (see its definition there above)

#print("testing {}".format(mod))
inp = SDR([2048]).randomize(0.05) #starting SDR with 5% bits ON

#learn TM a bit, anomaly should be low
for _ in range(200):
inp.addNoise(0.02) #change 2% bits -> 98% overlap => anomaly should ideally be 2%
tm.compute(inp, learn=True)
self.assertLess(tm.anomaly, 0.08)
Copy link
Member Author

Choose a reason for hiding this comment

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

new test that TM actually learns and anomalies converge.




def _print(txt):
if debugPrint:
print(txt)

if __name__ == "__main__":
unittest.main()
unittest.main()
47 changes: 32 additions & 15 deletions src/examples/hotgym/HelloSPTP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include "htm/algorithms/TemporalMemory.hpp"
#include "htm/algorithms/SpatialPooler.hpp"
#include "htm/encoders/RandomDistributedScalarEncoder.hpp"
#include "htm/algorithms/AnomalyLikelihood.hpp"
#include "htm/algorithms/SDRClassifier.hpp" // Classifier, Predictor

#include "htm/types/Sdr.hpp"
#include "htm/utils/Random.hpp"
Expand All @@ -36,12 +36,21 @@ namespace examples {
using namespace std;
using namespace htm;

/**
* helper to transform (Real) data to categories (UInt) for Classifier/Predictor
**/
UInt realToCategory_(const Real r) {
return static_cast<UInt>((r+1.0f /*map sin(x):[-1,1] map it to [0,2]*/)*1000); //precision on 3 dec places
Copy link
Member Author

Choose a reason for hiding this comment

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

the std::bad_alloc bug (only on Release) was caused here, as the mapping to labels used to underflow (UInt -1), resulting in a huge label, which led to a huge PDF vectors. (not in Debug, because that runs only like 2 steps).

}
Real categoryToReal_(const UInt bin) {
return static_cast<Real>((bin/1000.0f)-1.0f);
}

// work-load
Real64 BenchmarkHotgym::run(UInt EPOCHS, bool useSPlocal, bool useSPglobal, bool useTM, const UInt COLS, const UInt DIM_INPUT, const UInt CELLS)
{
#ifndef NDEBUG
EPOCHS = 2; // make test faster in Debug
EPOCHS = 10; // make test faster in Debug
#endif

#if defined __aarch64__ || defined __arm__
Expand Down Expand Up @@ -75,8 +84,10 @@ EPOCHS = 2; // make test faster in Debug
Random rnd(42); //uses fixed seed for deterministic output checks

TemporalMemory tm(vector<UInt>{COLS}, CELLS);
tm.setAnomalyMode(TemporalMemory::ANMode::RAW); //set other modes here

Predictor pred( vector<UInt>{0,100}); //predict 0 (=classify current), 100 steps ahead

AnomalyLikelihood anLikelihood;
tInit.stop();

// data for processing input
Expand All @@ -85,7 +96,7 @@ EPOCHS = 2; // make test faster in Debug
SDR outSPlocal(spLocal.getColumnDimensions()); //for SPlocal
SDR outSP(vector<UInt>{COLS});
SDR outTM(spGlobal.getColumnDimensions());
Real an = 0.0f, anLikely = 0.0f; //for anomaly:
Real an = 0.0f; //for anomaly:
MovingAverage avgAnom10(1000); //chose the window large enough so there's (some) periodicity in the patter, so TM can learn something

//metrics
Expand All @@ -98,9 +109,6 @@ EPOCHS = 2; // make test faster in Debug
* For example: fn = sin(x) -> periodic >= 2Pi ~ 6.3 && x+=0.01 -> 630 steps to 1st period -> window >= 630
*/
Real avgAnomOld_ = 1.0;
NTA_CHECK(avgAnomOld_ >= avgAnom10.getCurrentAvg()) << "TM should learn and avg anomalies improve, but we got: "
<< avgAnomOld_ << " and now: " << avgAnom10.getCurrentAvg(); //invariant


// Start a stopwatch timer
printf("starting: %d iterations.", EPOCHS);
Expand All @@ -113,11 +121,13 @@ EPOCHS = 2; // make test faster in Debug
//Encode
tEnc.start();
x+=0.01f; //step size for fn(x)
enc.encode(sin(x), input); //model sin(x) function //TODO replace with CSV data
// cout << x << "\n" << sin(x) << "\n" << input << "\n\n";
const Real data = sin(x);
enc.encode(data, input); //model sin(x) function //TODO replace with CSV data
// cout << x << "\n" << data << "\n" << input << "\n\n";
tEnc.stop();

tRng.start();
//TODO this is dropout:
input.addNoise(0.01f, rnd); //change 1% of the SDR for each iteration, this makes a random sequence, but seemingly stable
tRng.stop();

Expand Down Expand Up @@ -148,19 +158,22 @@ EPOCHS = 2; // make test faster in Debug
//Anomaly (pure x likelihood)
an = tm.anomaly;
avgAnom10.compute(an); //moving average
if(e % 1000 == 0) {
if(e % (500 + avgAnom10.getData().size() /*size of avg window for an*/) == 0) {
NTA_CHECK(avgAnomOld_ >= avgAnom10.getCurrentAvg()) << "TM should learn and avg anomalies improve, but we got: "
<< avgAnomOld_ << " and now: " << avgAnom10.getCurrentAvg(); //invariant
avgAnomOld_ = avgAnom10.getCurrentAvg(); //update
}
tAnLikelihood.start();
anLikelihood.anomalyProbability(an); //FIXME AnLikelihood is 0.0, probably not working correctly
tAnLikelihood.stop();

//Classifier, Predictor
tCls.start();
pred.learn(e, outTM, { realToCategory_(data) }); //FIXME fails with bad_alloc if label is too large! PDF should use map, instead of a vector
Copy link
Member Author

Choose a reason for hiding this comment

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

FIXME: this should be resolved in the Predictor. A large label should not crash the program (caused by huge PDF if label is large). See if PDF can use map.

tCls.stop();


// print
if (e == EPOCHS - 1) {
tAll.stop();
pred.reset();

//print connections stats
cout << "\nInput :\n" << statsInput
Expand All @@ -176,10 +189,14 @@ EPOCHS = 2; // make test faster in Debug
cout << "Epoch = " << e << endl;
cout << "Anomaly = " << an << endl;
cout << "Anomaly (avg) = " << avgAnom10.getCurrentAvg() << endl;
cout << "Anomaly (Likelihood) = " << anLikely << endl;
cout << "SP (g)= " << outSP << endl;
cout << "SP (l)= " << outSPlocal <<endl;
cout << "TM= " << outTM << endl;
cout << "Cls[0]= " << categoryToReal_(argmax(pred.infer(outTM)[0])) << endl;
cout << "Cls[100]= " << categoryToReal_(argmax(pred.infer(outTM)[100])) << endl;

NTA_CHECK( categoryToReal_(argmax(pred.infer(outTM)[0])) != -1) << "Classifier did not learn"; //FIXME Predictor is not learning, this should be ~ sin(49.99)
Copy link
Member Author

@breznak breznak Sep 20, 2019

Choose a reason for hiding this comment

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

FIXME: this is always 0, predictor/classifier is not learning!
Although the unit-tests are passing.
@Thanh-Binh PR #667 should have fixed the symptoms you've described. But my predictor is still failing here.
Is this the same issue as you described, does it replicate your (failing) experiments?

Copy link
Member Author

Choose a reason for hiding this comment

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

@Thanh-Binh ping, can you please have a look at this PR and the test here? I'm getting 0s from the Classifier inference and not sure why.



//timers
cout << "==============TIMERS============" << endl;
Expand All @@ -189,7 +206,7 @@ EPOCHS = 2; // make test faster in Debug
if(useSPlocal) cout << "SP (l):\t" << tSPloc.getElapsed()*1.0f << endl;
if(useSPglobal) cout << "SP (g):\t" << tSPglob.getElapsed() << endl;
if(useTM) cout << "TM:\t" << tTM.getElapsed() << endl;
cout << "AN:\t" << tAnLikelihood.getElapsed() << endl;
cout << "Cls:\t" << tCls.getElapsed() << endl;

// check deterministic SP, TM output
SDR goldEnc({DIM_INPUT});
Expand Down
2 changes: 1 addition & 1 deletion src/examples/hotgym/HelloSPTP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class BenchmarkHotgym {
);

//timers
Timer tInit, tAll, tRng, tEnc, tSPloc, tSPglob, tTM, tAnLikelihood;
Timer tInit, tAll, tRng, tEnc, tSPloc, tSPglob, tTM, tCls;
};

} //-ns
Expand Down
6 changes: 5 additions & 1 deletion src/htm/algorithms/TemporalMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ void TemporalMemory::reset(void) {
activeSegments_.clear();
matchingSegments_.clear();
segmentsValid_ = false;
tmAnomaly_.anomaly_ = -1.0f; //TODO reset rather to 0.5 as default (undecided) anomaly
tmAnomaly_.reset();
}

// ==============================
Expand Down Expand Up @@ -718,6 +718,10 @@ SynapseIdx TemporalMemory::getMaxSynapsesPerSegment() const {

UInt TemporalMemory::version() const { return TM_VERSION; }

void TemporalMemory::setAnomalyMode(ANMode mode) {
tmAnomaly_.reset();
tmAnomaly_.mode_ = mode;
}

static set<pair<CellIdx, SynapseIdx>>
getComparableSegmentSet(const Connections &connections,
Expand Down
22 changes: 17 additions & 5 deletions src/htm/algorithms/TemporalMemory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class TemporalMemory : public Serializable
* cells and winner cells of these external inputs must be given to methods
* TM.compute and TM.activateDendrites
*
* @param anomalyMode (optional, default `ANMode::RAW`)from enum ANMode, how is
* @param anomalyMode (optional, default `TemporalMemory::ANMode::RAW`)from enum ANMode, how is
* `TM.anomaly` computed. Options ANMode {DISABLED, RAW, LIKELIHOOD, LOGLIKELIHOOD}
*
*/
Expand All @@ -149,7 +149,7 @@ class TemporalMemory : public Serializable
SynapseIdx maxSynapsesPerSegment = 255,
bool checkInputs = true,
UInt externalPredictiveInputs = 0,
ANMode anomalyMode = ANMode::RAW
ANMode anomalyMode = TemporalMemory::ANMode::RAW
);

virtual void
Expand All @@ -169,7 +169,7 @@ class TemporalMemory : public Serializable
SynapseIdx maxSynapsesPerSegment = 255,
bool checkInputs = true,
UInt externalPredictiveInputs = 0,
ANMode anomalyMode = ANMode::RAW
ANMode anomalyMode = TemporalMemory::ANMode::RAW
);

virtual ~TemporalMemory();
Expand Down Expand Up @@ -677,13 +677,17 @@ class TemporalMemory : public Serializable
Real anomaly_ = 0.5f; //default value
ANMode mode_ = ANMode::RAW;
AnomalyLikelihood anomalyLikelihood_; //TODO provide default/customizable params here
};
void reset() {
anomaly_ = 0.5f;
mode_ = ANMode::RAW;
//TODO provide anomalyLikelihood_.reset();
Copy link
Member Author

Choose a reason for hiding this comment

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

when calling setAnomalyMode during a running sequence, the likelihood score would be broken, should reset. But reset is not available now for Likelihood (and I don't intend to implement it in this PR, so it stays broken).

}
} tmAnomaly_;

public:
Connections connections;
const UInt &externalPredictiveInputs = externalPredictiveInputs_;

anomaly_tm tmAnomaly_;
/*
* anomaly score computed for the current inputs
* (auto-updates after each call to TM::compute())
Expand All @@ -694,6 +698,14 @@ class TemporalMemory : public Serializable
const Real &anomaly = tmAnomaly_.anomaly_; //this is position dependant, the struct anomaly_tm must be defined before this use,
// otherwise this surprisingly compiles, but a call to `tmAnomaly_.anomaly` segfaults!

/**
* set new mode for TM.anomaly computation.
* This will reset existing anomaly.
*
* @param mode: Options TemporalMemory::ANMode {DISABLED, RAW, LIKELIHOOD, LOGLIKELIHOOD}
*/
void setAnomalyMode(ANMode mode);

};

} // namespace htm
Expand Down