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

New detector: naive WIP #4

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
14 changes: 14 additions & 0 deletions config/thresholds.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@
"threshold": 1.0
}
},
"naive": {
"reward_low_FN_rate": {
"score": -232.0,
"threshold": 1.1
},
"reward_low_FP_rate": {
"score": -116.0,
"threshold": 1.1
},
"standard": {
"score": -116.0,
"threshold": 1.1
}
},
"null": {
"reward_low_FN_rate": {
"score": -232.0,
Expand Down
Empty file added nab/detectors/naive/__init__.py
Empty file.
90 changes: 90 additions & 0 deletions nab/detectors/naive/naive_detector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# ----------------------------------------------------------------------
# Copyright (C) 2015, Numenta, Inc. Unless you have an agreement
# with Numenta, Inc., for a separate license for this software code, the
# following terms and conditions apply:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero Public License for more details.
#
# You should have received a copy of the GNU Affero Public License
# along with this program. If not, see http://www.gnu.org/licenses.
#
# http://numenta.org/licenses/
# ----------------------------------------------------------------------

import math #exp

from nab.detectors.base import AnomalyDetector

EPSILON = 0.00000001

class NaiveDetector(AnomalyDetector):
"""
This is implementation of the "naive forecast", aka "random walk forecasting",
which is a baseline algorithm for time-series forecasting.
It predicts the last seen value. So `Prediction(t+1) = Input(t)`.
Copy link
Member Author

Choose a reason for hiding this comment

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

Naive, random-walk is a simple predictor


Hyperparameter to optimize is @param coef in `initialize`.
"""


def initialize(self, coef=10.0):
"""
@param `coef` for the activation function that scales anomaly score to [0, 1.0]
The function is: `anomalyScore = 1-exp(-coef*x)`, where
`x=abs(current - predicted)/predicted`.
"""
super().initialize()
self.predicted = 0.0 #previous value, last seen
self.coef = coef


def handleRecord(self, inputData):
"""The predicted value is simply the last seen value,
Anomaly score is computed as a function of current,predicted.

See @ref `initialize` param `coef`.
"""
current = float(inputData["value"])
inputData['predicted'] = self.predicted
try:
anomalyScore = self.anomalyFn_(current, self.predicted)
except:
#on any math error (overflow,...), we mark this as anomaly. tough love
anomalyScore = 1.0

ret = [anomalyScore, self.predicted]
self.predicted=current
return (ret)


def getAdditionalHeaders(self):
return ['predicted']


def anomalyFn_(self, current, predicted):
"""
compute anomaly score from 2 scalars
"""
if predicted == 0.0:
predicted = EPSILON #avoid division by zero

# the computation
x = abs(current - predicted)/predicted
score = 1-math.exp(-self.coef * x)
Copy link
Member Author

Choose a reason for hiding this comment

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

is there a better way to get anomaly score from predictions?
This a) crashes sometimes, b) does not provide good AD


# bound to anomaly range (should not happen, but some are over)
# if(score > 1):
# score = 1.0
# elif(score < 0):
# score = 0.0

assert(score >= 0 and score <= 1), print("ERR: score: "+str(score)+ "\t curr: "+str(current)+"\t pred: "+str(predicted))
return score

5 changes: 5 additions & 0 deletions results/final_results.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"reward_low_FP_rate": 43.40851703291233,
"standard": 57.99434335898808
},
"naive": {
"reward_low_FN_rate": 0.0,
"reward_low_FP_rate": 0.0,
"standard": 0.0
},
"null": {
"reward_low_FN_rate": 0.0,
"reward_low_FP_rate": 0.0,
Expand Down
4 changes: 3 additions & 1 deletion run.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def main(args):
type=str,
default=["numenta", "numentaTM", "htmcore", "htmjava", "null", "random",
"bayesChangePt", "windowedGaussian", "expose",
"relativeEntropy", "earthgeckoSkyline"],
"relativeEntropy", "earthgeckoSkyline", "naive"],
help="Comma separated list of detector(s) to use, e.g. "
"null,numenta")

Expand Down Expand Up @@ -227,6 +227,8 @@ def main(args):
ContextOSEDetector )
if "earthgeckoSkyline" in args.detectors:
from nab.detectors.earthgecko_skyline.earthgecko_skyline_detector import EarthgeckoSkylineDetector
if "naive" in args.detectors:
from nab.detectors.naive.naive_detector import NaiveDetector
if "htmcore" in args.detectors:
from nab.detectors.htmcore.htmcore_detector import HtmcoreDetector

Expand Down