Skip to content

Commit dde3263

Browse files
authored
Merge pull request #588 from htm-community/tm_anomaly_likelihood
TM anomaly: new modes: likelihood,...
2 parents 8892599 + f77bfdc commit dde3263

File tree

5 files changed

+107
-24
lines changed

5 files changed

+107
-24
lines changed

bindings/py/cpp_src/bindings/algorithms/py_TemporalMemory.cpp

+15-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ Example usage:
4747
TODO
4848
)");
4949

50+
py::enum_<TemporalMemory::ANMode>(m, "ANMode")
51+
.value("DISABLED", TemporalMemory::ANMode::DISABLED)
52+
.value("RAW", TemporalMemory::ANMode::RAW)
53+
.value("LIKELIHOOD", TemporalMemory::ANMode::LIKELIHOOD)
54+
.value("LOGLIKELIHOOD", TemporalMemory::ANMode::LOGLIKELIHOOD)
55+
.export_values();
56+
5057
py_HTM.def(py::init<>());
5158
py_HTM.def(py::init<std::vector<CellIdx>
5259
, CellIdx
@@ -62,7 +69,8 @@ Example usage:
6269
, SegmentIdx
6370
, SynapseIdx
6471
, bool
65-
, UInt>(),
72+
, UInt
73+
, TemporalMemory::ANMode>(),
6674
R"(Initialize the temporal memory (TM) using the given parameters.
6775
6876
Argument columnDimensions
@@ -123,6 +131,10 @@ Argument externalPredictiveInputs
123131
TemporalMemory. If this is given (and greater than 0) then the active
124132
cells and winner cells of these external inputs must be given to methods
125133
TM.compute and TM.activateDendrites
134+
135+
Argument anomalyMode (optional, default ANMode::RAW) selects mode for `TM.anomaly`.
136+
Options are ANMode {DISABLED, RAW, LIKELIHOOD, LOGLIKELIHOOD}
137+
126138
)"
127139
, py::arg("columnDimensions")
128140
, py::arg("cellsPerColumn") = 32
@@ -139,7 +151,8 @@ Argument externalPredictiveInputs
139151
, py::arg("maxSynapsesPerSegment") = 255
140152
, py::arg("checkInputs") = true
141153
, py::arg("externalPredictiveInputs") = 0u
142-
);
154+
, py::arg("anomalyMode") = TemporalMemory::ANMode::RAW
155+
);
143156

144157
py_HTM.def("printParameters",
145158
[](const HTM_t& self)

src/htm/algorithms/AnomalyLikelihood.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ AnomalyLikelihood::AnomalyLikelihood(UInt learningPeriod, UInt estimationSamples
2727
iteration_ = 0;
2828
NTA_CHECK(historicWindowSize >= estimationSamples); // cerr << "estimationSamples exceeds historicWindowSize";
2929
NTA_CHECK(aggregationWindow < reestimationPeriod && reestimationPeriod < historicWindowSize);
30+
NTA_WARN << "C++ AnomalyLikelihood may still need some testing.";
3031
}
3132

3233

src/htm/algorithms/TemporalMemory.cpp

+44-9
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,14 @@ TemporalMemory::TemporalMemory(
6666
SegmentIdx maxSegmentsPerCell,
6767
SynapseIdx maxSynapsesPerSegment,
6868
bool checkInputs,
69-
UInt externalPredictiveInputs) {
69+
UInt externalPredictiveInputs,
70+
ANMode anomalyMode) {
71+
7072
initialize(columnDimensions, cellsPerColumn, activationThreshold,
7173
initialPermanence, connectedPermanence, minThreshold,
7274
maxNewSynapseCount, permanenceIncrement, permanenceDecrement,
7375
predictedSegmentDecrement, seed, maxSegmentsPerCell,
74-
maxSynapsesPerSegment, checkInputs, externalPredictiveInputs);
76+
maxSynapsesPerSegment, checkInputs, externalPredictiveInputs, anomalyMode);
7577
}
7678

