编辑
2025-11-27
C#
00

目录

前言和背景
前提条件
准备数据集
5. 定义数据模型
6. 检测时序周期
7. 检测时序异常
8. 运行与结果
9. 后续步骤

本文介绍如何在 .NET 6 环境下使用 ML.NET 检测时序数据的周期和异常,并以电话呼叫量数据 (phone.csv) 为例进行完整演示。本教程针对已有 C# 基础并希望学习 ML.NET 的读者。让我们开始吧!


检测时序中的异常通常涉及以下基本原理

  1. 时序数据的特征:时序数据是按时间顺序排列的数据,通常包含时间戳和相应的数值。理解数据的基本特征(如趋势、季节性和周期性)是异常检测的第一步。
  2. 定义异常:异常通常被定义为与正常模式显著不同的数据点。这些异常可能是突发的(瞬时异常)或持续的(长期异常)。

前言和背景

时序数据中常见的需求之一就是检测“异常点”,例如在服务器访问量、传感器读数、电话呼叫量等数据中发现突然的异常值。这些异常点有可能是网络攻击、设备故障、或其他重要潜在事件。

ML.NET 提供了基于频谱残差 (SR) 和卷积神经网络 (CNN) 的 SR-CNN 算法,用于在时序中检测异常。此算法可以先帮我们自动检测周期,再评估剩余的部分是否存在异常点。


前提条件

  1. Visual Studio 2022(已安装 “.NET 桌面开发” 工作负载)
  2. .NET 6 SDK

此外,这篇教程主要使用下列 NuGet 包:


准备数据集

将下载好的 phone.csv 文件放至项目的 “Data” 文件夹下。该数据集包含两列:

  • timestamp:时间戳
  • value:对应时间的电话呼叫次数等数值

5. 定义数据模型

在解决方案中新建一个 “PhoneCallsData.cs” 文件,用于存放数据的输入、预测类型等模型类。

C#
/// <summary> /// 输入数据类(映射到 CSV 文件中的列) /// </summary> public class PhoneData { [LoadColumn(0)] public string? timestamp; [LoadColumn(1)] public double value; }
C#
/// <summary> /// 预测数据类,用于捕获异常检测输出 /// </summary> public class PhonePrediction { // Anomaly (0或1),表示是否为异常点 [VectorType(4)] public double[]? Prediction { get; set; } }
  1. 存储检测结果PhonePrediction 类包含多个属性,用于表示异常检测的输出结果,包括:
    • Anomaly Value:指示数据点是否被视为异常(0 或 1)。
    • Anomaly Score:表示异常程度的数值评分。
    • Magnitude (Mag):反映异常的影响或重要性。
    • Expected Value:基于模型的预测值。
    • Upper 和 Lower Boundaries:定义正常数据点的预期范围,通常通过预测值和影响度计算得出。

6. 检测时序周期

首先,我们需要检测时序的周期(seasonality)。在 ML.NET 中可以使用 DetectSeasonality 方法自动估算出周期。

Program.cs 中添加以下逻辑:

