本文介绍如何在 .NET 6 环境下使用 ML.NET 检测时序数据的周期和异常,并以电话呼叫量数据 (phone.csv) 为例进行完整演示。本教程针对已有 C# 基础并希望学习 ML.NET 的读者。让我们开始吧!
检测时序中的异常通常涉及以下基本原理
时序数据中常见的需求之一就是检测“异常点”,例如在服务器访问量、传感器读数、电话呼叫量等数据中发现突然的异常值。这些异常点有可能是网络攻击、设备故障、或其他重要潜在事件。
ML.NET 提供了基于频谱残差 (SR) 和卷积神经网络 (CNN) 的 SR-CNN 算法,用于在时序中检测异常。此算法可以先帮我们自动检测周期,再评估剩余的部分是否存在异常点。
此外,这篇教程主要使用下列 NuGet 包:
将下载好的 phone.csv 文件放至项目的 “Data” 文件夹下。该数据集包含两列:
在解决方案中新建一个 “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; }
}
PhonePrediction 类包含多个属性,用于表示异常检测的输出结果,包括:
首先,我们需要检测时序的周期(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();
}
}
}
DetectPeriod 方法检测电话呼叫数据的周期性。这有助于理解数据中的潜在模式。DetectAnomaly 方法通过 SR-CNN 算法识别数据中的异常点。SR-CNN 算法的配置包括阈值和灵敏度等参数,以优化检测过程。在调用 DetectAnomaly 时,我们使用了 SrCnnEntireAnomalyDetectorOptions 来配置参数:
DetectPeriod 中捕获到的周期。SrCnnDetectMode.AnomalyAndMargin,在检测到异常时还可以提供误差上下限范围。
通过以上步骤,你已经成功使用 ML.NET 中的 SR-CNN 算法来检测电话呼叫量数据中的周期和异常点,希望对你的 ML.NET 学习之旅有所帮助!
祝学习愉快!如果你有更多问题,欢迎继续探索或提问。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!