AI時代的網站與手機App建置與開發Part20 - 使用ML.NET進行異常偵測

 ·       摘要

從一堆資料中找出異常的資料一直是資料分析很重要的課題, 例如生產線需要偵測NG商品,  公司想要找出異常的出貨單, 銀行想找出異常的信用卡交易, 科學家想找出氣溫異常, 金管單位想找出股價急漲/急跌的異常, 超巿想找出營收或利潤異常等等都是異常偵測的應用場合.

: 找出資料中的異常

在這篇文章中, 我們將要為大家介紹如何使用ML.NET分析商品銷售資料的異常.

·       準備訓練資料

首先請啟動瀏覽器, 瀏覽至Product Sales Data下載商品銷售資料(下載網址: https://www.kaggle.com/datasets/ksabishek/product-sales-data?resource=download).

1所示即為下載得到的資料:

1: 商品銷售資料節錄

這份商品銷售資料的欄位詳細說明如下表:

欄位名稱

欄位說明

Date

商品銷售日期

QP1

商品1的銷售量

QP2

商品2的銷售量

QP3

商品3的銷售量

QP4

商品4的銷售量

SP1

商品1的銷售利潤

SP2

商品2的銷售利潤

SP3

商品3的銷售利潤

SP4

商品4的銷售利潤

·       使用ML.NET執行銷售資料的異常偵測

以下會以分析商品銷售資料中的QP1欄位的內容為例, 有興趣的讀者可以自行修改程式以分析其他欄位的資料是否異常.

·       定義描述商品銷售資料的ProductSalesData類別

internal class ProductSalesData

{

    [LoadColumn(1)]

    public string Date;

    [LoadColumn(2)]

    public float QP1;

}

·       定義描述異常偵測結果的ProductSalesPrediction類別

internal class ProductSalesPrediction

{

    [VectorType(3)]

    public double[] Prediction { get; set; }

}

·       實作偵測商品銷售資料的異常

private void btnProductSalesAnpmalyDetect_Click(object sender, RoutedEventArgs e)

{

    MLContext mlContext = new MLContext();

  // 載入資料

    IDataView ProductSales = mlContext.Data.LoadFromTextFile<ProductSalesData>(

"statsfinal.csv", separatorChar: ',', hasHeader: true);

    // 將訓練資料轉換成List集合

List<ProductSalesData> data =mlContext.Data.CreateEnumerable<ProductSalesData

    (ProductSales, reuseRowObject: false).ToList();

IDataView dataView = mlContext.Data.LoadFromEnumerable(data);

    //使用線形圖顯示銷售資料

PlotSalesChart(data);

    Trace.WriteLine("Detect temporary changes in pattern");

DetectSpike(mlContext, 36, dataView);

    Trace.WriteLine("Detect Persistent changes in pattern");

    DetectChangepoint(mlContext, 36, dataView);

}

private void DetectSpike(MLContext mlContext, int docSize, IDataView productSales)

{

var iidSpikeEstimator = mlContext.Transforms.DetectIidSpike(outputColumnName:

nameof(ProductSalesPrediction.Prediction), inputColumnName:

nameof(ProductSalesData.QP1), confidence: 95.0, pvalueHistoryLength: docSize / 4);

    // 訓練

    ITransformer iidSpikeTransform = iidSpikeEstimator.Fit(productSales);

    //取得訓練結果

    IDataView transformedData = iidSpikeTransform.Transform(productSales);

    var predictions = mlContext.Data.CreateEnumerable<ProductSalesPrediction>(

transformedData, reuseRowObject: false);

    //顯示預測結果

    Trace.WriteLine("Alert\tScore\tP-Value");

    foreach (var p in predictions)

    {

        var results={p.Prediction[0]}\t{p.Prediction[1]:f2}\t{p.Prediction[2]:F2}";

        if (p.Prediction[0] == 1)

        {

            results += " <-- Spike detected";

        }

        Trace.WriteLine(results);

    }

}

private void DetectChangepoint(MLContext mlContext, int docSize,

IDataView productSales)

{

var iidChangePointEstimator =

mlContext.Transforms.DetectIidChangePoint(outputColumnName: nameof(ProductSalesPrediction.Prediction), inputColumnName: nameof(ProductSalesData.QP1), confidence: 95.0, changeHistoryLength: docSize / 4);

    //訓練

    var iidChangePointTransform = iidChangePointEstimator.Fit(productSales);

    //取得訓練結果

    IDataView transformedData = iidChangePointTransform.Transform(productSales);

    var predictions = mlContext.Data.CreateEnumerable<ProductSalesPrediction>(

transformedData, reuseRowObject: false);

    //顯示預測結果

    Trace.WriteLine("Alert\tScore\tP-Value\tMartingale value");

    foreach (var p in predictions)

    {

        var results = $"{p.Prediction[0]}\t

{p.Prediction[1]:f2}\t{p.Prediction[2]:F2}\t{p.Prediction[3]:F2}";

        if (p.Prediction[0] == 1)

        {

            results += " <-- alert is on, predicted changepoint";

        }

        Trace.WriteLine(results);

    }

}

private void PlotSalesChart(List<ProductSalesData> productSales)

{

    //建立繪圖模型

    var plotModel = new PlotModel { Title = "Sales Data" };

    //建立線形圖需要的序列資料

    var lineSeries = new LineSeries

    {

        Title = "Sales",

        MarkerType = MarkerType.Circle,

        ItemsSource = productSales,

        Mapping = item =>

        {

            var data = (ProductSalesData)item;

            DateTime date;

            if (DateTime.TryParseExact(data.Date, "dd-MM-yyyy", null,

System.Globalization.DateTimeStyles.None, out date))

            {

                return new DataPoint(DateTimeAxis.ToDouble(date), data.QP1);

            }

            else

            {

                throw new FormatException(

$"The input string '{data.Date}' was not in a correct format.");

            }

        }

    };

    //將序列資料加入到繪圖模型

    plotModel.Series.Add(lineSeries);

    //建立顯示繪圖模型的PlotView檢視

    var plotView = new PlotView

    {

        Model = plotModel,

    };

    //顯示PlotView檢視

    Content = plotView;

}

上述的程式碼首先會以線形圖的方式顯示欲偵測的商品鋿售資料, 如圖2所示:

2: 以線形圖的方式顯示欲偵測的商品鋿售資料

然後分析商品銷售資料是否有短暫的銷售異常, 如圖3所示:


3:商品銷售有短暫銷售異常者會標示為1, 並加註<--Spike detected

接下來再分析商品銷售狀況是否有反轉點, 如圖4所示:

4: 商品銷售有反轉點者會標示為1, 並加註<--alert is on, predicted changepoint

範例下載:https://github.com/CraigIII/ProductSalesAnomalyDetection.git

留言

這個網誌中的熱門文章

AI時代的網站與手機App建置與開發Part27 - ML.NET與物件偵測

AI時代的網站與手機App建置與開發Part28 - 使用YOLO模型進行物件偵測

AI時代的網站與手機App建置與開發Part24 - ML.NET與圖片異常偵測