7779
TemporalMemory::~TemporalMemory() {}
@@ -91,7 +93,8 @@ void TemporalMemory::initialize(
9193
SegmentIdx maxSegmentsPerCell,
9294
SynapseIdx maxSynapsesPerSegment,
9395
bool checkInputs,
94-
UInt externalPredictiveInputs) {
96+
UInt externalPredictiveInputs,
97+
ANMode anomalyMode) {
9598

9699
// Validate all input parameters
97100
NTA_CHECK(columnDimensions.size() > 0) << "Number of column dimensions must be greater than 0";
@@ -132,6 +135,8 @@ void TemporalMemory::initialize(
132135
maxSegmentsPerCell_ = maxSegmentsPerCell;
133136
maxSynapsesPerSegment_ = maxSynapsesPerSegment;
134137

138+
tmAnomaly_.mode_ = anomalyMode;
139+
135140
reset();
136141
}
137142

@@ -483,15 +488,43 @@ void TemporalMemory::compute(const SDR &activeColumns,
483488
activateDendrites(learn, externalPredictiveInputsActive, externalPredictiveInputsWinners);
484489

485490
// Update Anomaly Metric. The anomaly is the percent of active columns that
486-
// were not predicted.
487-
anomaly_ = computeRawAnomalyScore(
488-
activeColumns,
489-
cellsToColumns( getPredictiveCells() ));
491+
// were not predicted.
492+
// Must be computed here, between `activateDendrites()` and `activateCells()`.
493+
switch(tmAnomaly_.mode_) {
494+
495+
case ANMode::DISABLED: {
496+
tmAnomaly_.anomaly_ = 0.5f;
497+
} break;
498+
499+
case ANMode::RAW: {
500+
tmAnomaly_.anomaly_ = computeRawAnomalyScore(
501+
activeColumns,
502+
cellsToColumns( getPredictiveCells() ));
503+
} break;
504+
505+
case ANMode::LIKELIHOOD: {
506+
const Real raw = computeRawAnomalyScore(
507+
activeColumns,
508+
cellsToColumns( getPredictiveCells() ));
509+
tmAnomaly_.anomaly_ = tmAnomaly_.anomalyLikelihood_.anomalyProbability(raw);
510+
} break;
511+
512+
case ANMode::LOGLIKELIHOOD: {
513+
const Real raw = computeRawAnomalyScore(
514+
activeColumns,
515+
cellsToColumns( getPredictiveCells() ));
516+
const Real like = tmAnomaly_.anomalyLikelihood_.anomalyProbability(raw);
517+
const Real log = tmAnomaly_.anomalyLikelihood_.computeLogLikelihood(like);
518+
tmAnomaly_.anomaly_ = log;
519+
} break;
490520
// TODO: Update mean & standard deviation of anomaly here.
521+
};
522+
NTA_ASSERT(tmAnomaly_.anomaly_ >= 0.0f and tmAnomaly_.anomaly_ <= 1.0f) << "TM.anomaly is out-of-bounds!";
491523

492524
activateCells(activeColumns, learn);
493525
}
494526

527+
495528
void TemporalMemory::compute(const SDR &activeColumns, const bool learn) {
496529
SDR externalPredictiveInputsActive({ externalPredictiveInputs_ });
497530
SDR externalPredictiveInputsWinners({ externalPredictiveInputs_ });
@@ -504,7 +537,7 @@ void TemporalMemory::reset(void) {
504537
activeSegments_.clear();
505538
matchingSegments_.clear();
506539
segmentsValid_ = false;
507-
anomaly_ = -1.0f;
540+
tmAnomaly_.anomaly_ = -1.0f; //TODO reset rather to 0.5 as default (undecided) anomaly
508541
}
509542

510543
// ==============================
@@ -713,7 +746,9 @@ bool TemporalMemory::operator==(const TemporalMemory &other) const {
713746
winnerCells_ != other.winnerCells_ ||
714747
maxSegmentsPerCell_ != other.maxSegmentsPerCell_ ||
715748
maxSynapsesPerSegment_ != other.maxSynapsesPerSegment_ ||
716-
anomaly_ != other.anomaly_ ) {
749+
tmAnomaly_.anomaly_ != other.tmAnomaly_.anomaly_ ||
750+
tmAnomaly_.mode_ != other.tmAnomaly_.mode_ ||
751+
tmAnomaly_.anomalyLikelihood_ != other.tmAnomaly_.anomalyLikelihood_ ) {
717752
return false;
718753
}
719754

src/htm/algorithms/TemporalMemory.hpp

+43-10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include <htm/types/Sdr.hpp>
2828
#include <htm/types/Serializable.hpp>
2929
#include <htm/utils/Random.hpp>
30+
#include <htm/algorithms/AnomalyLikelihood.hpp>
31+
3032
#include <vector>
3133

3234

@@ -35,6 +37,7 @@ namespace htm {
3537
using namespace std;
3638
using namespace htm;
3739

40+
3841
/**
3942
* Temporal Memory implementation in C++.
4043
*
@@ -53,6 +56,7 @@ using namespace htm;
5356
class TemporalMemory : public Serializable
5457
{
5558
public:
59+
enum class ANMode { DISABLED = 0, RAW = 1, LIKELIHOOD = 2, LOGLIKELIHOOD = 3};
5660
TemporalMemory();
5761

5862
/**
@@ -124,6 +128,10 @@ class TemporalMemory : public Serializable
124128
* TemporalMemory. If this is given (and greater than 0) then the active
125129
* cells and winner cells of these external inputs must be given to methods
126130
* TM.compute and TM.activateDendrites
131+
*
132+
* @param anomalyMode (optional, default `ANMode::RAW`)from enum ANMode, how is
133+
* `TM.anomaly` computed. Options ANMode {DISABLED, RAW, LIKELIHOOD, LOGLIKELIHOOD}
134+
*
127135
*/
128136
TemporalMemory(
129137
vector<CellIdx> columnDimensions,
@@ -140,13 +148,15 @@ class TemporalMemory : public Serializable
140148
SegmentIdx maxSegmentsPerCell = 255,
141149
SynapseIdx maxSynapsesPerSegment = 255,
142150
bool checkInputs = true,
143-
UInt externalPredictiveInputs = 0);
151+
UInt externalPredictiveInputs = 0,
152+
ANMode anomalyMode = ANMode::RAW
153+
);
144154

145155
virtual void
146156
initialize(
147-
vector<CellIdx> columnDimensions = {2048},
148-
CellIdx cellsPerColumn = 32,
149-
SynapseIdx activationThreshold = 13,
157+
vector<CellIdx> columnDimensions = {2048},
158+
CellIdx cellsPerColumn = 32,
159+
SynapseIdx activationThreshold = 13,
150160
Permanence initialPermanence = 0.21,
151161
Permanence connectedPermanence = 0.50,
152162
SynapseIdx minThreshold = 10,
@@ -158,7 +168,9 @@ class TemporalMemory : public Serializable
158168
SegmentIdx maxSegmentsPerCell = 255,
159169
SynapseIdx maxSynapsesPerSegment = 255,
160170
bool checkInputs = true,
161-
UInt externalPredictiveInputs = 0);
171+
UInt externalPredictiveInputs = 0,
172+
ANMode anomalyMode = ANMode::RAW
173+
);
162174

163175
virtual ~TemporalMemory();
164176

@@ -231,6 +243,10 @@ class TemporalMemory : public Serializable
231243
* the TemporalMemory via its compute method ensures that you'll always
232244
* be able to call getActiveCells at the end of the time step.
233245
*
246+
* Additionaly, this method computes anomaly for `TM.anomaly&`, if you
247+
* use other learning methods (activateCells(), activateDendrites()) your
248+
* anomaly scores will be off.
249+
*
234250
* @param activeColumns
235251
* Sorted SDR of active columns.
236252
*
@@ -467,7 +483,9 @@ class TemporalMemory : public Serializable
467483
CEREAL_NVP(activeCells_),
468484
CEREAL_NVP(winnerCells_),
469485
CEREAL_NVP(segmentsValid_),
470-
CEREAL_NVP(anomaly_),
486+
CEREAL_NVP(tmAnomaly_.anomaly_),
487+
CEREAL_NVP(tmAnomaly_.mode_),
488+
CEREAL_NVP(tmAnomaly_.anomalyLikelihood_),
471489
CEREAL_NVP(connections));
472490

