Skip to content

Commit 9e3e1a6

Browse files
committed
Update RCF to v3.8 and Enable Auto AD with 'Alert Once' Option (opensearch-project#979)
* Update RCF to v3.8 and Enable Auto AD with 'Alert Once' Option This PR added support for automatic Anomaly Detection (AD) and the 'Alert Once' option introduced in RCF 3.8. Testing done: 1. Deserialization Test: * Verified model deserialization from 3.0-rc3. * Ensured consistent scoring using the rc3 checkpoint and rc3 dependency on identical data. 2. Backward Compatibility Test: * Executed a mixed cluster with versions 2.10 and 3.0. * Validated that older detectors still produce results without throwing any exceptions in a blue-green simulation scenario. Signed-off-by: Kaituo Li <kaituo@amazon.com> * reduce recall since alertOnce reduced recall Signed-off-by: Kaituo Li <kaituo@amazon.com> * remove commented out code Signed-off-by: Kaituo Li <kaituo@amazon.com> --------- Signed-off-by: Kaituo Li <kaituo@amazon.com>
1 parent 2499d85 commit 9e3e1a6

File tree

12 files changed

+381
-26
lines changed

12 files changed

+381
-26
lines changed

build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -750,9 +750,9 @@ dependencies {
750750
implementation group: 'com.yahoo.datasketches', name: 'memory', version: '0.12.2'
751751
implementation group: 'commons-lang', name: 'commons-lang', version: '2.6'
752752
implementation group: 'org.apache.commons', name: 'commons-pool2', version: '2.10.0'
753-
implementation 'software.amazon.randomcutforest:randomcutforest-serialization:3.0-rc3'
754-
implementation 'software.amazon.randomcutforest:randomcutforest-parkservices:3.0-rc3'
755-
implementation 'software.amazon.randomcutforest:randomcutforest-core:3.0-rc3'
753+
implementation 'software.amazon.randomcutforest:randomcutforest-serialization:3.8.0'
754+
implementation 'software.amazon.randomcutforest:randomcutforest-parkservices:3.8.0'
755+
implementation 'software.amazon.randomcutforest:randomcutforest-core:3.8.0'
756756

757757
// force Jackson version to avoid version conflict issue
758758
implementation "com.fasterxml.jackson.core:jackson-databind:2.14.1"

src/main/java/org/opensearch/ad/ml/EntityColdStarter.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.opensearch.threadpool.ThreadPool;
6060

6161
import com.amazon.randomcutforest.config.Precision;
62+
import com.amazon.randomcutforest.config.TransformMethod;
6263
import com.amazon.randomcutforest.parkservices.ThresholdedRandomCutForest;
6364

6465
/**
@@ -373,17 +374,18 @@ private void trainModelFromDataSegments(
373374
// overlapping x3, x4, and only store x5, x6.
374375
.shingleSize(shingleSize)
375376
.internalShinglingEnabled(true)
376-
.anomalyRate(1 - this.thresholdMinPvalue);
377+
.anomalyRate(1 - this.thresholdMinPvalue)
378+
.transformMethod(TransformMethod.NORMALIZE)
379+
.alertOnce(true)
380+
.autoAdjust(true);
377381

378382
if (rcfSeed > 0) {
379383
rcfBuilder.randomSeed(rcfSeed);
380384
}
381385
ThresholdedRandomCutForest trcf = new ThresholdedRandomCutForest(rcfBuilder);
382-
383386
while (!dataPoints.isEmpty()) {
384387
trcf.process(dataPoints.poll(), 0);
385388
}
386-
387389
EntityModel model = entityState.getModel();
388390
if (model == null) {
389391
model = new EntityModel(entity, new ArrayDeque<>(), null);

src/main/java/org/opensearch/ad/ml/ModelManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
import com.amazon.randomcutforest.RandomCutForest;
5151
import com.amazon.randomcutforest.config.Precision;
52+
import com.amazon.randomcutforest.config.TransformMethod;
5253
import com.amazon.randomcutforest.parkservices.AnomalyDescriptor;
5354
import com.amazon.randomcutforest.parkservices.ThresholdedRandomCutForest;
5455

@@ -530,6 +531,10 @@ private void trainModelForStep(
530531
.boundingBoxCacheFraction(AnomalyDetectorSettings.REAL_TIME_BOUNDING_BOX_CACHE_RATIO)
531532
.shingleSize(detector.getShingleSize())
532533
.anomalyRate(1 - thresholdMinPvalue)
534+
.transformMethod(TransformMethod.NORMALIZE)
535+
.alertOnce(true)
536+
.autoAdjust(true)
537+
.internalShinglingEnabled(false)
533538
.build();
534539
Arrays.stream(dataPoints).forEach(s -> trcf.process(s, 0));
535540

@@ -620,6 +625,10 @@ public List<ThresholdingResult> getPreviewResults(double[][] dataPoints, int shi
620625
.boundingBoxCacheFraction(AnomalyDetectorSettings.BATCH_BOUNDING_BOX_CACHE_RATIO)
621626
.shingleSize(shingleSize)
622627
.anomalyRate(1 - this.thresholdMinPvalue)
628+
.transformMethod(TransformMethod.NORMALIZE)
629+
.alertOnce(true)
630+
.autoAdjust(true)
631+
.internalShinglingEnabled(false)
623632
.build();
624633
return Arrays.stream(dataPoints).map(point -> {
625634
AnomalyDescriptor descriptor = trcf.process(point, 0);

src/main/java/org/opensearch/ad/task/ADBatchTaskCache.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.opensearch.ad.settings.AnomalyDetectorSettings;
3131

3232
import com.amazon.randomcutforest.config.Precision;
33+
import com.amazon.randomcutforest.config.TransformMethod;
3334
import com.amazon.randomcutforest.parkservices.ThresholdedRandomCutForest;
3435

3536
/**
@@ -80,6 +81,10 @@ protected ADBatchTaskCache(ADTask adTask) {
8081
.boundingBoxCacheFraction(AnomalyDetectorSettings.BATCH_BOUNDING_BOX_CACHE_RATIO)
8182
.shingleSize(shingleSize)
8283
.anomalyRate(1 - AnomalyDetectorSettings.THRESHOLD_MIN_PVALUE)
84+
.transformMethod(TransformMethod.NORMALIZE)
85+
.alertOnce(true)
86+
.autoAdjust(true)
87+
.internalShinglingEnabled(false)
8388
.build();
8489

8590
this.thresholdModelTrained = false;

src/test/java/org/opensearch/ad/MemoryTrackerTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.opensearch.test.OpenSearchTestCase;
3333

3434
import com.amazon.randomcutforest.config.Precision;
35+
import com.amazon.randomcutforest.config.TransformMethod;
3536
import com.amazon.randomcutforest.parkservices.ThresholdedRandomCutForest;
3637

3738
public class MemoryTrackerTests extends OpenSearchTestCase {
@@ -108,6 +109,9 @@ public void setUp() throws Exception {
108109
.boundingBoxCacheFraction(AnomalyDetectorSettings.REAL_TIME_BOUNDING_BOX_CACHE_RATIO)
109110
.shingleSize(shingleSize)
110111
.internalShinglingEnabled(true)
112+
.transformMethod(TransformMethod.NORMALIZE)
113+
.alertOnce(true)
114+
.autoAdjust(true)
111115
.build();
112116

113117
detector = mock(AnomalyDetector.class);
@@ -151,6 +155,9 @@ public void testEstimateModelSize() {
151155
.internalShinglingEnabled(true)
152156
// same with dimension for opportunistic memory saving
153157
.shingleSize(shingleSize)
158+
.transformMethod(TransformMethod.NORMALIZE)
159+
.alertOnce(true)
160+
.autoAdjust(true)
154161
.build();
155162
assertEquals(603708, tracker.estimateTRCFModelSize(rcf2));
156163
assertTrue(tracker.isHostingAllowed(detectorId, rcf2));
@@ -170,6 +177,9 @@ public void testEstimateModelSize() {
170177
.internalShinglingEnabled(false)
171178
// same with dimension for opportunistic memory saving
172179
.shingleSize(1)
180+
.transformMethod(TransformMethod.NORMALIZE)
181+
.alertOnce(true)
182+
.autoAdjust(true)
173183
.build();
174184
assertEquals(1685208, tracker.estimateTRCFModelSize(rcf3));
175185

@@ -187,6 +197,9 @@ public void testEstimateModelSize() {
187197
.internalShinglingEnabled(true)
188198
// same with dimension for opportunistic memory saving
189199
.shingleSize(1)
200+
.transformMethod(TransformMethod.NORMALIZE)
201+
.alertOnce(true)
202+
.autoAdjust(true)
190203
.build();
191204
assertEquals(521304, tracker.estimateTRCFModelSize(rcf4));
192205

@@ -204,6 +217,9 @@ public void testEstimateModelSize() {
204217
.internalShinglingEnabled(true)
205218
// same with dimension for opportunistic memory saving
206219
.shingleSize(2)
220+
.transformMethod(TransformMethod.NORMALIZE)
221+
.alertOnce(true)
222+
.autoAdjust(true)
207223
.build();
208224
assertEquals(467340, tracker.estimateTRCFModelSize(rcf5));
209225

@@ -221,6 +237,9 @@ public void testEstimateModelSize() {
221237
.internalShinglingEnabled(true)
222238
// same with dimension for opportunistic memory saving
223239
.shingleSize(4)
240+
.transformMethod(TransformMethod.NORMALIZE)
241+
.alertOnce(true)
242+
.autoAdjust(true)
224243
.build();
225244
assertEquals(603676, tracker.estimateTRCFModelSize(rcf6));
226245

@@ -238,6 +257,9 @@ public void testEstimateModelSize() {
238257
.internalShinglingEnabled(true)
239258
// same with dimension for opportunistic memory saving
240259
.shingleSize(16)
260+
.transformMethod(TransformMethod.NORMALIZE)
261+
.alertOnce(true)
262+
.autoAdjust(true)
241263
.build();
242264
assertEquals(401481, tracker.estimateTRCFModelSize(rcf7));
243265

@@ -255,6 +277,9 @@ public void testEstimateModelSize() {
255277
.internalShinglingEnabled(true)
256278
// same with dimension for opportunistic memory saving
257279
.shingleSize(32)
280+
.transformMethod(TransformMethod.NORMALIZE)
281+
.alertOnce(true)
282+
.autoAdjust(true)
258283
.build();
259284
assertEquals(1040432, tracker.estimateTRCFModelSize(rcf8));
260285

@@ -272,6 +297,9 @@ public void testEstimateModelSize() {
272297
.internalShinglingEnabled(true)
273298
// same with dimension for opportunistic memory saving
274299
.shingleSize(64)
300+
.transformMethod(TransformMethod.NORMALIZE)
301+
.alertOnce(true)
302+
.autoAdjust(true)
275303
.build();
276304
assertEquals(1040688, tracker.estimateTRCFModelSize(rcf9));
277305

@@ -289,6 +317,9 @@ public void testEstimateModelSize() {
289317
.internalShinglingEnabled(true)
290318
// same with dimension for opportunistic memory saving
291319
.shingleSize(65)
320+
.transformMethod(TransformMethod.NORMALIZE)
321+
.alertOnce(true)
322+
.autoAdjust(true)
292323
.build();
293324
expectThrows(IllegalArgumentException.class, () -> tracker.estimateTRCFModelSize(rcf10));
294325
}

src/test/java/org/opensearch/ad/e2e/SingleStreamModelPerfIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public void testDataset() throws Exception {
4747
// TODO: this test case will run for a much longer time and timeout with security enabled
4848
if (!isHttps()) {
4949
disableResourceNotFoundFaultTolerence();
50-
verifyAnomaly("synthetic", 1, 1500, 8, .4, .9, 10);
50+
verifyAnomaly("synthetic", 1, 1500, 8, .4, .7, 10);
5151
}
5252
}
5353

@@ -96,7 +96,7 @@ private void verifyTestResults(
9696

9797
// recall = windows containing predicted anomaly points / total anomaly windows
9898
double recall = anomalies.size() > 0 ? positiveAnomalies / anomalies.size() : 1;
99-
assertTrue(recall >= minRecall);
99+
assertTrue(String.format(Locale.ROOT, "recall should be %f but got %f", recall, minRecall), recall >= minRecall);
100100

101101
assertTrue(errors <= maxError);
102102
LOG.info("Precision: {}, Window recall: {}", precision, recall);

0 commit comments

Comments
 (0)