教學課程:使用 ML.NET 偵測時間序列中的異常

瞭解如何建置時間序列數據的異常偵測應用程式。 本教學課程會在 Visual Studio 2019 中使用 C# 建立 .NET 控制台應用程式。

在本教學課程中,您將瞭解如何:

  • 載入數據
  • 偵測時間序列的周期
  • 偵測時間序列中的定期異常

您可以在 dotnet/samples 存放庫找到本教學課程的原始程式碼。

先決條件

建立主控台應用程式

  1. 建立名為 「PhoneCallsAnomalyDetection」 的 C# 控制台應用程式。 選取 下一步。

  2. 選擇 .NET 8 作為目標框架。 選取 ,創建。

  3. 在專案中建立名為 Data 的目錄,以儲存數據集檔案。

  4. 安裝 Microsoft.ML NuGet 套件 版本 1.5.2:

    1. 在 [方案總管] 中,以滑鼠右鍵按下您的專案,然後選取 [管理 NuGet 套件]。
    2. 選擇 [nuget.org] 作為套件來源。
    3. 選取 [流覽] 索引標籤。
    4. 搜尋 Microsoft.ML。
    5. 從套件清單中選取 [Microsoft.ML],然後從 [版本] 下拉式清單中選擇版本 1.5.2。
    6. 按下 [安裝] 按鈕。
    7. 選取 [確定] 按鈕於 [預覽變更] 對話方塊上,然後如果您同意列出的套件的許可條款,請在 [授權接受] 對話方塊上選取 [我接受] 按鈕。

    針對 Microsoft.ML.TimeSeries1.5.2版重複這些步驟。

  5. 在 using 檔案頂端新增下列 指令:

    using Microsoft.ML;
    using Microsoft.ML.TimeSeries;
    using PhoneCallsAnomalyDetection;
    

下載您的數據

  1. 下載數據集,並將其儲存至您先前建立的 Data 資料夾:

    在 phone-calls.csv 上按下滑鼠右鍵,然後選取 [儲存連結](或目標) 作為...。

    請確定您將 *.csv 檔案儲存至 Data 資料夾,或在其他地方儲存之後,將 *.csv 檔案移至 Data 資料夾。

  2. 在 [方案總管] 中,以滑鼠右鍵按一下 *.csv 檔案,選取 [屬性]。 在 [進階] 下,將 [複製到輸出目錄] 的值變更為 [如果較新則複製]。

下表是 *.csv 檔案中的數據預覽:

時間戳記 價值
2018/9/3 36.69670857
2018/9/4 35.74160571
..... .....
2018/10/3 34.49893429
... ....

此檔案代表時間序列。 檔案中的每個數據列都是數據點。 每個數據點都有兩個屬性,即 timestamp 和 value,以代表每天的來電數目。 通話數目會轉換成去敏感處理。

建立類別並定義路徑

接下來,定義您的輸入和預測類別數據結構。

將新類別新增至您的專案:

  1. 在 方案總管中,以滑鼠右鍵按下專案,然後選取 新增 > 新增項目。

  2. 在 [[新增專案] 對話框中,選取 [類別],並將 [名稱] 字段變更為 [PhoneCallsData.cs]。 然後,選取 新增。

    PhoneCallsData.cs 檔案會在程式碼編輯器中開啟。

  3. 將下列 using 指示詞新增至 PhoneCallsData.cs頂端:

    using Microsoft.ML.Data;
    
  4. 移除現有的類別定義,將下列包含兩個類別 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 都信息豐富。 它們會告訴您某個點是否為異常、點的預期值,以及該點的下層和上限區域。

  5. 將下列程式代碼新增至 using 指示詞下方的行,以指定資料檔案的路徑:

    string _dataPath = Path.Combine(Environment.CurrentDirectory, "Data", "phone-calls.csv");
    

初始化變數

  1. 以下列程式代碼取代 Console.WriteLine("Hello World!") 行,以宣告和初始化 mlContext 變數:

    MLContext mlContext = new MLContext();
    

    MLContext 類別 是所有 ML.NET 作業的起點,初始化 mlContext 會建立可跨模型建立工作流程對象共用的新 ML.NET 環境。 概念上,DBContext 類似於 Entity Framework 中。

載入數據

ML.NET 中的資料會以 IDataView 介面來表示。 IDataView 是描述表格式數據(數值和文字)的彈性、有效率的方式。 數據可以從文本檔或其他來源(例如 SQL 資料庫或記錄檔)載入至 IDataView 物件。

  1. 在建立 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)和卷積神經網路(CNN)為基礎的進階和新穎演算法,可偵測時間序列上的異常。 如需此演算法的詳細資訊,請參閱 MicrosoftTime-Series 異常偵測服務。

