使用 ML.NET 实现峰值检测来排查异常
機器學習中一類問題稱為峰值檢測,它旨在識別與大部分時序中明顯不同但臨時突發(fā)的數(shù)據(jù)值。及時檢測到這些可疑的個體、事件或觀察值很重要,這樣才能盡量減少其產(chǎn)生。異常情況檢測是檢測時序數(shù)據(jù)離群值的過程,在給定的輸入時序上指向“怪異”或不是預期行為的峰值。
通常有兩種類型的時序異常檢測:
峰值,指示系統(tǒng)中臨時突發(fā)的異常行為。
更改點,指示系統(tǒng)中一段時間內(nèi)持續(xù)更改的開始。
在 ML.NET 中,IID 峰值檢測或 IID 更改點檢測算法適用于獨立且均勻分布的數(shù)據(jù)集。峰值檢測不需要任何訓練,這一點不像其他的機器學習場景,代碼也非常簡單。
我們來看一個真實的例子,假設(shè)有這樣一組日志數(shù)據(jù):
如圖所示有紅色背景色的條形表明了發(fā)生異常。在 2019 年 10 月 19 日之前的錯誤計數(shù),每天只發(fā)生了幾例,而當天會突然達到峰值。當修復系統(tǒng)后系統(tǒng)正常運行了兩天,不過由于引入了一個新的 Bug 在 2019 年 10 月 22 日又出現(xiàn)一個新的峰值。這個瞬間的峰值反映了系統(tǒng)肯定出現(xiàn)大規(guī)模異常。接下來我們就通過 ML.NET 來實現(xiàn)對峰值的識別。
首先安裝以下 NuGet 包:
Install-Package Microsoft.ML Install-Package Microsoft.ML.TimeSeriesMicrosoft.MLMicrosoft.ML.TimeSeries?包含時間序列數(shù)據(jù)的相關(guān)對象。
var mlContext = new MLContext();為了快速示例,我將模擬包含峰值的輸入數(shù)據(jù)如下:
var counts = new[] { 0, 1, 1, 0, 2, 1, 0, 0, 1, 1, 50, 0, 1, 0, 2, 1, 0, 1 };50這個值很突出的代表了峰值。接下來我們定義一個強類型對象作為輸入模型:
class Input {public float Count { get; set; } }與輸入一樣,定義一個強類型輸出模型:
class Output {[VectorType(3)]public double[] Prediction { get; set; } }用 VectorType 標注了預測數(shù)據(jù)為 3 個元素的雙精度類型的數(shù)組。
接下來,我們定義評估器對象,可用于分析模型的效果:
var estimator = mlContext.Transforms.DetectIidSpike(nameof(Output.Prediction),nameof(Input.Count),confidence:99,pvalueHistoryLength:counts.Length / 4);DetectIidSpike 方法有 4 個參數(shù),輸入屬性的名稱、輸出屬性的名稱,置信度、峰值出現(xiàn)范圍,一般會將范圍設(shè)置為輸入長度的四分之一。
最后,生成轉(zhuǎn)換對象,創(chuàng)建預訓練模型,只是這個模型不需要訓練,所以傳入空數(shù)組:
ITransformer transformer = estimator.Fit(mlContext.Data.LoadFromEnumerable(new List<Input>()));有了模型,我們處理用于預測的輸入數(shù)據(jù):
var input = counts.Select(x => new Input { Count = x }); IDataView transformedData = transformer.Transform(mlContext.Data.LoadFromEnumerable(input));最后一步獲取預測結(jié)果:
var predictions = mlContext.Data.CreateEnumerable<Output>(transformedData, false);在此步驟中,我們用循環(huán)輸出每一個預測數(shù)據(jù)的結(jié)果:
foreach (var p in predictions) {Console.WriteLine($"{p.Prediction[0]}\t{p.Prediction[1]}\t{p.Prediction[2]}"); }在這里,你會看到輸出預測中每行都是一個包含3個浮點數(shù)的數(shù)組:
第一列是一個bool值,指示該行是否為峰值。0 表示不是峰值,第二列是原始輸入,第三列是逗號分隔的置信值。
以下是到目前為止的完整代碼:
var counts = new[] { 0, 1, 1, 0, 2, 1, 0, 0, 1, 1, 50, 0, 1, 0, 2, 1, 0, 1 };var mlContext = new MLContext(); var estimator = mlContext.Transforms.DetectIidSpike(nameof(Output.Prediction), nameof(Input.Count), confidence:99, pvalueHistoryLength:counts.Length / 4); ITransformer transformer = estimator.Fit(mlContext.Data.LoadFromEnumerable(new List<Input>())); var input = counts.Select(x => new Input { Count = x }); IDataView transformedData = transformer.Transform(mlContext.Data.LoadFromEnumerable(input)); var predictions = mlContext.Data.CreateEnumerable<Output>(transformedData, false);foreach (var p in predictions) {Console.WriteLine($"{p.Prediction[0]}\t{p.Prediction[1]}\t{p.Prediction[2]}"); }乍一看,代碼可能看起來有點啰嗦。主要是 ML.NET 無論訓練模型與否,管道幾乎相同的。
我們再試試改一下 confidence 設(shè)置。看看會發(fā)生什么:
var estimator = mlContext.Transforms.DetectIidSpike(nameof(Output.Prediction),nameof(Input.Count),confidence:95,pvalueHistoryLength:counts.Length / 4);對于相同的輸入,預測現(xiàn)在如下所示:
注意到區(qū)別了嗎?第五行現(xiàn)在也標記為峰值。
我們將輸入值更改為以下內(nèi)容:
var counts = new[] { 1, 3, 0, 4, 5, 5, 4, 3, 3, 0, 13, 8, 1, 61, 21, 40, 7, 7, 5, 6, 8, 33, 11, 5,2, 10, 11, 18, 14, 23, 8, 17, 15, 13, 24, 29, 15, 20, 29, 19, 18, 17, 23, 47, 7, 14, 26, 28,5, 22, 47, 22, 20, 9, 40, 6, 8, 4, 10, 10, 1, 4, 27, 3, 3, 7, 6, 12, 8, 3, 1, 2, 0, 0, 2, 0,2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2 };此時在沒有 ML.NET 時就感覺到想要發(fā)現(xiàn)異常要困難得多了吧。運行程序后,下面是結(jié)果的一部分:
最后一行標記為峰值。但看看第14行絕對看起來像一個異常,ML.NET 似乎有點問題,置信值低于0.05。我們可以稍微處理一下:
if (p.Prediction[2] < (1 - 0.95)) {p.Prediction[0] = 1; }這雖然有點玩套路,但基本上真的峰值被預測出來,不過弄出了更多不正確的預測。實際上應(yīng)該在置信度的基礎(chǔ)上用額外的二次過濾機制來保證結(jié)果傾向于正確:
到目前為止,我們可以使用 ML.NET 來實現(xiàn)機器學習檢測異常數(shù)據(jù)了,這會讓我們監(jiān)控一類的應(yīng)用更加智能!
總結(jié)
以上是生活随笔為你收集整理的使用 ML.NET 实现峰值检测来排查异常的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dnSpy反编译、部署调试神器
- 下一篇: OpenTelemetry - 云原生下