Browse Source

seasonality iterations

anomly-detector-configurable-iteration-steps-#35
Alexey Velikiy 2 years ago
parent
commit
b003f1ee17
  1. 8
      client/src/views/Home.vue
  2. 19
      server/src/services/analytic_service/analytic_unit/anomaly_analytic_unit.rs
  3. 5
      server/src/services/analytic_service/analytic_unit/types.rs

8
client/src/views/Home.vue

@ -41,6 +41,8 @@
<input :value="analyticUnitConfig.confidence" @change="confidenceChange" /> <br/> <input :value="analyticUnitConfig.confidence" @change="confidenceChange" /> <br/>
Seasonality: Seasonality:
<input :value="analyticUnitConfig.seasonality" @change="seasonalityChange" /> <br/> <input :value="analyticUnitConfig.seasonality" @change="seasonalityChange" /> <br/>
Seasonality iterations:
<input :value="analyticUnitConfig.seasonality_iterations" @change="seasonalityIterationsChange" /> <br/>
<br/> <br/>
</div> </div>
</div> </div>
@ -117,7 +119,11 @@ export default defineComponent({
cfg.seasonality = parseFloat(e.target.value); cfg.seasonality = parseFloat(e.target.value);
this.$store.dispatch('patchConfig', { Anomaly: cfg }); this.$store.dispatch('patchConfig', { Anomaly: cfg });
}, },
seasonalityIterationsChange(e) {
let cfg = _.clone(this.analyticUnitConfig);
cfg.seasonality_iterations = Math.ceil(e.target.value);
this.$store.dispatch('patchConfig', { Anomaly: cfg });
},
}, },
data: function () { data: function () {
return { return {

19
server/src/services/analytic_service/analytic_unit/anomaly_analytic_unit.rs

@ -10,7 +10,6 @@ use chrono::prelude::*;
// TODO: move to config // TODO: move to config
const DETECTION_STEP: u64 = 10; const DETECTION_STEP: u64 = 10;
const SEASONALITY_ITERATIONS: u64 = 3; // TODO: better name
// timerange offset in seconds backwards from end of ts in assumption that ts has no gaps // timerange offset in seconds backwards from end of ts in assumption that ts has no gaps
fn get_value_with_offset(ts: &Vec<(u64, f64)>, offset: u64) -> Option<(u64, f64)>{ fn get_value_with_offset(ts: &Vec<(u64, f64)>, offset: u64) -> Option<(u64, f64)>{
@ -30,14 +29,16 @@ struct SARIMA {
pub ts: Vec<(u64, f64)>, pub ts: Vec<(u64, f64)>,
pub seasonality: u64, pub seasonality: u64,
pub confidence: f64, pub confidence: f64,
pub seasonality_iterations: u64
} }
impl SARIMA { impl SARIMA {
pub fn new(seasonality: u64, confidence: f64) -> SARIMA { pub fn new(seasonality: u64, confidence: f64, seasonality_iterations: u64) -> SARIMA {
return SARIMA { return SARIMA {
ts: Vec::new(), ts: Vec::new(),
seasonality, seasonality,
confidence confidence,
seasonality_iterations
}; };
} }
@ -56,17 +57,17 @@ impl SARIMA {
let to = ts.last().unwrap().0; let to = ts.last().unwrap().0;
let iter_steps = (self.seasonality / DETECTION_STEP) as usize; let iter_steps = (self.seasonality / DETECTION_STEP) as usize;
if to - from != SEASONALITY_ITERATIONS * self.seasonality { if to - from != self.seasonality_iterations * self.seasonality {
return Err(anyhow::format_err!("timeserie to learn from should be {} * sasonality", SEASONALITY_ITERATIONS)); return Err(anyhow::format_err!("timeserie to learn from should be {} * sasonality", self.seasonality_iterations));
} }
for k in 0..iter_steps { for k in 0..iter_steps {
let mut vts = Vec::new(); let mut vts = Vec::new();
for si in 0..SEASONALITY_ITERATIONS { for si in 0..self.seasonality_iterations {
vts.push(ts[k + iter_steps * si as usize].1); vts.push(ts[k + iter_steps * si as usize].1);
} }
let mut vt: f64 = vts.iter().sum(); let mut vt: f64 = vts.iter().sum();
vt /= SEASONALITY_ITERATIONS as f64; vt /= self.seasonality_iterations as f64;
let t = ts[ts.len() - iter_steps + k].0; let t = ts[ts.len() - iter_steps + k].0;
res_ts.push((t, vt)); res_ts.push((t, vt));
} }
@ -167,11 +168,11 @@ impl AnalyticUnit for AnomalyAnalyticUnit {
} }
} }
async fn learn(&mut self, ms: MetricService, _ss: SegmentsService) -> LearningResult { async fn learn(&mut self, ms: MetricService, _ss: SegmentsService) -> LearningResult {
let mut sarima = SARIMA::new(self.config.seasonality, self.config.confidence); let mut sarima = SARIMA::new(self.config.seasonality, self.config.confidence, self.config.seasonality_iterations);
let utc: DateTime<Utc> = Utc::now(); let utc: DateTime<Utc> = Utc::now();
let to = utc.timestamp() as u64; let to = utc.timestamp() as u64;
let from = to - self.config.seasonality * SEASONALITY_ITERATIONS; let from = to - self.config.seasonality * self.config.seasonality_iterations;
let mr = ms.query(from, to, DETECTION_STEP).await.unwrap(); let mr = ms.query(from, to, DETECTION_STEP).await.unwrap();
if mr.data.keys().len() == 0 { if mr.data.keys().len() == 0 {

5
server/src/services/analytic_service/analytic_unit/types.rs

@ -30,6 +30,7 @@ pub struct AnomalyConfig {
pub alpha: f64, pub alpha: f64,
pub confidence: f64, pub confidence: f64,
pub seasonality: u64, // step in seconds, can be zero pub seasonality: u64, // step in seconds, can be zero
pub seasonality_iterations: u64
} }
impl Default for AnomalyConfig { impl Default for AnomalyConfig {
@ -38,6 +39,7 @@ impl Default for AnomalyConfig {
alpha: 0.5, alpha: 0.5,
confidence: 10.0, confidence: 10.0,
seasonality: 60 * 60, seasonality: 60 * 60,
seasonality_iterations: 3
} }
} }
} }
@ -85,7 +87,8 @@ impl AnalyticUnitConfig {
AnalyticUnitConfig::Anomaly(scfg) => { AnalyticUnitConfig::Anomaly(scfg) => {
if tcfg.is_some() { if tcfg.is_some() {
let t = tcfg.as_ref().unwrap(); let t = tcfg.as_ref().unwrap();
let need_learning = t.seasonality != scfg.seasonality; let mut need_learning = t.seasonality != scfg.seasonality;
need_learning |= t.seasonality_iterations != scfg.seasonality_iterations;
return (AnalyticUnitConfig::Anomaly(tcfg.unwrap()), need_learning); return (AnalyticUnitConfig::Anomaly(tcfg.unwrap()), need_learning);
} else { } else {
return (AnalyticUnitConfig::Anomaly(Default::default()), false); return (AnalyticUnitConfig::Anomaly(Default::default()), false);

Loading…
Cancel
Save