在本教學課程中,您會看到這些程式可以使用兩個函式來完成。

偵測期間

在第一個步驟中,您會叫用 DetectSeasonality 函式來判斷數列的期間。

建立 DetectPeriod 方法

  1. 使用下列程式代碼,在 DetectPeriod 檔案底部建立 方法:

    int DetectPeriod(MLContext mlContext, IDataView phoneCalls)
    {
    
    }
    
  2. 使用 DetectSeasonality 函式來偵測期間。 請使用下列程式碼將其新增至 DetectPeriod 方法:

    int period = mlContext.AnomalyDetection.DetectSeasonality(phoneCalls, nameof(PhoneCallsData.value));
    
  3. 在 DetectPeriod 方法中新增以下程式碼為下一行程式碼,以顯示週期值:

    Console.WriteLine($"Period of the series is: {period}.");
    
  4. 傳回週期值。

    // <SnippetSetupSrCnnParameters>
    
  5. 在呼叫 DetectPeriod 方法下方,將下列呼叫新增至 LoadFromTextFile() 方法:

    int period = DetectPeriod(mlContext, dataView);
    

週期偵測結果

執行應用程式。 您的結果應該如下所示。

Period of the series is: 7.

偵測異常

在此步驟中,您會使用 DetectEntireAnomalyBySrCnn 方法來尋找異常狀況。

建立 DetectAnomaly 方法

  1. 使用下列程式代碼,在 DetectAnomaly 方法正下方建立 DetectPeriod 方法:

    void DetectAnomaly(MLContext mlContext, IDataView phoneCalls, int period)
    {
    
    }
    
  2. 使用下列程式代碼在 SrCnnEntireAnomalyDetectorOptions 方法中設定 DetectAnomaly:

    var options = new SrCnnEntireAnomalyDetectorOptions()
    {
        Threshold = 0.3,
        Sensitivity = 64.0,
        DetectMode = SrCnnDetectMode.AnomalyAndMargin,
        Period = period,
    };
    
  3. 藉由在 DetectAnomaly 方法中新增下列程式代碼行,以 SR-CNN 演算法偵測異常:

    var outputDataView = mlContext.AnomalyDetection.DetectEntireAnomalyBySrCnn(phoneCalls, nameof(PhoneCallsPrediction.Prediction), nameof(PhoneCallsData.value), options);
    
  4. 將結果數據視圖轉換成強型別 IEnumerable,使用 CreateEnumerable 方法和以下程式碼以更容易顯示。

    var predictions = mlContext.Data.CreateEnumerable<PhoneCallsPrediction>(
        outputDataView, reuseRowObject: false);
    
  5. 使用下列程式代碼建立顯示標頭,作為 DetectAnomaly 方法中的下一行:

    Console.WriteLine("Index,Data,Anomaly,AnomalyScore,Mag,ExpectedValue,BoundaryUnit,UpperBoundary,LowerBoundary");
    

    您會在變更點偵測結果中顯示下列資訊:

    • Index 是每個點的索引。
    • Anomaly 是是否偵測到每個點異常的指標。
    • ExpectedValue 是每個點的估計值。
    • LowerBoundary 是每個點可以不被視為異常的最低值。
    • UpperBoundary 是每個點的最高值,不可以是異常。
  6. 遍歷 predictionsIEnumerable 並使用下列程式代碼顯示結果:

    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("");
    
  7. 將下列呼叫新增至 DetectAnomaly 方法呼叫下方的 DetectPeriod() 方法:

    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 存放庫找到本教學課程的原始程式碼。

在本教學課程中,您已瞭解如何:

  • 載入數據
  • 偵測時間序列數據的週期
  • 偵測時間序列數據的異常

後續步驟

請查看 Machine Learning 範例 GitHub 存放庫,以探索耗電量異常偵測範例。