Skip to content

Commit

Permalink
Merge pull request #588 from htm-community/tm_anomaly_likelihood
Browse files Browse the repository at this point in the history
TM anomaly: new modes: likelihood,...
  • Loading branch information
breznak authored Aug 10, 2019
2 parents 8892599 + f77bfdc commit dde3263
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 24 deletions.
17 changes: 15 additions & 2 deletions bindings/py/cpp_src/bindings/algorithms/py_TemporalMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ Example usage:
TODO
)");

py::enum_<TemporalMemory::ANMode>(m, "ANMode")
.value("DISABLED", TemporalMemory::ANMode::DISABLED)
.value("RAW", TemporalMemory::ANMode::RAW)
.value("LIKELIHOOD", TemporalMemory::ANMode::LIKELIHOOD)
.value("LOGLIKELIHOOD", TemporalMemory::ANMode::LOGLIKELIHOOD)
.export_values();

py_HTM.def(py::init<>());
py_HTM.def(py::init<std::vector<CellIdx>
, CellIdx
Expand All @@ -62,7 +69,8 @@ Example usage:
, SegmentIdx
, SynapseIdx
, bool
, UInt>(),
, UInt
, TemporalMemory::ANMode>(),
R"(Initialize the temporal memory (TM) using the given parameters.
Argument columnDimensions
Expand Down Expand Up @@ -123,6 +131,10 @@ Argument externalPredictiveInputs
TemporalMemory. If this is given (and greater than 0) then the active
cells and winner cells of these external inputs must be given to methods
TM.compute and TM.activateDendrites
Argument anomalyMode (optional, default ANMode::RAW) selects mode for `TM.anomaly`.
Options are ANMode {DISABLED, RAW, LIKELIHOOD, LOGLIKELIHOOD}
)"
, py::arg("columnDimensions")
, py::arg("cellsPerColumn") = 32
Expand All @@ -139,7 +151,8 @@ Argument externalPredictiveInputs
, py::arg("maxSynapsesPerSegment") = 255
, py::arg("checkInputs") = true
, py::arg("externalPredictiveInputs") = 0u
);
, py::arg("anomalyMode") = TemporalMemory::ANMode::RAW
);

py_HTM.def("printParameters",
[](const HTM_t& self)
Expand Down
1 change: 1 addition & 0 deletions src/htm/algorithms/AnomalyLikelihood.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ AnomalyLikelihood::AnomalyLikelihood(UInt learningPeriod, UInt estimationSamples
iteration_ = 0;
NTA_CHECK(historicWindowSize >= estimationSamples); // cerr << "estimationSamples exceeds historicWindowSize";
NTA_CHECK(aggregationWindow < reestimationPeriod && reestimationPeriod < historicWindowSize);
NTA_WARN << "C++ AnomalyLikelihood may still need some testing.";
}


Expand Down
53 changes: 44 additions & 9 deletions src/htm/algorithms/TemporalMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ TemporalMemory::TemporalMemory(
SegmentIdx maxSegmentsPerCell,
SynapseIdx maxSynapsesPerSegment,
bool checkInputs,
UInt externalPredictiveInputs) {
UInt externalPredictiveInputs,
ANMode anomalyMode) {

initialize(columnDimensions, cellsPerColumn, activationThreshold,
initialPermanence, connectedPermanence, minThreshold,
maxNewSynapseCount, permanenceIncrement, permanenceDecrement,
predictedSegmentDecrement, seed, maxSegmentsPerCell,
maxSynapsesPerSegment, checkInputs, externalPredictiveInputs);
maxSynapsesPerSegment, checkInputs, externalPredictiveInputs, anomalyMode);
}

TemporalMemory::~TemporalMemory() {}
Expand All @@ -91,7 +93,8 @@ void TemporalMemory::initialize(
SegmentIdx maxSegmentsPerCell,
SynapseIdx maxSynapsesPerSegment,
bool checkInputs,
UInt externalPredictiveInputs) {
UInt externalPredictiveInputs,
ANMode anomalyMode) {

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

tmAnomaly_.mode_ = anomalyMode;

reset();
}

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

