-
Notifications
You must be signed in to change notification settings - Fork 75
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
TM anomaly #406
TM anomaly #406
Conversation
Anomaly is computed in TM directly, not in Anomaly class
which gets called in all cases (TM::compute, TM::activateCells) so anomalyScore would be always available
keeping computeRawAnomaly tests + Likelihood tests
problem was in cellsToColumns() which expects a SDR, but onfortunately (known err) does not fail if vector is passed!
all code related to anomaly computation in TM now moved to internal struct TMAnomaly, with public TM.anomaly.score
serialize the new functionality in TMAnomaly
deprecated, use: - TM.anomaly.score for "normal" TM's anomaly, - computeRawAnomalyScore from Anomaly.hpp - AnomalyLikelihood
rework to split update() called after activateDendrites() and getScore() called when needed
not working, need to be fixed later
Update: I came with a workaround for
now, TM does not support anomaly for cases when extra inputs (param extra>0) are used. Otherwise anomaly runs just fine. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like these changes. I think there is a much simpler solution to this problem. Instead of removing the anomaly class and adding a different class, just reuse the existing anomaly class. The TM should keep an instance of the existing anomaly class and call Anomaly::compute
inside of TM::compute
, in between the calls to activateDendrites
and activateCells
which ensures that the TM always has valid predictions as well as the current feed forward input.
// Calculate and return percent of active columns that were not predicted. | ||
SDR both(active.dimensions); | ||
both.intersection(active, predicted); | ||
|
||
return (active.getSum() - both.getSum()) / Real(active.getSum()); | ||
} | ||
|
||
Real computeRawAnomalyScore(vector<UInt>& active, | ||
vector<UInt>& predicted) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we keep this overload?
This is where I keep my notes about how the SDR class should be doing set intersections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where I keep my notes about how the SDR class should be doing set intersections.
We could keep it,
or keep the code just as a commet, if you're not actively developing on that?
And ideally move the comment to SDR.intersection() ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I we can drop this method, but the rest of my criticism stands.
- Friend classes are confusing and usually not necessary.
- This doesn't include anomaly likelihood.
- External predictive inputs don't work? I read through the code and it looks like they should just work, if you remove the checks that disable it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of removing the anomaly class and adding a different class, just reuse the existing anomaly class.
I thought you wanted me to get rid of the old Anomaly class and replace it with as simple as possibleimplementation in TM (?), which I ended up liking.
- I'm OK to revive the old Anomaly class, with a few changes *).
- Or I can just move the code TMAnomaly to Anomaly.hpp and be done with it.
Which of those would you prefer?
Friend classes are confusing and usually not necessary.
I'll avoid 'friend access' when moving to a separate new/old file.
This doesn't include anomaly likelihood.
Yes, and it is intended. And about other refactoring in Anomaly class *):
- I've implemented Anomaly class in the past to simplify various options with anomaly (thresholds, likelihood, moving average,...)
- now with later usage experience, I don;t think it's a good design, and as those features can be implemented as "one liners" with current code, I don't think it should be provided with anomaly class. "Let TM.getAnomalyScore() be the simple raw anomaly" (#A), just conveniently computed. And let the user do what they need later with it.
thereshold
: simpleif(score > XXX) return 1 else return 0;
likelihood
:- (probably even does not truly work)
- in Anomaly had a FIXME note that it's not truly configurable. And should be separated.
- I could use the approach with abstract class
AbstractAnomaly::compute(SDR, SDR)
, but it's complicating things, see argument #A - AnomalyLikelihood is not even a true anomaly (computed from 2 SDRs), but a likelihood of anomalies, thus computed AFTER and from anomaly score. So
score = TM.getAnomalyScore(); Likelihood::anomalyProbability(score, timestep);
moving average
: again, simpleMovingAverage ma(5); ma.add(score); ma.get();
TL;DR: the proper configuration and ordering is too complicated in a wrapper Anomaly class, while relatively easy hand-tailored by user (with tools we provide).
External predictive inputs don't work? I read through the code and it looks like they should just work, if you remove the checks that disable it.
Externals break it in the way the they add "cell indices out of scope of the current TM" (idx in <numberOfCells() , numOfCells + extra>).
See for yourself in SDR::setSparseInplace(), you must use Debug build to trigger, that's why it's failing only on the OSX CI. I'm proposing to proceed as is, and discuss the fixes on external inputs in a separate issue or #442
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you prefer?
My goal is to have convenient way to print out everything interesting about the TM to a human readable string, and from it to determine if the TM is working. I see the anomaly metric as a diagnostic tool.
- I want to know the mean & standard deviation of the anomaly. The anomaly likelihood also uses this info.
- It should be enabled by default.
The proper configuration and ordering is too complicated.
Agreed,
- The user should be responsible for discarding anomaly readings when they haven't finished training the network.
- The user should be responsible for applying the threshold.
- I'm skeptical that the WEIGHTED mode can outperform the likelyhood mode. I don't understand the reasoning behind it, and it's not mentioned in the published literature. Maybe we could drop this too.
- AnomalyLikelihood needs 1 parameter to do what it does, and it has 4 or 5.
I would also like to use the anomaly likelyhood code in the TM class. The original publication of NAB (https://arxiv.org/pdf/1510.03336.pdf) uses the likelihood code. We could create a new API for this, like: class AbstractAnomaly{
virtual Real compute(const sdr::SDR& active,
const sdr::SDR& predicted) = nullptr;
}; Or typedef std::function<Real (const SDR&, const SDR&)> AbstractAnomaly; And then let the user specify the function (Anomaly or AnomalyLikelihood) and its parameters. Alternatively, if one of the three methods (Anomaly, AnomalyLikelihood, Weighted) is objectively the best then we should bake it into the TM, and not worry the users about it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no problems with these changes.
tm_->getActiveCells(sdr); | ||
tm_->getActiveCells(sdr); //active cells | ||
if (args_.orColumnOutputs) { //output as columns | ||
sdr = tm_->cellsToColumns(sdr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add another output to TMRegion to provide access to anomaly scores, etc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can decide this later when discussing "anomaly region", but yes, anomaly score would be moved to TM class, so it'll end up as a new part of existing TMRegion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkeeney if you have time, would you mind pushing a commit that adds a field "float anomalyScore" to TMRegion, and is computed from TM.getAnomalyScore()
, please?
I could do it but Regions are still not my cup of coffee.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understand. Yes, I can do this.
But rather than a field I suggest that it be a new output. That way, the Anomaly score can be passed to some other module after each iteration. It would be cool if we had a plot region to send it to.
If we just make it a field that can be queried then the user needs to add a callback to get it at each iteration. We can provide that as well but it would not be as useful.
I will take a look at the old .py code to see what the old python Anomaly Region did. I assume there was one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
But rather than a field I suggest that it be a new output. That way, the Anomaly score can be passed to some other module after each iteration.
Ok, this is the better solution. Would it be a problem that the output is different? (1 integer, instead of TM::getColumnDimensions(), or TM::getColumnDims + cellsPerColumn?
I will take a look at the old .py code to see what the old python Anomaly Region did. I assume there was one.
there wasn't any AnomalyRegion
need to save/load anomaly_ member
as its a part of TM now, tAnLikely still exists
for normal computation, constructor must always specify columnDimensions, only expection is TM tm; for deserialization, but that should never be used for compute()
removes old alternative with vectors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please review, this finishes the new TM.anomaly implementation from #445
and has some further fixes to TM.
@@ -180,6 +180,9 @@ using namespace nupic::algorithms::connections; | |||
|
|||
py_HTM.def_property_readonly("extra", [](const HTM_t &self) { return self.extra; } ); | |||
|
|||
py_HTM.def_property_readonly("anomaly", [](const HTM_t &self) { return self.anomaly; }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TM.anomaly py binding
@@ -1064,7 +1044,8 @@ bool TemporalMemory::operator==(const TemporalMemory &other) { | |||
winnerCells_ != other.winnerCells_ || | |||
maxSegmentsPerCell_ != other.maxSegmentsPerCell_ || | |||
maxSynapsesPerSegment_ != other.maxSynapsesPerSegment_ || | |||
iteration_ != other.iteration_) { | |||
iteration_ != other.iteration_ || | |||
anomaly_ != other.anomaly_ ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed operator ==, and op!= which was broken. added test
* | ||
* See TM::compute() for details of the parameters. | ||
* | ||
*/ | ||
void activateDendrites(const bool learn = true, | ||
const vector<UInt> &extraActive = {std::numeric_limits<UInt>::max()}, | ||
const vector<UInt> &extraWinners = {std::numeric_limits<UInt>::max()}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed the vector version of activateDendrites. All TM's public API now uses SDR
@@ -472,6 +474,7 @@ using namespace nupic::algorithms::connections; | |||
CEREAL_NVP(activeCells_), | |||
CEREAL_NVP(winnerCells_), | |||
CEREAL_NVP(segmentsValid_), | |||
CEREAL_NVP(anomaly_), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed serialization for new TM.anomaly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me!
Thank you, David! I'm quite happy with the Two remaining issues:
|
I assume you still want this new function. |
yes, but now it's called just
I agree with your suggestion that output would be a better choice. |
Ok, I am creating a new PR to do this. |
For Outputs we currently have:
With all of the recent changes, is this still the expected outputs from TMRegion. |
yes.
might change name to
|
This is not 100% correct. When a mini-column bursts because it was unpredicted (this is an anomaly!), then a winner cell is arbitrarily chosen. |
Changing the name of predictedActiveCells will break the API. But we are changing so many other things, now is probably a good time to change this as well. I was going to get this out in just a few min but but with these name changes this will take longer. My wife says I need to go wash the windows...company coming next week. I will try to finish this PR later today. |
hmm, we wanted to keep NetworkAPI as stable as possible. I'm not sure what @ctrl-z-9000-times meant with the comment
if it's just the comment about name vs winner cells, we should keep the old output name, and add this note to the description. |
Ok, windows are washed. On to more interesting things...
|
What comment should I add to the Also, The vector overload of activateDendrites( ) does not appear to be implemented.
|
"Cells predicted to be active in the next step" ?
yes, there's only SDR version now. activateDendrites(bool), or activateDendrites(bool, SDR, SDR) |
oh, just needed to pull from master. |
Anomaly is computed in TM directly, not in Anomaly class
TODO:
waiting for TM activeCells_ is a SDR #442 to resolve issue with extraActive, causing SDR to fail. Triggered by "testExtraActive " testFixes #347