473491
cereal::size_type numActiveSegments = activeSegments_.size();
@@ -522,7 +540,9 @@ class TemporalMemory : public Serializable
522540
CEREAL_NVP(activeCells_),
523541
CEREAL_NVP(winnerCells_),
524542
CEREAL_NVP(segmentsValid_),
525-
CEREAL_NVP(anomaly_),
543+
CEREAL_NVP(tmAnomaly_.anomaly_),
544+
CEREAL_NVP(tmAnomaly_.mode_),
545+
CEREAL_NVP(tmAnomaly_.anomalyLikelihood_),
526546
CEREAL_NVP(connections));
527547

528548
numActiveConnectedSynapsesForSegment_.assign(connections.segmentFlatListLength(), 0);
@@ -646,21 +666,34 @@ class TemporalMemory : public Serializable
646666
vector<SynapseIdx> numActiveConnectedSynapsesForSegment_;
647667
vector<SynapseIdx> numActivePotentialSynapsesForSegment_;
648668

649-
Real anomaly_;
650-
651669
Random rng_;
652670

671+
/**
672+
* holds logic and data for TM's anomaly
673+
*/
674+
struct anomaly_tm {
675+
protected:
676+
friend class TemporalMemory;
677+
Real anomaly_ = 0.5f; //default value
678+
ANMode mode_ = ANMode::RAW;
679+
AnomalyLikelihood anomalyLikelihood_; //TODO provide default/customizable params here
680+
};
681+
653682
public:
654683
Connections connections;
655684
const UInt &externalPredictiveInputs = externalPredictiveInputs_;
685+
686+
anomaly_tm tmAnomaly_;
656687
/*
657688
* anomaly score computed for the current inputs
658689
* (auto-updates after each call to TM::compute())
659690
*
660691
* @return a float value from computeRawAnomalyScore()
661692
* from Anomaly.hpp
662693
*/
663-
const Real &anomaly = anomaly_;
694+
const Real &anomaly = tmAnomaly_.anomaly_; //this is position dependant, the struct anomaly_tm must be defined before this use,
695+
// otherwise this surprisingly compiles, but a call to `tmAnomaly_.anomaly` segfaults!
696+
664697
};
665698

666699
} // namespace htm

src/test/unit/algorithms/ConnectionsPerformanceTest.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,11 @@ float runTemporalMemoryTest(UInt numColumns, UInt w, int numSequences, //TODO
7070

7171
// learn
7272
for (int i = 0; i < 5; i++) {
73-
for (auto sequence : sequences) {
74-
for (auto sdr : sequence) {
73+
for (const auto& sequence : sequences) {
74+
for (const auto& sdr : sequence) {
7575
tm.compute(sdr, true);
76-
avgAnomAfter = anom10.compute(tm.anomaly); //average anomaly score
76+
const Real an = tm.anomaly;
77+
avgAnomAfter = anom10.compute(an); //average anomaly score
7778
}
7879
tm.reset();
7980
}

0 commit comments

Comments
 (0)