// Update Anomaly Metric. The anomaly is the percent of active columns that
// were not predicted.
anomaly_ = computeRawAnomalyScore(
activeColumns,
cellsToColumns( getPredictiveCells() ));
// were not predicted.
// Must be computed here, between `activateDendrites()` and `activateCells()`.
switch(tmAnomaly_.mode_) {

case ANMode::DISABLED: {
tmAnomaly_.anomaly_ = 0.5f;
} break;

case ANMode::RAW: {
tmAnomaly_.anomaly_ = computeRawAnomalyScore(
activeColumns,
cellsToColumns( getPredictiveCells() ));
} break;

case ANMode::LIKELIHOOD: {
const Real raw = computeRawAnomalyScore(
activeColumns,
cellsToColumns( getPredictiveCells() ));
tmAnomaly_.anomaly_ = tmAnomaly_.anomalyLikelihood_.anomalyProbability(raw);
} break;

case ANMode::LOGLIKELIHOOD: {
const Real raw = computeRawAnomalyScore(
activeColumns,
cellsToColumns( getPredictiveCells() ));
const Real like = tmAnomaly_.anomalyLikelihood_.anomalyProbability(raw);
const Real log = tmAnomaly_.anomalyLikelihood_.computeLogLikelihood(like);
tmAnomaly_.anomaly_ = log;
} break;
// TODO: Update mean & standard deviation of anomaly here.
};
NTA_ASSERT(tmAnomaly_.anomaly_ >= 0.0f and tmAnomaly_.anomaly_ <= 1.0f) << "TM.anomaly is out-of-bounds!";

activateCells(activeColumns, learn);
}


void TemporalMemory::compute(const SDR &activeColumns, const bool learn) {
SDR externalPredictiveInputsActive({ externalPredictiveInputs_ });
SDR externalPredictiveInputsWinners({ externalPredictiveInputs_ });
Expand All @@ -504,7 +537,7 @@ void TemporalMemory::reset(void) {
activeSegments_.clear();
matchingSegments_.clear();
segmentsValid_ = false;
anomaly_ = -1.0f;
tmAnomaly_.anomaly_ = -1.0f; //TODO reset rather to 0.5 as default (undecided) anomaly
}

// ==============================
Expand Down Expand Up @@ -713,7 +746,9 @@ bool TemporalMemory::operator==(const TemporalMemory &other) const {
winnerCells_ != other.winnerCells_ ||
maxSegmentsPerCell_ != other.maxSegmentsPerCell_ ||
maxSynapsesPerSegment_ != other.maxSynapsesPerSegment_ ||
anomaly_ != other.anomaly_ ) {
tmAnomaly_.anomaly_ != other.tmAnomaly_.anomaly_ ||
tmAnomaly_.mode_ != other.tmAnomaly_.mode_ ||
tmAnomaly_.anomalyLikelihood_ != other.tmAnomaly_.anomalyLikelihood_ ) {
return false;
}

Expand Down
53 changes: 43 additions & 10 deletions src/htm/algorithms/TemporalMemory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include <htm/types/Sdr.hpp>
#include <htm/types/Serializable.hpp>
#include <htm/utils/Random.hpp>
#include <htm/algorithms/AnomalyLikelihood.hpp>

#include <vector>


Expand All @@ -35,6 +37,7 @@ namespace htm {
using namespace std;
using namespace htm;


/**
* Temporal Memory implementation in C++.
*
Expand All @@ -53,6 +56,7 @@ using namespace htm;
class TemporalMemory : public Serializable
{
public:
enum class ANMode { DISABLED = 0, RAW = 1, LIKELIHOOD = 2, LOGLIKELIHOOD = 3};
TemporalMemory();

/**
Expand Down Expand Up @@ -124,6 +128,10 @@ class TemporalMemory : public Serializable
* TemporalMemory. If this is given (and greater than 0) then the active
* 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
* `TM.anomaly` computed. Options ANMode {DISABLED, RAW, LIKELIHOOD, LOGLIKELIHOOD}
*
*/
TemporalMemory(
vector<CellIdx> columnDimensions,
Expand All @@ -140,13 +148,15 @@ class TemporalMemory : public Serializable
SegmentIdx maxSegmentsPerCell = 255,
SynapseIdx maxSynapsesPerSegment = 255,
bool checkInputs = true,
UInt externalPredictiveInputs = 0);
UInt externalPredictiveInputs = 0,
ANMode anomalyMode = ANMode::RAW
);

virtual void
initialize(
vector<CellIdx> columnDimensions = {2048},
CellIdx cellsPerColumn = 32,
SynapseIdx activationThreshold = 13,
vector<CellIdx> columnDimensions = {2048},
CellIdx cellsPerColumn = 32,
SynapseIdx activationThreshold = 13,
Permanence initialPermanence = 0.21,
Permanence connectedPermanence = 0.50,
SynapseIdx minThreshold = 10,
Expand All @@ -158,7 +168,9 @@ class TemporalMemory : public Serializable
SegmentIdx maxSegmentsPerCell = 255,
SynapseIdx maxSynapsesPerSegment = 255,
bool checkInputs = true,
UInt externalPredictiveInputs = 0);
UInt externalPredictiveInputs = 0,
ANMode anomalyMode = ANMode::RAW
);

virtual ~TemporalMemory();

Expand Down Expand Up @@ -231,6 +243,10 @@ class TemporalMemory : public Serializable
* the TemporalMemory via its compute method ensures that you'll always
* be able to call getActiveCells at the end of the time step.
*
* Additionaly, this method computes anomaly for `TM.anomaly&`, if you
* use other learning methods (activateCells(), activateDendrites()) your
* anomaly scores will be off.
*
* @param activeColumns
* Sorted SDR of active columns.
*
Expand Down Expand Up @@ -467,7 +483,9 @@ class TemporalMemory : public Serializable
CEREAL_NVP(activeCells_),
CEREAL_NVP(winnerCells_),
CEREAL_NVP(segmentsValid_),
CEREAL_NVP(anomaly_),
CEREAL_NVP(tmAnomaly_.anomaly_),
CEREAL_NVP(tmAnomaly_.mode_),
CEREAL_NVP(tmAnomaly_.anomalyLikelihood_),
CEREAL_NVP(connections));

cereal::size_type numActiveSegments = activeSegments_.size();
Expand Down Expand Up @@ -522,7 +540,9 @@ class TemporalMemory : public Serializable
CEREAL_NVP(activeCells_),
CEREAL_NVP(winnerCells_),
CEREAL_NVP(segmentsValid_),
CEREAL_NVP(anomaly_),
CEREAL_NVP(tmAnomaly_.anomaly_),
CEREAL_NVP(tmAnomaly_.mode_),
CEREAL_NVP(tmAnomaly_.anomalyLikelihood_),
CEREAL_NVP(connections));

numActiveConnectedSynapsesForSegment_.assign(connections.segmentFlatListLength(), 0);
Expand Down Expand Up @@ -646,21 +666,34 @@ class TemporalMemory : public Serializable
vector<SynapseIdx> numActiveConnectedSynapsesForSegment_;
vector<SynapseIdx> numActivePotentialSynapsesForSegment_;

Real anomaly_;

Random rng_;

/**
* holds logic and data for TM's anomaly
*/
struct anomaly_tm {
protected:
friend class TemporalMemory;
Real anomaly_ = 0.5f; //default value
ANMode mode_ = ANMode::RAW;
AnomalyLikelihood anomalyLikelihood_; //TODO provide default/customizable params here
};

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())
*
* @return a float value from computeRawAnomalyScore()
* from Anomaly.hpp
*/
const Real &anomaly = anomaly_;
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!

};

} // namespace htm
Expand Down
7 changes: 4 additions & 3 deletions src/test/unit/algorithms/ConnectionsPerformanceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ float runTemporalMemoryTest(UInt numColumns, UInt w, int numSequences, //TODO

// learn
for (int i = 0; i < 5; i++) {
for (auto sequence : sequences) {
for (auto sdr : sequence) {
for (const auto& sequence : sequences) {
for (const auto& sdr : sequence) {
tm.compute(sdr, true);
avgAnomAfter = anom10.compute(tm.anomaly); //average anomaly score
const Real an = tm.anomaly;
avgAnomAfter = anom10.compute(an); //average anomaly score
}
tm.reset();
}
Expand Down

0 comments on commit dde3263

Please sign in to comment.