你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
detect_anomalous_spike_fl()
适用于:✅Microsoft Fabric✅Azure 数据资源管理器✅Azure Monitor✅Microsoft Sentinel
检测时间戳数据中数值变量异常峰值的外观。
函数 detect_anomalous_spike_fl()
是 UDF(用户定义的函数),用于检测数值变量(例如外泄数据量或登录尝试失败)中异常峰值的外观(如流量日志)。 在网络安全上下文中,此类事件可能可疑,并指示潜在的攻击或泄露。
异常模型基于两个分数的组合:Z 分数(高于平均值的标准偏差数)和 Q 分数(在高分位以上的交叉范围数)。 Z 分数是一个简单且常见的离群值指标;Q 分数基于 Tukey 的围栏 , 但我们将定义扩展到任何分量, 以获得更多控制。 选择不同的分位数(默认情况下,使用第 95 个和第 25 个分位数)可以检测更重要的离群值,从而提高精度。 该模型基于某些数值变量构建,按范围(例如订阅或帐户)以及每个实体(例如用户或设备)进行计算。
在计算单变量数值数据点的分数并检查其他要求(例如,范围训练期间的活动天数高于预定义阈值)之后,我们将检查每个分数是否高于其预定义阈值。 如果是这样,则会检测到峰值,并将数据点标记为异常。 生成两个模型:一个用于实体级别(由 entityColumnName 参数定义)(例如每个范围的用户或设备(由 scopeColumnName 参数定义)(例如帐户或订阅)。 第二个模型是为整个范围构建的。 异常情况检测逻辑针对每个模型执行,如果在其中一个模型中检测到异常,则会显示该逻辑。 默认情况下,检测到向上峰值;在某些上下文中,向下峰值('dips')也可能很有趣,并且可以通过调整逻辑来检测。
模型的直接输出是基于分数的异常分数。 分数在 [0, 1] 范围内单调,1 表示异常。 除了异常分数之外,还有一个二进制标志用于检测到的异常(由最小阈值参数控制)和其他解释字段。
请注意,该函数忽略变量的时态结构(主要用于可伸缩性和可解释性)。 如果变量具有重要的时态组件(如趋势和季节性),我们建议考虑 series_decompose_anomalies() 函数,或使用 series_decompose() 来计算残差并在其上执行 detect_anomalous_spike_fl()
。
语法
detect_anomalous_spike_fl(
numericColumnName, entityColumnName, scopeColumnName, timeColumnName, startTraining, startDetection, endDetection, [minTrainingDaysThresh], [lowPercentileForQscore], [highPercentileForQscore], [minSlicesPerEntity], [zScoreThreshEntity], [qScoreThreshEntity], [minNumValueThreshEntity], [minSlicesPerScope], [zScoreThreshScope], [qScoreThreshScope], [minNumValueThreshScope])
详细了解
参数
名字 | 类型 | 必填 | 描述 |
---|---|---|---|
numericColumnName | string |
✔️ | 包含计算异常模型的数值变量的输入表列的名称。 |
entityColumnName | string |
✔️ | 输入表列的名称,其中包含计算异常模型的实体的名称或 ID。 |
scopeColumnName | string |
✔️ | 包含分区或范围的输入表列的名称,以便为每个范围生成不同的异常模型。 |
timeColumnName | string |
✔️ | 包含时间戳的输入表列的名称,用于定义训练和检测周期。 |
startTraining | datetime |
✔️ | 异常模型的训练期的开始。 其结束由检测周期的开始定义。 |
startDetection | datetime |
✔️ | 异常情况检测的检测周期的开始。 |
endDetection | datetime |
✔️ | 异常检测的检测期结束。 |
minTrainingDaysThresh | int |
用于计算异常的范围所存在的训练期间的最低天数。 如果它低于阈值,则范围被视为太新且未知,因此不会计算异常。 默认值为 14。 | |
lowPercentileForQscore | real |
[0.0,1.0] 范围内的数字,表示要计算为 Q 分数的低限制的百分位。 在 Tukey 的围栏中,使用 0.25。 默认值为 0.25。 选择较低的百分位可以提高精度,因为检测到更严重的异常。 | |
highPercentileForQscore | real |
[0.0,1.0] 范围内的数字,表示要计算为 Q 分数的高限制的百分位。 在 Tukey 的围栏中,使用 0.75。 默认值为 0.9。 选择更高的百分位可以提高精度,因为检测到更严重的异常。 | |
minSlicesPerEntity | int |
在为实体生成异常模型之前,要存在的“切片”(例如,天)的最低阈值。 如果数字低于阈值,则实体被视为太新且不稳定。 默认值为 20。 | |
zScoreThreshEntity | real |
要标记为异常的实体级别 Z 分数(高于平均值的标准偏差数)的最低阈值。 选择更高的值时,只会检测到更严重的异常。 默认值为 3.0。 | |
qScoreThreshEntity | real |
要标记为异常的实体级 Q 分数(高于高分位数的跨分位数范围)的最低阈值。 选择更高的值时,只会检测到更严重的异常。 默认值为 2.0。 | |
minNumValueThreshEntity | long |
要标记为实体异常的数字变量的最小阈值。 当值在统计上异常(Z 分数高和 Q 分数),但值本身太小而无法有趣时,这非常有用。 默认值为 0。 | |
minSlicesPerScope | int |
在为范围生成异常模型之前,要存在的“切片”(例如,天)的最低阈值。 如果数字低于阈值,则范围被视为太新且不稳定。 默认值为 20。 | |
zScoreThreshScope | real |
要标记为异常的范围级别 Z 分数(高于平均值的标准偏差数)的最低阈值。 选择更高的值时,只会检测到更严重的异常。 默认值为 3.0。 | |
qScoreThreshScope | real |
要标记为异常的范围级别 Q 分数(高于高分位的范围之间的分量范围数)的最低阈值。 选择更高的值时,只会检测到更严重的异常。 默认值为 2.0。 | |
minNumValueThreshScope | long |
要标记为作用域异常的数字变量的最小阈值。 当值在统计上异常(Z 分数高和 Q 分数),但值本身太小而无法有趣时,这非常有用。 默认值为 0。 |
函数定义
可以通过将函数代码嵌入为查询定义的函数,或将其创建为数据库中的存储函数来定义函数,如下所示:
- 查询定义的
- 存储
使用以下 let 语句定义函数。 不需要任何权限。
let detect_anomalous_spike_fl = (T:(*), numericColumnName:string, entityColumnName:string, scopeColumnName:string
, timeColumnName:string, startTraining:datetime, startDetection:datetime, endDetection:datetime, minTrainingDaysThresh:int = 14
, lowPercentileForQscore:real = 0.25, highPercentileForQscore:real = 0.9
, minSlicesPerEntity:int = 20, zScoreThreshEntity:real = 3.0, qScoreThreshEntity:real = 2.0, minNumValueThreshEntity:long = 0
, minSlicesPerScope:int = 20, zScoreThreshScope:real = 3.0, qScoreThreshScope:real = 2.0, minNumValueThreshScope:long = 0)
{
// pre-process the input data by adding standard column names and dividing to datasets
let timePeriodBinSize = 'day'; // we assume a reasonable bin for time is day
let processedData = (
T
| extend scope = column_ifexists(scopeColumnName, '')
| extend entity = column_ifexists(entityColumnName, '')
| extend numVec = tolong(column_ifexists(numericColumnName, 0))
| extend sliceTime = todatetime(column_ifexists(timeColumnName, ''))
| where isnotempty(scope) and isnotempty(sliceTime)
| extend dataSet = case((sliceTime >= startTraining and sliceTime < startDetection), 'trainSet'
, sliceTime >= startDetection and sliceTime <= endDetection, 'detectSet'
, 'other')
| where dataSet in ('trainSet', 'detectSet')
);
let aggregatedCandidateScopeData = (
processedData
| summarize firstSeenScope = min(sliceTime), lastSeenScope = max(sliceTime) by scope
| extend slicesInTrainingScope = datetime_diff(timePeriodBinSize, startDetection, firstSeenScope)
| where slicesInTrainingScope >= minTrainingDaysThresh and lastSeenScope >= startDetection
);
let entityModelData = (
processedData
| join kind = inner (aggregatedCandidateScopeData) on scope
| where dataSet == 'trainSet'
| summarize countSlicesEntity = dcount(sliceTime), avgNumEntity = avg(numVec), sdNumEntity = stdev(numVec)
, lowPrcNumEntity = percentile(numVec, lowPercentileForQscore), highPrcNumEntity = percentile(numVec, highPercentileForQscore)
, firstSeenEntity = min(sliceTime), lastSeenEntity = max(sliceTime)
by scope, entity
| extend slicesInTrainingEntity = datetime_diff(timePeriodBinSize, startDetection, firstSeenEntity)
);
let scopeModelData = (
processedData
| join kind = inner (aggregatedCandidateScopeData) on scope
| where dataSet == 'trainSet'
| summarize countSlicesScope = dcount(sliceTime), avgNumScope = avg(numVec), sdNumScope = stdev(numVec)
, lowPrcNumScope = percentile(numVec, lowPercentileForQscore), highPrcNumScope = percentile(numVec, highPercentileForQscore)
by scope
);
let resultsData = (
processedData
| where dataSet == 'detectSet'
| join kind = inner (aggregatedCandidateScopeData) on scope
| join kind = leftouter (entityModelData) on scope, entity
| join kind = leftouter (scopeModelData) on scope
| extend zScoreEntity = iff(countSlicesEntity >= minSlicesPerEntity, round((toreal(numVec) - avgNumEntity)/(sdNumEntity + 1), 2), 0.0)
, qScoreEntity = iff(countSlicesEntity >= minSlicesPerEntity, round((toreal(numVec) - highPrcNumEntity)/(highPrcNumEntity - lowPrcNumEntity + 1), 2), 0.0)
, zScoreScope = iff(countSlicesScope >= minSlicesPerScope, round((toreal(numVec) - avgNumScope)/(sdNumScope + 1), 2), 0.0)
, qScoreScope = iff(countSlicesScope >= minSlicesPerScope, round((toreal(numVec) - highPrcNumScope)/(highPrcNumScope - lowPrcNumScope + 1), 2), 0.0)
| extend isSpikeOnEntity = iff((slicesInTrainingEntity >= minTrainingDaysThresh and zScoreEntity > zScoreThreshEntity and qScoreEntity > qScoreThreshEntity and numVec >= minNumValueThreshEntity), 1, 0)
, entityHighBaseline= round(max_of((avgNumEntity + sdNumEntity), highPrcNumEntity), 2)
, isSpikeOnScope = iff((countSlicesScope >= minTrainingDaysThresh and zScoreScope > zScoreThreshScope and qScoreScope > qScoreThreshScope and numVec >= minNumValueThreshScope), 1, 0)
, scopeHighBaseline = round(max_of((avgNumEntity + 2 * sdNumEntity), highPrcNumScope), 2)
| extend entitySpikeAnomalyScore = iff(isSpikeOnEntity == 1, round(1.0 - 0.25/(max_of(zScoreEntity, qScoreEntity)),4), 0.00)
, scopeSpikeAnomalyScore = iff(isSpikeOnScope == 1, round(1.0 - 0.25/(max_of(zScoreScope, qScoreScope)), 4), 0.00)
| where isSpikeOnEntity == 1 or isSpikeOnScope == 1
| extend avgNumEntity = round(avgNumEntity, 2), sdNumEntity = round(sdNumEntity, 2)
, avgNumScope = round(avgNumScope, 2), sdNumScope = round(sdNumScope, 2)
| project-away entity1, scope1, scope2, scope3
| extend anomalyType = iff(isSpikeOnEntity == 1, strcat('spike_', entityColumnName), strcat('spike_', scopeColumnName)), anomalyScore = max_of(entitySpikeAnomalyScore, scopeSpikeAnomalyScore)
| extend anomalyExplainability = iff(isSpikeOnEntity == 1
, strcat('The value of numeric variable ', numericColumnName, ' for ', entityColumnName, ' ', entity, ' is ', numVec, ', which is abnormally high for this '
, entityColumnName, ' at this ', scopeColumnName
, '. Based on observations from last ' , slicesInTrainingEntity, ' ', timePeriodBinSize, 's, the expected baseline value is below ', entityHighBaseline, '.')
, strcat('The value of numeric variable ', numericColumnName, ' on ', scopeColumnName, ' ', scope, ' is ', numVec, ', which is abnormally high for this '
, scopeColumnName, '. Based on observations from last ' , slicesInTrainingScope, ' ', timePeriodBinSize, 's, the expected baseline value is below ', scopeHighBaseline, '.'))
| extend anomalyState = iff(isSpikeOnEntity == 1
, bag_pack('avg', avgNumEntity, 'stdev', sdNumEntity, strcat('percentile_', lowPercentileForQscore), lowPrcNumEntity, strcat('percentile_', highPercentileForQscore), highPrcNumEntity)
, bag_pack('avg', avgNumScope, 'stdev', sdNumScope, strcat('percentile_', lowPercentileForQscore), lowPrcNumScope, strcat('percentile_', highPercentileForQscore), highPrcNumScope))
| project-away lowPrcNumEntity, highPrcNumEntity, lowPrcNumScope, highPrcNumScope
);
resultsData
};
// Write your query to use the function here.
例
以下示例使用 调用运算符 来运行函数。
- 查询定义的
- 存储
若要使用查询定义的函数,请调用嵌入的函数定义之后。
let detect_anomalous_spike_fl = (T:(*), numericColumnName:string, entityColumnName:string, scopeColumnName:string
, timeColumnName:string, startTraining:datetime, startDetection:datetime, endDetection:datetime, minTrainingDaysThresh:int = 14
, lowPercentileForQscore:real = 0.25, highPercentileForQscore:real = 0.9
, minSlicesPerEntity:int = 20, zScoreThreshEntity:real = 3.0, qScoreThreshEntity:real = 2.0, minNumValueThreshEntity:long = 0
, minSlicesPerScope:int = 20, zScoreThreshScope:real = 3.0, qScoreThreshScope:real = 2.0, minNumValueThreshScope:long = 0)
{
// pre-process the input data by adding standard column names and dividing to datasets
let timePeriodBinSize = 'day'; // we assume a reasonable bin for time is day
let processedData = (
T
| extend scope = column_ifexists(scopeColumnName, '')
| extend entity = column_ifexists(entityColumnName, '')
| extend numVec = tolong(column_ifexists(numericColumnName, 0))
| extend sliceTime = todatetime(column_ifexists(timeColumnName, ''))
| where isnotempty(scope) and isnotempty(sliceTime)
| extend dataSet = case((sliceTime >= startTraining and sliceTime < startDetection), 'trainSet'
, sliceTime >= startDetection and sliceTime <= endDetection, 'detectSet'
, 'other')
| where dataSet in ('trainSet', 'detectSet')
);
let aggregatedCandidateScopeData = (
processedData
| summarize firstSeenScope = min(sliceTime), lastSeenScope = max(sliceTime) by scope
| extend slicesInTrainingScope = datetime_diff(timePeriodBinSize, startDetection, firstSeenScope)
| where slicesInTrainingScope >= minTrainingDaysThresh and lastSeenScope >= startDetection
);
let entityModelData = (
processedData
| join kind = inner (aggregatedCandidateScopeData) on scope
| where dataSet == 'trainSet'
| summarize countSlicesEntity = dcount(sliceTime), avgNumEntity = avg(numVec), sdNumEntity = stdev(numVec)
, lowPrcNumEntity = percentile(numVec, lowPercentileForQscore), highPrcNumEntity = percentile(numVec, highPercentileForQscore)
, firstSeenEntity = min(sliceTime), lastSeenEntity = max(sliceTime)
by scope, entity
| extend slicesInTrainingEntity = datetime_diff(timePeriodBinSize, startDetection, firstSeenEntity)
);
let scopeModelData = (
processedData
| join kind = inner (aggregatedCandidateScopeData) on scope
| where dataSet == 'trainSet'
| summarize countSlicesScope = dcount(sliceTime), avgNumScope = avg(numVec), sdNumScope = stdev(numVec)
, lowPrcNumScope = percentile(numVec, lowPercentileForQscore), highPrcNumScope = percentile(numVec, highPercentileForQscore)
by scope
);
let resultsData = (
processedData
| where dataSet == 'detectSet'
| join kind = inner (aggregatedCandidateScopeData) on scope
| join kind = leftouter (entityModelData) on scope, entity
| join kind = leftouter (scopeModelData) on scope
| extend zScoreEntity = iff(countSlicesEntity >= minSlicesPerEntity, round((toreal(numVec) - avgNumEntity)/(sdNumEntity + 1), 2), 0.0)
, qScoreEntity = iff(countSlicesEntity >= minSlicesPerEntity, round((toreal(numVec) - highPrcNumEntity)/(highPrcNumEntity - lowPrcNumEntity + 1), 2), 0.0)
, zScoreScope = iff(countSlicesScope >= minSlicesPerScope, round((toreal(numVec) - avgNumScope)/(sdNumScope + 1), 2), 0.0)
, qScoreScope = iff(countSlicesScope >= minSlicesPerScope, round((toreal(numVec) - highPrcNumScope)/(highPrcNumScope - lowPrcNumScope + 1), 2), 0.0)
| extend isSpikeOnEntity = iff((slicesInTrainingEntity >= minTrainingDaysThresh and zScoreEntity > zScoreThreshEntity and qScoreEntity > qScoreThreshEntity and numVec >= minNumValueThreshEntity), 1, 0)
, entityHighBaseline= round(max_of((avgNumEntity + sdNumEntity), highPrcNumEntity), 2)
, isSpikeOnScope = iff((countSlicesScope >= minTrainingDaysThresh and zScoreScope > zScoreThreshScope and qScoreScope > qScoreThreshScope and numVec >= minNumValueThreshScope), 1, 0)
, scopeHighBaseline = round(max_of((avgNumEntity + 2 * sdNumEntity), highPrcNumScope), 2)
| extend entitySpikeAnomalyScore = iff(isSpikeOnEntity == 1, round(1.0 - 0.25/(max_of(zScoreEntity, qScoreEntity)),4), 0.00)
, scopeSpikeAnomalyScore = iff(isSpikeOnScope == 1, round(1.0 - 0.25/(max_of(zScoreScope, qScoreScope)), 4), 0.00)
| where isSpikeOnEntity == 1 or isSpikeOnScope == 1
| extend avgNumEntity = round(avgNumEntity, 2), sdNumEntity = round(sdNumEntity, 2)
, avgNumScope = round(avgNumScope, 2), sdNumScope = round(sdNumScope, 2)
| project-away entity1, scope1, scope2, scope3
| extend anomalyType = iff(isSpikeOnEntity == 1, strcat('spike_', entityColumnName), strcat('spike_', scopeColumnName)), anomalyScore = max_of(entitySpikeAnomalyScore, scopeSpikeAnomalyScore)
| extend anomalyExplainability = iff(isSpikeOnEntity == 1
, strcat('The value of numeric variable ', numericColumnName, ' for ', entityColumnName, ' ', entity, ' is ', numVec, ', which is abnormally high for this '
, entityColumnName, ' at this ', scopeColumnName
, '. Based on observations from last ' , slicesInTrainingEntity, ' ', timePeriodBinSize, 's, the expected baseline value is below ', entityHighBaseline, '.')
, strcat('The value of numeric variable ', numericColumnName, ' on ', scopeColumnName, ' ', scope, ' is ', numVec, ', which is abnormally high for this '
, scopeColumnName, '. Based on observations from last ' , slicesInTrainingScope, ' ', timePeriodBinSize, 's, the expected baseline value is below ', scopeHighBaseline, '.'))
| extend anomalyState = iff(isSpikeOnEntity == 1
, bag_pack('avg', avgNumEntity, 'stdev', sdNumEntity, strcat('percentile_', lowPercentileForQscore), lowPrcNumEntity, strcat('percentile_', highPercentileForQscore), highPrcNumEntity)
, bag_pack('avg', avgNumScope, 'stdev', sdNumScope, strcat('percentile_', lowPercentileForQscore), lowPrcNumScope, strcat('percentile_', highPercentileForQscore), highPrcNumScope))
| project-away lowPrcNumEntity, highPrcNumEntity, lowPrcNumScope, highPrcNumScope
);
resultsData
};
let detectPeriodStart = datetime(2022-04-30 05:00:00.0000000);
let trainPeriodStart = datetime(2022-03-01 05:00);
let names = pack_array("Admin", "Dev1", "Dev2", "IT-support");
let countNames = array_length(names);
let testData = range t from 1 to 24*60 step 1
| extend timeSlice = trainPeriodStart + 1h * t
| extend countEvents = round(2*rand() + iff((t/24)%7>=5, 10.0, 15.0) - (((t%24)/10)*((t%24)/10)), 2) * 100
| extend userName = tostring(names[toint(rand(countNames))])
| extend deviceId = hash_md5(rand())
| extend accountName = iff(((rand() < 0.2) and (timeSlice < detectPeriodStart)), 'testEnvironment', 'prodEnvironment')
| extend userName = iff(timeSlice == detectPeriodStart, 'H4ck3r', userName)
| extend countEvents = iff(timeSlice == detectPeriodStart, 3*countEvents, countEvents)
| sort by timeSlice desc
;
testData
| invoke detect_anomalous_spike_fl(numericColumnName = 'countEvents'
, entityColumnName = 'userName'
, scopeColumnName = 'accountName'
, timeColumnName = 'timeSlice'
, startTraining = trainPeriodStart
, startDetection = detectPeriodStart
, endDetection = detectPeriodStart
)
输出
t | timeSlice | countEvents | userName | deviceId | accountName | 范围 | 实体 | numVec | sliceTime | 数据 | firstSeenScope | lastSeenScope | slicesInTrainingScope | countSlicesEntity | avgNumEntity | sdNumEntity | firstSeenEntity | lastSeenEntity | slicesInTrainingEntity | countSlicesScope | avgNumScope | sdNumScope | zScoreEntity | qScoreEntity | zScoreScope | qScoreScope | isSpikeOnEntity | entityHighBaseline | isSpikeOnScope | scopeHighBaseline | entitySpikeAnomalyScore | scopeSpikeAnomalyScore | anomalyType | anomalyScore | anomalyExplainability | anomalyState |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1440 | 2022-04-30 05:00:00.0000000 | 5079 | H4ck3r | 9e8e151aced5a64938b93ee0c13fe940 | prodEnvironment | prodEnvironment | H4ck3r | 5079 | 2022-04-30 05:00:00.0000000 | detectSet | 2022-03-01 08:00:00.0000000 | 2022-04-30 05:00:00.0000000 | 60 | 1155 | 1363.22 | 267.51 | 0 | 0 | 13.84 | 185.46 | 0 | 1 | 628 | 0 | 0.9987 | spike_accountName | 0.9987 | accountName prodEnvironment 上的数值变量 countEvents 的值为 5079,此 accountName 异常高。 根据过去 60 天的观察结果,预期基线值低于 628.0。 | {“avg”: 1363.22,“stdev”: 267.51,“percentile_0.25”: 605,“percentile_0.9”: 628} |
运行函数的输出是检测数据集中的行,这些行标记为作用域或实体级别的异常峰值。 为了清楚起见,添加了一些其他字段:
-
dataSet
:当前数据集(始终detectSet
)。 -
firstSeenScope
:首次看到作用域时的时间戳。 -
lastSeenScope
:上次看到范围时的时间戳。 -
slicesInTrainingScope
:训练数据集中存在作用域的切片数(例如天数)。 -
countSlicesEntity
:实体在作用域上存在的切片数(例如天数)。 -
avgNumEntity
:范围上每个实体训练集中的数字变量的平均值。 -
sdNumEntity
:范围上每个实体训练集中数值变量的标准偏差。 -
firstSeenEntity
:在作用域上首次看到实体时的时间戳。 -
lastSeenEntity
:在作用域上最后看到实体时的时间戳。 -
slicesInTrainingEntity
:训练数据集中实体存在于范围的切片数(例如天数)。 -
countSlicesScope
:范围存在的切片数(例如天数)。 -
avgNumScope
:每个范围定型集中数值变量的平均值。 -
sdNumScope
:每个范围定型集中数值变量的标准偏差。 -
zScoreEntity
:基于实体模型的数字变量的当前值的 Z 分数。 -
qScoreEntity
:基于实体模型的数字变量的当前值的 Q 分数。 -
zScoreScope
:基于范围模型的数字变量的当前值的 Z 分数。 -
qScoreScope
:基于范围模型的数字变量的当前值的 Q 分数。 -
isSpikeOnEntity
:基于实体模型的异常峰值的二进制标志。 -
entityHighBaseline
:基于实体模型的数字变量值的预期高基线。 -
isSpikeOnScope
:基于范围模型异常峰值的二进制标志。 -
scopeHighBaseline
:基于范围模型的数字变量值的预期高基线。 -
entitySpikeAnomalyScore
:基于实体模型的峰值异常分数;范围 [0,1] 中的数字,较高的值表示更多的异常。 -
scopeSpikeAnomalyScore
:基于范围模型的峰值异常分数;范围 [0,1] 中的数字,较高的值表示更多的异常。 -
anomalyType
:显示异常类型(在一起运行多个异常情况检测逻辑时很有用)。 -
anomalyScore
:基于所选模型的峰值异常评分。 -
anomalyExplainability
:生成的异常及其解释的文本包装器。 -
anomalyState
:所选模型(平均值、标准偏差和百分位)描述模型的指标包。
在上面的示例中,在 countEvents 变量上使用用户作为实体和帐户作为作用域运行此函数,默认参数会检测范围级别的峰值。 由于用户“H4ck3r”在训练期间没有足够的数据,则不会针对实体级别计算异常,并且所有相关字段均为空。 范围级别异常的异常分数为 0.998,这意味着此峰值对于范围来说是异常的。
如果我们提高到足够高的最小阈值中的任何一个,则不会检测到任何异常,因为要求过高。
输出显示具有异常峰值的行以及采用标准化格式的说明字段。 这些字段可用于调查异常情况,以及针对多个数值变量运行异常峰值检测或一起运行其他算法。
网络安全上下文中的建议用法针对有意义的数值变量(下载的数据量、上传的文件计数或登录尝试失败)运行函数(例如帐户订阅)和实体(如用户或设备)。 检测到的异常峰值意味着数值高于该范围或实体的预期值,并且可能可疑。