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

Anomaly detection/combined scorer #1995

Merged
merged 7 commits into from
Mar 5, 2025
Merged

Conversation

ram-senth
Copy link
Member

No description provided.

@ram-senth ram-senth force-pushed the anomaly-detection/combined-scorer branch 8 times, most recently from 31be8fa to bbccf33 Compare February 28, 2025 07:20
@ram-senth ram-senth force-pushed the anomaly-detection/combined-scorer branch 2 times, most recently from 9a50bc9 to 62f6a83 Compare March 3, 2025 19:13
@ram-senth ram-senth marked this pull request as ready for review March 3, 2025 19:23
@ram-senth ram-senth requested a review from a team as a code owner March 3, 2025 19:23
Comment on lines 121 to 125
prophet_timestamps = np.array([None] * n_predictions)
prophet_ys = np.array([None] * n_predictions)
prophet_yhats = np.array([None] * n_predictions)
prophet_yhat_lowers = np.array([None] * n_predictions)
prophet_yhat_uppers = np.array([None] * n_predictions)
Copy link
Member

Choose a reason for hiding this comment

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

performance nit: would it be better to use np.full(n_predictions, None)? This would just create the array directly rather than performing a conversion

Copy link
Member Author

Choose a reason for hiding this comment

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

Think this was something you added? I just changed the values to None instead of 0.0 as 0.0 can affect scores.

Copy link
Member

Choose a reason for hiding this comment

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

Yes you're right I did add these to pass down the prophet related info. Was reviewing the _hydrate_alert function prior to this so was thinking of potential optimizations. I can add this as a followup PR if needed

Copy link
Member Author

Choose a reason for hiding this comment

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

It is fine, I can make the changes. It is a minor one.

Comment on lines 89 to 97
forecast_len = algo_config.prophet_forecast_len * (60 // config.time_period)
ts_internal = convert_external_ts_to_internal(timeseries)
prophet_df = prophet_detector.predict(
ts_internal.timestamps,
ts_internal.values,
forecast_len,
config.time_period,
config.sensitivity,
)
Copy link
Member

Choose a reason for hiding this comment

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

qq: Could we use the cached predictions in the db rather than re-running predict here?

Copy link
Member

Choose a reason for hiding this comment

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

and/or does forecast_len need to be > 0 in this case? Since specifically we just want the prophet predictions for what we currently have (history) and are not storing the future predictions in this function. I assume this would make this function slightly faster?

Copy link
Member Author

Choose a reason for hiding this comment

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

This method is called from both store_data and batch_predict calls. In the store_data path, the result from here is stored in DB. Agreed that the we do not need to predict for the batch_detection path but then that API is not being used actively in prod at this time. So I would not put it high on the list.

raise ServerError("No flags and scores from MP scorer")

if prophet_df is None or prophet_df.empty:
# logger.warning("The prophet_df is None or empty, skipping prophet scoring")
Copy link
Member

Choose a reason for hiding this comment

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

I think this log should still be included

Copy link
Member Author

Choose a reason for hiding this comment

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

This will cause a bunch of warnings in the case of combo_detect at present, where a batch detection is followed by a bunch of stream detection calls. I will be adding this back once I update that in a followup pr.

):
return prophet_flag

if (direction == "up" and y >= yhat_upper) or (direction == "down" and y <= yhat_lower):
Copy link
Member

Choose a reason for hiding this comment

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

Should this just be strictly greater/less than? I noticed thats what it was in the original LocationDetectors too. Probably doesn't make a large difference otherwise though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Doesn't make any noticeable difference.

Comment on lines 231 to 233
if pd.to_datetime(timestamp) in prophet_map["flag"]:
found += 1
pd_dt = pd.to_datetime(timestamp)
Copy link
Member

Choose a reason for hiding this comment

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

I believe it would be faster to convert the entire timestamp array to pd datetimes first then iterate over the converted values rather than converting each element within the loop

Copy link
Member Author

Choose a reason for hiding this comment

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

For batch detection we are looking up at most 96 timestamps and for streaming we are looking up 1 timestamp. The prediction data can be up to 2688 long. So I do not think updating the entire DataFrame will be more efficient.

and prophet_flag == "anomaly_higher_confidence"
):
flags.append("anomaly_higher_confidence")
elif prophet_score >= 2.0:
Copy link
Member

Choose a reason for hiding this comment

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

I noticed this threshold was >= 5.0 on the data-analysis notebook (I can't select the exact lines of code but if you cmd + f merge_prophet_mp_results its in that function). Is there a reason this was dropped to 2.0?

Copy link
Member Author

Choose a reason for hiding this comment

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

Wasn't that part of your recommendation from your experiments for that one timeseries with an extended flatline at 0? Or am I misremembering it? Regardless, all my experiments in these past few days that we evaluated together and with Tillman has been using 2.0.

Copy link
Member

Choose a reason for hiding this comment

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

No, I think my investigation for that was prior to us fixing the stream logic for the scoring. Since 2.0 seems to work then LGTM and we can adjust later if needed

Comment on lines 258 to 260
# todo: publish metrics for found/total
# if debug:
# print(f"found {found} out of {len(timestamps)}")
Copy link
Member

Choose a reason for hiding this comment

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

nits: Should this just be added now as a log.info()? Also extra debug comment?

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually I'm actively working on this. I enabled this log and noticed that there is a difference in how timestamps are treated by the code that retrieves data from DB, resulting in stream computations not finding the datapoint. Am trouble shooting it.

Copy link
Member

Choose a reason for hiding this comment

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

FWIW it may be the discrepancy in the timestamps stored in DB (seconds) vs the pd timestamps which I believe defaults to 'ns': https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html

ad_config: AnomalyDetectionConfig,
) -> FlagsAndScores:

# todo: return prophet thresholds
Copy link
Member

Choose a reason for hiding this comment

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

qq: I assume adding this would be a followup PR to support the UI changes for the confidence interval?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes

@@ -376,7 +376,6 @@ def ready_check(app_config: AppConfig = injected):
from seer.inference_models import models_loading_status

status = models_loading_status()
logger.info(f"Model loading status: {status}")
Copy link
Member

Choose a reason for hiding this comment

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

Why is this removed?

Copy link
Member Author

Choose a reason for hiding this comment

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

This was causing a log entry to be published every second, increasing the noise in logs.

Copy link
Member

Choose a reason for hiding this comment

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

Just asked jenn/rohan about this. It isnt used for autofix, but may be helpful for grouping?

Would it make more sense to push this log to a few lines below within the if statements (FAILED/DONE checks on lines 384-387)?

Copy link
Member Author

Choose a reason for hiding this comment

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

I do not think there is much value in this log. Let me know if it is helpful for grouping.

Copy link
Member

@aayush-se aayush-se Mar 3, 2025

Choose a reason for hiding this comment

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

Just reviewed the entire file this log can probably just be removed.

Copy link
Member

@aayush-se aayush-se left a comment

Choose a reason for hiding this comment

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

Looks great! No structural changes just had a couple of nits and questions.

Copy link
Member

@aayush-se aayush-se left a comment

Choose a reason for hiding this comment

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

Changes look good

@ram-senth ram-senth force-pushed the anomaly-detection/combined-scorer branch from aa95433 to 540a511 Compare March 4, 2025 21:23
@ram-senth ram-senth force-pushed the anomaly-detection/combined-scorer branch from 23b9384 to 8b85677 Compare March 5, 2025 18:01
@ram-senth ram-senth merged commit d787f75 into main Mar 5, 2025
22 checks passed
@ram-senth ram-senth deleted the anomaly-detection/combined-scorer branch March 5, 2025 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants