Skip to content

Commit bcd02cc

Browse files
authored
[ML] Correct query times for model plot and forecast (#328)
Backport #327.
1 parent 40ece70 commit bcd02cc

File tree

6 files changed

+60
-28
lines changed

6 files changed

+60
-28
lines changed

docs/CHANGELOG.asciidoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@
2828
2929
//=== Regressions
3030
31+
== {es} version 6.5.3
32+
33+
=== Bug Fixes
34+
35+
Correct query times for model plot and forecast in the bucket to match the times we assign
36+
the samples we add to the model for each bucket. For long bucket lengths, this could result
37+
in apparently shifted model plot with respect to the data and increased errors in forecasts.
38+
3139
== {es} version 6.5.0
3240
3341
//=== Breaking Changes

include/api/CForecastRunner.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class API_EXPORT CForecastRunner final : private core::CNonCopyable {
112112
using TAnomalyDetectorPtr = std::shared_ptr<model::CAnomalyDetector>;
113113
using TAnomalyDetectorPtrVec = std::vector<TAnomalyDetectorPtr>;
114114

115-
using TForecastModelWrapper = model::CForecastDataSink::SForecastModelWrapper;
115+
using TForecastModelWrapper = model::CForecastDataSink::CForecastModelWrapper;
116116
using TForecastResultSeries = model::CForecastDataSink::SForecastResultSeries;
117117
using TForecastResultSeriesVec = std::vector<TForecastResultSeries>;
118118
using TMathsModelPtr = std::unique_ptr<maths::CModel>;

include/model/CForecastDataSink.h

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,31 @@ class MODEL_EXPORT CForecastDataSink final : private core::CNonCopyable {
4040
public:
4141
using TMathsModelPtr = std::shared_ptr<maths::CModel>;
4242
using TStrUMap = boost::unordered_set<std::string>;
43+
struct SForecastResultSeries;
4344

4445
//! Wrapper for 1 timeseries model, its feature and by Field
45-
struct MODEL_EXPORT SForecastModelWrapper {
46-
SForecastModelWrapper(model_t::EFeature feature,
46+
class MODEL_EXPORT CForecastModelWrapper {
47+
public:
48+
CForecastModelWrapper(model_t::EFeature feature,
4749
TMathsModelPtr&& forecastModel,
4850
const std::string& byFieldValue);
4951

50-
SForecastModelWrapper(SForecastModelWrapper&& other);
52+
CForecastModelWrapper(CForecastModelWrapper&& other);
5153

52-
SForecastModelWrapper(const SForecastModelWrapper& that) = delete;
53-
SForecastModelWrapper& operator=(const SForecastModelWrapper&) = delete;
54+
CForecastModelWrapper(const CForecastModelWrapper& that) = delete;
55+
CForecastModelWrapper& operator=(const CForecastModelWrapper&) = delete;
5456

55-
model_t::EFeature s_Feature;
56-
TMathsModelPtr s_ForecastModel;
57-
std::string s_ByFieldValue;
57+
bool forecast(const SForecastResultSeries& series,
58+
core_t::TTime startTime,
59+
core_t::TTime endTime,
60+
double boundsPercentile,
61+
CForecastDataSink& sink,
62+
std::string& message) const;
63+
64+
private:
65+
model_t::EFeature m_Feature;
66+
TMathsModelPtr m_ForecastModel;
67+
std::string m_ByFieldValue;
5868
};
5969

6070
//! Everything that defines 1 series of forecasts
@@ -68,7 +78,7 @@ class MODEL_EXPORT CForecastDataSink final : private core::CNonCopyable {
6878

6979
SModelParams s_ModelParams;
7080
int s_DetectorIndex;
71-
std::vector<SForecastModelWrapper> s_ToForecast;
81+
std::vector<CForecastModelWrapper> s_ToForecast;
7282
std::string s_ToForecastPersisted;
7383
std::string s_PartitionFieldName;
7484
std::string s_PartitionFieldValue;

lib/api/CForecastRunner.cc

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,17 +169,10 @@ void CForecastRunner::forecastWorker() {
169169
}
170170
}
171171

172-
const TForecastModelWrapper& model = series.s_ToForecast.back();
173-
model_t::TDouble1VecDouble1VecPr support =
174-
model_t::support(model.s_Feature);
175-
bool success = model.s_ForecastModel->forecast(
176-
forecastJob.s_StartTime, forecastJob.forecastEnd(),
177-
forecastJob.s_BoundsPercentile, support.first, support.second,
178-
boost::bind(&model::CForecastDataSink::push, &sink, _1,
179-
model_t::print(model.s_Feature), series.s_PartitionFieldName,
180-
series.s_PartitionFieldValue, series.s_ByFieldName,
181-
model.s_ByFieldValue, series.s_DetectorIndex),
182-
message);
172+
const TForecastModelWrapper& model{series.s_ToForecast.back()};
173+
bool success{model.forecast(
174+
series, forecastJob.s_StartTime, forecastJob.forecastEnd(),
175+
forecastJob.s_BoundsPercentile, sink, message)};
183176
series.s_ToForecast.pop_back();
184177

185178
if (success == false) {

lib/model/CForecastDataSink.cc

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <core/CLogger.h>
1010
#include <core/CScopedRapidJsonPoolAllocator.h>
1111

12+
#include <boost/bind.hpp>
13+
1214
#include <vector>
1315

1416
namespace ml {
@@ -54,16 +56,34 @@ const std::string CForecastDataSink::STATUS("forecast_status");
5456

5557
using TScopedAllocator = core::CScopedRapidJsonPoolAllocator<core::CRapidJsonConcurrentLineWriter>;
5658

57-
CForecastDataSink::SForecastModelWrapper::SForecastModelWrapper(model_t::EFeature feature,
59+
CForecastDataSink::CForecastModelWrapper::CForecastModelWrapper(model_t::EFeature feature,
5860
TMathsModelPtr&& forecastModel,
5961
const std::string& byFieldValue)
60-
: s_Feature(feature), s_ForecastModel(std::move(forecastModel)),
61-
s_ByFieldValue(byFieldValue) {
62+
: m_Feature(feature), m_ForecastModel(std::move(forecastModel)),
63+
m_ByFieldValue(byFieldValue) {
64+
}
65+
66+
CForecastDataSink::CForecastModelWrapper::CForecastModelWrapper(CForecastModelWrapper&& other)
67+
: m_Feature(other.m_Feature), m_ForecastModel(std::move(other.m_ForecastModel)),
68+
m_ByFieldValue(std::move(other.m_ByFieldValue)) {
6269
}
6370

64-
CForecastDataSink::SForecastModelWrapper::SForecastModelWrapper(SForecastModelWrapper&& other)
65-
: s_Feature(other.s_Feature), s_ForecastModel(std::move(other.s_ForecastModel)),
66-
s_ByFieldValue(std::move(other.s_ByFieldValue)) {
71+
bool CForecastDataSink::CForecastModelWrapper::forecast(const SForecastResultSeries& series,
72+
core_t::TTime startTime,
73+
core_t::TTime endTime,
74+
double boundsPercentile,
75+
CForecastDataSink& sink,
76+
std::string& message) const {
77+
core_t::TTime bucketLength{m_ForecastModel->params().bucketLength()};
78+
startTime = model_t::sampleTime(m_Feature, startTime, bucketLength);
79+
endTime = model_t::sampleTime(m_Feature, endTime, bucketLength);
80+
model_t::TDouble1VecDouble1VecPr support{model_t::support(m_Feature)};
81+
return m_ForecastModel->forecast(
82+
startTime, endTime, boundsPercentile, support.first, support.second,
83+
boost::bind(&model::CForecastDataSink::push, &sink, _1, model_t::print(m_Feature),
84+
series.s_PartitionFieldName, series.s_PartitionFieldValue,
85+
series.s_ByFieldName, m_ByFieldValue, series.s_DetectorIndex),
86+
message);
6787
}
6888

6989
CForecastDataSink::SForecastResultSeries::SForecastResultSeries(const SModelParams& modelParams)

lib/model/CModelDetailsView.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,12 @@ void CModelDetailsView::modelPlotForByFieldId(core_t::TTime time,
7777

7878
if (this->isByFieldIdActive(byFieldId)) {
7979
const maths::CModel* model = this->model(feature, byFieldId);
80-
if (!model) {
80+
if (model == nullptr) {
8181
return;
8282
}
8383

8484
std::size_t dimension = model_t::dimension(feature);
85+
time = model_t::sampleTime(feature, time, model->params().bucketLength());
8586

8687
maths_t::TDouble2VecWeightsAry weights(
8788
maths_t::CUnitWeights::unit<TDouble2Vec>(dimension));

0 commit comments

Comments
 (0)