C#
using Microsoft.ML; using Microsoft.ML.TimeSeries; namespace App12 { internal class Program { static void Main(string[] args) { // 1. 创建 MLContext MLContext mlContext = new MLContext(); // 2. 加载数据集 string dataPath = Path.Combine(Environment.CurrentDirectory, "Data", "phone.csv"); IDataView dataView = mlContext.Data.LoadFromTextFile<PhoneData>( path: dataPath, hasHeader: true, separatorChar: ','); // 3. 检测周期 Console.WriteLine("正在检测该时序的周期..."); int period = DetectPeriod(mlContext, dataView); Console.WriteLine($"检测到的周期为: {period}"); // 4. 检测异常 Console.WriteLine("正在检测异常点..."); DetectAnomaly(mlContext, dataView, period); Console.WriteLine("检测完成,按任意键退出..."); Console.ReadKey(); } /// <summary> /// 采用 ML.NET 的 DetectSeasonality 方法检测时序数据的周期 /// </summary> /// <param name="mlContext"></param> /// <param name="phoneCalls"></param> /// <returns></returns> static int DetectPeriod(MLContext mlContext, IDataView phoneCalls) { int period = mlContext.AnomalyDetection.DetectSeasonality( phoneCalls, nameof(PhoneData.value)); return period; } /// <summary> /// 利用 SR-CNN 算法在时序中检测异常 /// </summary> /// <param name="mlContext"></param> /// <param name="phoneCalls"></param> /// <param name="period"></param> static void DetectAnomaly(MLContext mlContext, IDataView phoneCalls, int period) { // 1. 设置 SR-CNN 算法的配置 var options = new SrCnnEntireAnomalyDetectorOptions() { Threshold = 0.3, // 分数阈值,超过则判定为异常 Sensitivity = 64.0, // 灵敏度 DetectMode = SrCnnDetectMode.AnomalyAndMargin, Period = period, // 提前获得的周期 }; // 2. 检测异常 var outputDataView = mlContext.AnomalyDetection.DetectEntireAnomalyBySrCnn( phoneCalls, outputColumnName: nameof(PhoneCallsPrediction.Prediction), inputColumnName: nameof(PhoneData.value), options); // 3. 转换检测结果以便查看 var predictions = mlContext.Data.CreateEnumerable<PhoneCallsPrediction>( outputDataView, reuseRowObject: false); // 4. 打印结果 Console.WriteLine("Index,Data,Anomaly,AnomalyScore,Mag,ExpectedValue,BoundaryUnit,UpperBoundary,LowerBoundary"); int index = 0; foreach (var p in predictions) { // SR-CNN 输出的四个值分别为 // 1) AnomalyScore 2) Mag (影响度) // 3) ExpectedValue 4) BoundaryUnit // 这里我们第一个元素实际上是0或1(是不是异常),可以通过比对score与阈值来判断 double anomalyValue = p.Prediction?[0] ?? 0; double anomalyScore = p.Prediction?[1] ?? 0; double mag = p.Prediction?[2] ?? 0; double expValue = p.Prediction?[3] ?? 0; // 这里可以自定义上下边界的计算 double upperBoundary = expValue + mag; double lowerBoundary = expValue - mag; Console.WriteLine($"{index},{anomalyValue},{anomalyScore},{mag},{expValue},{upperBoundary},{lowerBoundary}"); index++; } Console.WriteLine(); } } }
  1. 检测周期:程序首先通过 DetectPeriod 方法检测电话呼叫数据的周期性。这有助于理解数据中的潜在模式。
  2. 异常检测:在确定周期后,程序使用 DetectAnomaly 方法通过 SR-CNN 算法识别数据中的异常点。SR-CNN 算法的配置包括阈值和灵敏度等参数,以优化检测过程。
  3. 输出结果:检测结果以结构化的格式打印,显示每个数据点的索引、异常状态、评分、预测值和边界。

7. 检测时序异常

在调用 DetectAnomaly 时,我们使用了 SrCnnEntireAnomalyDetectorOptions 来配置参数:

  • Threshold:用于判断异常的阈值,通常越低越容易触发异常;数值越高,检测到的异常点就更少。
  • Sensitivity:算法对数据波动的敏感度,数值越大越敏感。
  • Period:对应我们在 DetectPeriod 中捕获到的周期。
  • DetectMode:这里选择 SrCnnDetectMode.AnomalyAndMargin,在检测到异常时还可以提供误差上下限范围。

8. 运行与结果

image.png

  1. Anomaly Value (异常值)
    • 这是一个二元值(0或1),指示数据点是否被视为异常。值为1表示该数据点被检测为异常,值为0则表示正常。
  2. Anomaly Score (异常评分)
    • 这是一个数值评分,反映了数据点的异常程度。分数越高,表示该数据点越可能是异常。这个评分有助于评估异常的严重性。
  3. Magnitude (Mag,影响度)
    • 该值表示异常的影响或重要性。它可以帮助理解异常对整体数据趋势的影响程度,数值越大,表示异常的影响越显著。
  4. Expected Value (预期值)
    • 这是模型对正常情况下该数据点的预测值。它提供了一个基准,用于比较实际观测值与模型预测值之间的差异。
  5. Upper Boundary (上界)Lower Boundary (下界)
    • 这两个值定义了正常数据点的预期范围。通常通过将影响度(Magnitude)加到预期值上得到上界,而下界则是通过从预期值中减去影响度得到。这些边界有助于判断数据点是否在正常范围内。

9. 后续步骤

  • 如果想要深入了解该方法的原理,可阅读 Microsoft 的时序异常检测服务论文
  • 还可以尝试其他数据集,例如能耗数据或产品销售数据,观察参数对异常检测结果的影响。
  • 官方示例库中可以找到更多参考。
  • 也可进一步研究其他 ML.NET 功能,如聚类、分类、回归、推荐系统等。

通过以上步骤,你已经成功使用 ML.NET 中的 SR-CNN 算法来检测电话呼叫量数据中的周期和异常点,希望对你的 ML.NET 学习之旅有所帮助!

祝学习愉快!如果你有更多问题,欢迎继续探索或提问。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!