Skip to content

Commit 5f875da

Browse files
authored
Returning multiple dimensions in RCA for anomaly detection (#5236)
* Proposal for returning multiple dimensions in RCA * Handling review comments. * Refactoring. Test. * Updating * Handled review comments * Revert libmf * Removed linq * Handled comments on names.
1 parent dd81e79 commit 5f875da

File tree

6 files changed

+366
-87
lines changed

6 files changed

+366
-87
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.ML;
5+
using Microsoft.ML.TimeSeries;
6+
7+
namespace Samples.Dynamic
8+
{
9+
public static class LocalizeRootCauseMultipleDimensions
10+
{
11+
private static string AGG_SYMBOL = "##SUM##";
12+
13+
public static void Example()
14+
{
15+
// Create a new ML context, for ML.NET operations. It can be used for
16+
// exception tracking and logging, as well as the source of randomness.
17+
var mlContext = new MLContext();
18+
19+
// Create an root cause localization input instance.
20+
DateTime timestamp = GetTimestamp();
21+
var data = new RootCauseLocalizationInput(timestamp, GetAnomalyDimension(), new List<MetricSlice>() { new MetricSlice(timestamp, GetTimeSeriesPoints()) }, AggregateType.Sum, AGG_SYMBOL);
22+
23+
// Get the root cause localization result.
24+
List<RootCause> prediction = mlContext.AnomalyDetection.LocalizeRootCauses(data);
25+
26+
// Print the localization results.
27+
int count = 0;
28+
foreach (RootCause cause in prediction)
29+
{
30+
count++;
31+
foreach (RootCauseItem item in cause.Items)
32+
{
33+
Console.WriteLine($"Prepared cause #{count} ...");
34+
Console.WriteLine($"Score: {item.Score}, Path: {String.Join(" ", item.Path)}, Direction: {item.Direction}, Dimension:{String.Join(" ", item.Dimension)}");
35+
}
36+
}
37+
38+
//Prepared cause #1 ...
39+
//Score: 0.26670448876705927, Path: DataCenter, Direction: Up, Dimension:[Country, UK] [DeviceType, ##SUM##] [DataCenter, DC1]
40+
//Prepared cause #2 ...
41+
//Score: 0.254746585094852, Path: DeviceType, Direction: Up, Dimension:[Country, UK] [DeviceType, Laptop] [DataCenter, ##SUM##]
42+
}
43+
44+
private static List<TimeSeriesPoint> GetTimeSeriesPoints()
45+
{
46+
List<TimeSeriesPoint> TimeSeriesPoints = new List<TimeSeriesPoint>();
47+
48+
Dictionary<string, Object> dic1 = new Dictionary<string, Object>
49+
{
50+
{ "Country", "UK" },
51+
{ "DeviceType", "Laptop" },
52+
{ "DataCenter", "DC1" }
53+
};
54+
TimeSeriesPoints.Add(new TimeSeriesPoint(200, 100, true, dic1));
55+
56+
Dictionary<string, Object> dic2 = new Dictionary<string, Object>();
57+
dic2.Add("Country", "UK");
58+
dic2.Add("DeviceType", "Mobile");
59+
dic2.Add("DataCenter", "DC1");
60+
TimeSeriesPoints.Add(new TimeSeriesPoint(1000, 100, true, dic2));
61+
62+
Dictionary<string, Object> dic3 = new Dictionary<string, Object>();
63+
dic3.Add("Country", "UK");
64+
dic3.Add("DeviceType", AGG_SYMBOL);
65+
dic3.Add("DataCenter", "DC1");
66+
TimeSeriesPoints.Add(new TimeSeriesPoint(1200, 200, true, dic3));
67+
68+
Dictionary<string, Object> dic4 = new Dictionary<string, Object>();
69+
dic4.Add("Country", "UK");
70+
dic4.Add("DeviceType", "Laptop");
71+
dic4.Add("DataCenter", "DC2");
72+
TimeSeriesPoints.Add(new TimeSeriesPoint(100, 100, false, dic4));
73+
74+
Dictionary<string, Object> dic5 = new Dictionary<string, Object>();
75+
dic5.Add("Country", "UK");
76+
dic5.Add("DeviceType", "Mobile");
77+
dic5.Add("DataCenter", "DC2");
78+
TimeSeriesPoints.Add(new TimeSeriesPoint(200, 200, false, dic5));
79+
80+
Dictionary<string, Object> dic6 = new Dictionary<string, Object>();
81+
dic6.Add("Country", "UK");
82+
dic6.Add("DeviceType", AGG_SYMBOL);
83+
dic6.Add("DataCenter", "DC2");
84+
TimeSeriesPoints.Add(new TimeSeriesPoint(300, 300, false, dic6));
85+
86+
Dictionary<string, Object> dic7 = new Dictionary<string, Object>();
87+
dic7.Add("Country", "UK");
88+
dic7.Add("DeviceType", AGG_SYMBOL);
89+
dic7.Add("DataCenter", AGG_SYMBOL);
90+
TimeSeriesPoints.Add(new TimeSeriesPoint(1800, 750, true, dic7));
91+
92+
Dictionary<string, Object> dic8 = new Dictionary<string, Object>();
93+
dic8.Add("Country", "UK");
94+
dic8.Add("DeviceType", "Laptop");
95+
dic8.Add("DataCenter", AGG_SYMBOL);
96+
TimeSeriesPoints.Add(new TimeSeriesPoint(1500, 450, true, dic8));
97+
98+
Dictionary<string, Object> dic9 = new Dictionary<string, Object>();
99+
dic9.Add("Country", "UK");
100+
dic9.Add("DeviceType", "Mobile");
101+
dic9.Add("DataCenter", AGG_SYMBOL);
102+
TimeSeriesPoints.Add(new TimeSeriesPoint(600, 550, false, dic9));
103+
104+
Dictionary<string, Object> dic10 = new Dictionary<string, Object>();
105+
dic10.Add("Country", "UK");
106+
dic10.Add("DeviceType", "Mobile");
107+
dic10.Add("DataCenter", "DC3");
108+
TimeSeriesPoints.Add(new TimeSeriesPoint(100, 100, false, dic10));
109+
110+
Dictionary<string, Object> dic11 = new Dictionary<string, Object>();
111+
dic11.Add("Country", "UK");
112+
dic11.Add("DeviceType", "Laptop");
113+
dic11.Add("DataCenter", "DC3");
114+
TimeSeriesPoints.Add(new TimeSeriesPoint(200, 250, false, dic11));
115+
116+
Dictionary<string, Object> dic12 = new Dictionary<string, Object>();
117+
dic12.Add("Country", "UK");
118+
dic12.Add("DeviceType", AGG_SYMBOL);
119+
dic12.Add("DataCenter", "DC3");
120+
TimeSeriesPoints.Add(new TimeSeriesPoint(300, 350, false, dic12));
121+
122+
return TimeSeriesPoints;
123+
}
124+
125+
private static Dictionary<string, Object> GetAnomalyDimension()
126+
{
127+
Dictionary<string, Object> dim = new Dictionary<string, Object>();
128+
dim.Add("Country", "UK");
129+
dim.Add("DeviceType", AGG_SYMBOL);
130+
dim.Add("DataCenter", AGG_SYMBOL);
131+
132+
return dim;
133+
}
134+
135+
private static DateTime GetTimestamp()
136+
{
137+
return new DateTime(2020, 3, 23, 0, 0, 0);
138+
}
139+
}
140+
}

docs/samples/Microsoft.ML.Samples/Program.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,23 @@ namespace Microsoft.ML.Samples
66
{
77
public static class Program
88
{
9-
public static void Main(string[] args) => RunAll();
9+
public static void Main(string[] args) => RunAll(args == null || args.Length == 0 ? null : args[0]);
1010

11-
internal static void RunAll()
11+
internal static void RunAll(string name = null)
1212
{
1313
int samples = 0;
1414
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
1515
{
16-
var sample = type.GetMethod("Example", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
17-
18-
if (sample != null)
16+
if (name == null || name.Equals(type.Name))
1917
{
20-
Console.WriteLine(type.Name);
21-
sample.Invoke(null, null);
22-
samples++;
18+
var sample = type.GetMethod("Example", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
19+
20+
if (sample != null)
21+
{
22+
Console.WriteLine(type.Name);
23+
sample.Invoke(null, null);
24+
samples++;
25+
}
2326
}
2427
}
2528

src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Collections.Generic;
56
using Microsoft.ML.Data;
67
using Microsoft.ML.Runtime;
78
using Microsoft.ML.TimeSeries;
@@ -226,6 +227,35 @@ public static IDataView DetectEntireAnomalyBySrCnn(this AnomalyDetectionCatalog
226227
/// </format>
227228
/// </example>
228229
public static RootCause LocalizeRootCause(this AnomalyDetectionCatalog catalog, RootCauseLocalizationInput src, double beta = 0.3, double rootCauseThreshold = 0.95)
230+
{
231+
List<RootCause> causes = LocalizeRootCauses(catalog, src, beta, rootCauseThreshold);
232+
if (causes?.Count > 0)
233+
{
234+
return causes[0];
235+
}
236+
else
237+
{
238+
return null;
239+
}
240+
241+
}
242+
243+
/// <summary>
244+
/// Outputs an ordered list of <see cref="RootCause"/>s. The order corresponds to which prepared cause is most likely to be the root cause.
245+
/// </summary>
246+
/// <param name="catalog">The anomaly detection catalog.</param>
247+
/// <param name="src">Root cause's input. The data is an instance of <see cref="Microsoft.ML.TimeSeries.RootCauseLocalizationInput"/>.</param>
248+
/// <param name="beta">Beta is a weight parameter for user to choose. It is used when score is calculated for each root cause item. The range of beta should be in [0,1]. For a larger beta, root cause point which has a large difference between value and expected value will get a high score. On the contrary, for a small beta, root cause items which has a high relative change will get a high score.</param>
249+
/// <param name="rootCauseThreshold">A threshold to determine whether the point should be root cause. The range of this threshold should be in [0,1].
250+
/// If the point's delta is equal to or larger than rootCauseThreshold multiplied by anomaly dimension point's delta, this point is treated as a root cause. Different threshold will turn out different results. Users can choose the delta according to their data and requirments.</param>
251+
/// <example>
252+
/// <format type="text/markdown">
253+
/// <![CDATA[
254+
/// [!code-csharp[LocalizeRootCauseMultipleDimensions](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/Transforms/TimeSeries/LocalizeRootCauseMultipleDimensions.cs)]
255+
/// ]]>
256+
/// </format>
257+
/// </example>
258+
public static List<RootCause> LocalizeRootCauses(this AnomalyDetectionCatalog catalog, RootCauseLocalizationInput src, double beta = 0.5, double rootCauseThreshold = 0.95)
229259
{
230260
IHostEnvironment host = CatalogUtils.GetEnvironment(catalog);
231261

@@ -234,12 +264,11 @@ public static RootCause LocalizeRootCause(this AnomalyDetectionCatalog catalog,
234264

235265
//check parameters
236266
host.CheckUserArg(beta >= 0 && beta <= 1, nameof(beta), "Must be in [0,1]");
237-
host.CheckUserArg(rootCauseThreshold >= 0 && rootCauseThreshold <= 1, nameof(beta), "Must be in [0,1]");
267+
host.CheckUserArg(rootCauseThreshold >= 0 && rootCauseThreshold <= 1, nameof(rootCauseThreshold), "Must be in [0,1]");
238268

239-
//find out the root cause
269+
//find out the possible causes
240270
RootCauseAnalyzer analyzer = new RootCauseAnalyzer(src, beta, rootCauseThreshold);
241-
RootCause dst = analyzer.Analyze();
242-
return dst;
271+
return analyzer.AnalyzePossibleCauses();
243272
}
244273

245274
/// <summary>

0 commit comments

Comments
 (0)