教學課程:使用 ML.NET 偵測時間序列中的異常
了解如何建置時間序列資料的異常偵測應用程式。 此教學課程會示範如何在 Visual Studio 2019 中使用 C# 建立 .NET Core 主控台應用程式。
在本教學課程中,您會了解如何:
- 載入資料
- 偵測時間序列的期間
- 偵測定期時間序列的異常狀況
您可以在 dotnet/samples 存放庫中找到本教學課程的原始程式碼。
必要條件
已安裝 「.NET Desktop Development」 工作負載的Visual Studio 2022。
建立主控台應用程式
建立名為 "PhoneCallsAnomalyDetection" 的 C# 主控台應用程式。 按 [下一步] 按鈕。
選擇 .NET 6 作為要使用的架構。 按一下 [ 建立 ] 按鈕。
在專案中建立一個名為 Data 的目錄以儲存資料集檔案。
安裝 Microsoft.ML NuGet 封裝1.5.2 版:
- 在 [方案總管] 中,於您的專案上按一下滑鼠右鍵,然後選取 [管理 NuGet 套件]。
- 選擇「nuget.org」做為套件來源。
- 選取 [瀏覽] 索引標籤。
- 搜尋 Microsoft.ML。
- 從封裝清單中選取 [Microsoft.ML],然後從 [版本] 下拉式清單中選擇 [1.5.2] 版。
- 選取 [安裝] 按鈕。
- 在 [預覽變更] 對話方塊上,選取 [確定] 按鈕,然後在 [授權接受] 對話方塊上,如果您同意所列套件的授權條款,請選取 [我接受]。
針對 Microsoft.ML.TimeSeries1.5.2 版重複這些步驟。
在您的 Program.cs 檔案最上方新增下列
using
陳述式:using Microsoft.ML; using Microsoft.ML.TimeSeries; using PhoneCallsAnomalyDetection;
下載您的資料
下載資料集,並儲存至您先前建立的 Data 資料夾:
以滑鼠右鍵按一下 phone-calls.csv,然後選取 [另存連結 (或目標)...]
請務必將 *.csv 檔案儲存至 Data 資料夾,或儲存在其他位置之後,將 *.csv 檔案移至 Data 資料夾。
在 [方案總管] 中,以滑鼠右鍵按一下 *.csv 檔案,並選取 [內容]。 在 [進階] 底下,將 [複製到輸出目錄] 的值變更為 [有更新時才複製]。
下表是 *.csv 檔案的資料預覽:
timestamp | value |
---|---|
2018/9/3 | 36.69670857 |
2018/9/4 | 35.74160571 |
..... | ..... |
2018/10/3 | 34.49893429 |
... | .... |
此檔案代表時間序列。 檔案中的每個資料列都是資料點。 每個資料點都有兩個屬性,也就是 timestamp
和 value
,代表每天的通話數目。 通話數目會轉換成去敏感度。
建立類別及定義路徑
接下來,定義您的輸入和預測類別資料結構。
將新類別新增至專案:
在 [方案總管] 中,以滑鼠右鍵按一下專案,然後選取 [新增] > [新項目]。
在 [新增項目] 對話方塊中,選取 [類別],然後將 [名稱] 欄位變更為 PhoneCallsData.cs。 接著,選取 [新增] 按鈕。
隨即在程式碼編輯器中開啟 PhoneCallsData.cs 檔案。
將下列
using
陳述式新增至 PhoneCallsData.cs 的上方:using Microsoft.ML.Data;
移除現有的類別定義,然後將下列程式碼 (具有
PhoneCallsData
和PhoneCallsPrediction
這兩個類別) 新增至 PhoneCallsData.cs 檔案:public class PhoneCallsData { [LoadColumn(0)] public string? timestamp; [LoadColumn(1)] public double value; } public class PhoneCallsPrediction { // Vector to hold anomaly detection results, including isAnomaly, anomalyScore, // magnitude, expectedValue, boundaryUnits, upperBoundary and lowerBoundary. [VectorType(7)] public double[]? Prediction { get; set; } }
PhoneCallsData
會指定輸入資料類別。 LoadColumn 屬性會指定應該載入資料集內的哪些資料行 (依資料行索引)。 其有兩個屬性 (timestamp
和value
),會對應至資料檔案中的相同屬性。PhoneCallsPrediction
指定預測資料類別。 針對 SR-CNN 偵測器,預測取決於指定的偵測模式。 在此範例中,我們會選取AnomalyAndMargin
模式。 輸出包含七個資料行。 在大部分情況下,IsAnomaly
,ExpectedValue
、UpperBoundary
和LowerBoundary
都有足夠的資訊。 他們會告訴您某個點是否為異常、點的預期值,以及點的下/上限區域。將下列程式碼新增至緊接在使用陳述式下方的一行,以指定資料檔案的路徑:
string _dataPath = Path.Combine(Environment.CurrentDirectory, "Data", "phone-calls.csv");
將變數初始化
將
Console.WriteLine("Hello World!")
行替換成下列程式碼,宣告並初始化mlContext
變數:MLContext mlContext = new MLContext();
MLContext 類別是所有 ML.NET 作業的起點,且初始化
mlContext
會建立新的 ML.NET 環境,其可在模型建立工作流程物件之間共用。 就概念而言,類似於 Entity Framework 中的DBContext
。
載入資料
ML.NET 中的資料會以 IDataView 介面表示。 IDataView
是彈性且有效率的表格式資料描述方式 (數值和文字)。 資料可以從文字檔或其他來源 (例如 SQL 資料庫或記錄檔) 載入至 IDataView
物件。
在建立
mlContext
變數之後,新增下列程式碼:IDataView dataView = mlContext.Data.LoadFromTextFile<PhoneCallsData>(path: _dataPath, hasHeader: true, separatorChar: ',');
LoadFromTextFile() 會定義資料結構描述並讀入檔案中。 會接受資料路徑變數然後傳回
IDataView
。
時間序列異常偵測
時間序列異常偵測是偵測時間序列資料極端值的程序,其為指定輸入時間序列中非預期或「奇怪」行為的資料點。 這些異常通常表示問題網域中某些感興趣的事件:對使用者帳戶進行網路攻擊、電源中斷、伺服器上的高載 RPS、記憶體流失等。
若要尋找時間序列的異常,您應該先判斷序列的期間。 然後,時間序列可以分解成數個元件,也就是 Y = T + S + R
,其中 Y
是原始序列、T
是趨勢元件、S
是趨勢元件,而 R
是序列的剩餘元件。 此步驟稱為分解。 最後,會在剩餘元件上執行偵測,以尋找異常。 在 ML.NET 中,SR-CNN 演算法是一種進階且新的演算法,以 Sr 殘差 (SR) 和卷積類神經網路為基礎, (CNN) 來偵測時間序列的異常。 如需此演算法的詳細資訊,請參閱 Microsoft 的時間序列異常偵測服務。
在本教學課程中,您將會看到可以使用兩個函式來完成這些程序。
偵測期間
在第一個步驟中,我們會叫用 DetectSeasonality
函式來判斷序列列的期間。
建立 DetectPeriod 方法
使用下列程式碼,在 Program.cs 檔案底部建立
DetectPeriod
方法:int DetectPeriod(MLContext mlContext, IDataView phoneCalls) { }
使用 DetectSeasonality 函式來偵測期間。 使用下列程式碼將它新增至
DetectPeriod
方法:int period = mlContext.AnomalyDetection.DetectSeasonality(phoneCalls, nameof(PhoneCallsData.value));
將下列內容新增為
DetectPeriod
方法中的下一行程式碼來顯示期間值:Console.WriteLine("Period of the series is: {0}.", period);
傳回期間值。
// <SnippetSetupSrCnnParameters>
在
LoadFromTextFile()
方法的呼叫下方新增DetectPeriod
方法的下列呼叫:int period = DetectPeriod(mlContext, dataView);
期間偵測結果
執行應用程式。 您的結果應該與以下類似。
Period of the series is: 7.
偵測異常
在此步驟中,您會使用 DetectEntireAnomalyBySrCnn 方法來尋找異常。
建立 DetectAnomaly 方法
請使用下列程式碼,在
DetectPeriod
方法正下方建立DetectAnomaly
方法:void DetectAnomaly(MLContext mlContext, IDataView phoneCalls, int period) { }
使用下列程式碼在
DetectAnomaly
方法中設定 SrCnnEntireAnomalyDetectorOptions:var options = new SrCnnEntireAnomalyDetectorOptions() { Threshold = 0.3, Sensitivity = 64.0, DetectMode = SrCnnDetectMode.AnomalyAndMargin, Period = period, };
在
DetectAnomaly
方法中新增下列程式碼,以利用 SR-CNN 演算法偵測異常:var outputDataView = mlContext.AnomalyDetection.DetectEntireAnomalyBySrCnn(phoneCalls, nameof(PhoneCallsPrediction.Prediction), nameof(PhoneCallsData.value), options);
將輸出資料檢視轉換成強型別
IEnumerable
,以更輕鬆使用CreateEnumerable
方法和下列程式碼顯示:var predictions = mlContext.Data.CreateEnumerable<PhoneCallsPrediction>( outputDataView, reuseRowObject: false);
下列列程式碼作為
DetectAnomaly
方法中的下一行,建立顯示標頭:Console.WriteLine("Index,Data,Anomaly,AnomalyScore,Mag,ExpectedValue,BoundaryUnit,UpperBoundary,LowerBoundary");
您會在變更點偵測結果中顯示下列資訊:
Index
是每個點的索引。Anomaly
是每個點是否偵測為異常的指標。ExpectedValue
是每個點的估計值。LowerBoundary
是每個點的最小值,不能是異常值。UpperBoundary
是每個點的最高值,不能是異常值。
使用下列程式碼逐一查看
predictions
IEnumerable
,並顯示結果:var index = 0; foreach (var p in predictions) { if (p.Prediction is not null) { string output; if (p.Prediction[0] == 1) output = "{0},{1},{2},{3},{4}, <-- alert is on! detected anomaly"; else output = "{0},{1},{2},{3},{4}"; Console.WriteLine(output, index, p.Prediction[0], p.Prediction[3], p.Prediction[5], p.Prediction[6]); } ++index; } Console.WriteLine("");
在
DetectPeriod()
方法呼叫的下方新增DetectAnomaly
方法的下列呼叫:DetectAnomaly(mlContext, dataView, period);
異常偵測結果
執行應用程式。 您的結果應該與以下類似。 處理期間會顯示訊息。 您可能會看到警告或處理訊息。 為了讓結果變得清楚,部分訊息已從下列結果中移除。
Detect period of the series
Period of the series is: 7.
Detect anomaly points in the series
Index Data Anomaly AnomalyScore Mag ExpectedValue BoundaryUnit UpperBoundary LowerBoundary
0,0,36.841787256739266,41.14206982401966,32.541504689458876
1,0,35.67303618137362,39.97331874865401,31.372753614093227
2,0,34.710132999891826,39.029491313022824,30.390774686760828
3,0,33.44765248883495,37.786086547816545,29.10921842985335
4,0,28.937110922276364,33.25646923540736,24.61775260914537
5,0,5.143895892785781,9.444178460066171,0.843613325505391
6,0,5.163325228419392,9.463607795699783,0.8630426611390014
7,0,36.76414836240396,41.06443092968435,32.46386579512357
8,0,35.77908590657007,40.07936847385046,31.478803339289676
9,0,34.547259536635245,38.847542103915636,30.246976969354854
10,0,33.55193524820608,37.871293561337076,29.23257693507508
11,0,29.091800129624648,33.392082696905035,24.79151756234426
12,0,5.154836630338823,9.455119197619213,0.8545540630584334
13,0,5.234332502492464,9.534615069772855,0.934049935212073
14,0,36.54992549471526,40.85020806199565,32.24964292743487
15,0,35.79526470980883,40.095547277089224,31.494982142528443
16,0,34.34099013096804,38.64127269824843,30.040707563687647
17,0,33.61201516582131,37.9122977331017,29.31173259854092
18,0,29.223563320561812,33.5238458878422,24.923280753281425
19,0,5.170512168851533,9.470794736131923,0.8702296015711433
20,0,5.2614938889462834,9.561776456226674,0.9612113216658926
21,0,36.37103858487317,40.67132115215356,32.07075601759278
22,0,35.813544599026855,40.113827166307246,31.513262031746464
23,0,34.05600492733225,38.356287494612644,29.755722360051863
24,0,33.65828319077884,37.95856575805923,29.358000623498448
25,0,29.381125690882463,33.681408258162854,25.080843123602072
26,0,5.261543539820418,9.561826107100808,0.9612609725400283
27,0,5.4873712582971805,9.787653825577571,1.1870886910167897
28,1,36.504694001629254,40.804976568909645,32.20441143434886 <-- alert is on, detected anomaly
...
恭喜! 您現在已成功建置機器學習模型,以在定期序列上偵測期間和異常。
您可以在 dotnet/samples 存放庫中找到本教學課程的原始程式碼。
在本教學課程中,您已了解如何:
- 載入資料
- 偵測時間序列資料的期間
- 偵測時間序列資料中的異常
下一步
請查看機器學習範例 GitHub 存放庫,以探索耗電量異常偵測範例。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應