Compartilhar via


detect_anomalous_spike_fl()

Aplica-se a: ✅Microsoft FabricAzure Data ExplorerAzure MonitorMicrosoft Sentinel

Detecte a aparência de picos anômalos em variáveis numéricas em dados com carimbo de data/hora.

A função detect_anomalous_spike_fl() é uma UDF (função definida pelo usuário) que detecta a aparência de picos anômalos em variáveis numéricas - como quantidade de dados exfiltrados ou tentativas de entrada com falha - em dados com carimbo de data/hora, como logs de tráfego. No contexto de segurança cibernética, esses eventos podem ser suspeitos e indicar um possível ataque ou comprometimento.

O modelo de anomalias baseia-se em uma combinação de duas pontuações: pontuação Z (o número de desvios padrão acima da média) e pontuação Q (o número de intervalos interquantiles acima de um quantile alto). A pontuação Z é uma métrica simples e comum de exceção; A pontuação Q é baseada nas cercas de Tukey - mas estendemos a definição a quaisquer quantiles para mais controle. Escolher quantiles diferentes (por padrão, quantiles 95 e 25 são usados) permite detectar exceções mais significativas, melhorando assim a precisão. O modelo é criado com base em alguma variável numérica (como o número de tentativas de logon ou a quantidade de dados exfiltrados) e é calculado por escopo (como assinatura ou conta) e por entidade (como usuário ou dispositivo).

Depois de calcular as pontuações de um ponto de dados numérico de variação única e verificar outros requisitos (por exemplo, o número de dias ativos no período de treinamento no escopo está acima de um limite predefinido), verificamos se cada uma das pontuações está acima do limite predefinido. Nesse caso, um pico é detectado e o ponto de dados é sinalizado como anômalo. Dois modelos são criados: um para o nível de entidade (definido pelo parâmetro entityColumnName) - como usuário ou dispositivo por escopo (definido pelo parâmetro scopeColumnName) - como conta ou assinatura. O segundo modelo é criado para todo o escopo. A lógica de detecção de anomalias é executada para cada modelo e, se a anomalia for detectada em um deles, ela será mostrada. Por padrão, picos para cima são detectados; picos descendentes ('dips') também podem ser interessantes em alguns contextos e podem ser detectados adaptando a lógica.

A saída direta do modelo é uma pontuação de anomalias com base nas pontuações. A pontuação é monótona no intervalo de [0, 1], com 1 representando algo anômalo. Além da pontuação de anomalias, há um sinalizador binário para anomalias detectadas (controlada por um parâmetro de limite mínimo) e outros campos explicativos.

Observe que a função desconsidera a estrutura temporal da variável (principalmente para escalabilidade e explicabilidade). Se a variável tiver componentes temporais significativos , como tendência e sazonalidades, sugerimos considerar a função series_decompose_anomalies() ou usar series_decompose() para calcular o residual e executar sobre ela.

Sintaxe

detect_anomalous_spike_fl( numericColumnName, entityColumnName, scopeColumnName, timeColumnName, startTraining, startDetection, endDetection, [minTrainingDaysThresh], [lowPercentileForQscore], [highPercentileForQscore], [minSlicesPerEntity], [zScoreThreshEntity], [qScoreThreshEntity], [minNumValueThreshEntity], [minSlicesPerScope], [zScoreThreshScope], [qScoreThreshScope], [minNumValueThreshScope])

Saiba mais sobre convenções de sintaxe.

Parâmetros

Nome Tipo Necessário Descrição
numericColumnName string ✔️ O nome da coluna da tabela de entrada que contém variável numérica para a qual os modelos de anomalias são calculados.
entityColumnName string ✔️ O nome da coluna da tabela de entrada que contém os nomes ou IDs das entidades para as quais o modelo de anomalias é calculado.
scopeColumnName string ✔️ O nome da coluna da tabela de entrada que contém a partição ou o escopo, de modo que um modelo de anomalias diferente seja criado para cada escopo.
timeColumnName string ✔️ O nome da coluna da tabela de entrada que contém os carimbos de data/hora, que são usados para definir os períodos de treinamento e detecção.
startTraining datetime ✔️ O início do período de treinamento para o modelo de anomalias. Seu final é definido pelo início do período de detecção.
startDetection datetime ✔️ O início do período de detecção para detecção de anomalias.
endDetection datetime ✔️ O fim do período de detecção para detecção de anomalias.
minTrainingDaysThresh int O número mínimo de dias no período de treinamento que existe um escopo para calcular anomalias. Se estiver abaixo do limite, o escopo será considerado muito novo e desconhecido, portanto, as anomalias não serão calculadas. O valor padrão é 14.
lowPercentileForQscore real Um número no intervalo [0,0,1,0] que representa o percentil a ser calculado como um limite baixo para a pontuação Q. Nas cercas de Tukey, 0,25 é usado. O valor padrão é 0,25. Escolher um percentil mais baixo melhora a precisão à medida que anomalias mais significativas são detectadas.
highPercentileForQscore real Um número no intervalo [0,0,1,0] que representa o percentil a ser calculado como limite alto para pontuação Q. Nas cercas de Tukey, 0,75 é usado. O valor padrão é 0,9. Escolher um percentil mais alto melhora a precisão à medida que anomalias mais significativas são detectadas.
minSlicesPerEntity int O limite mínimo de 'fatias' (por exemplo, dias) a existir em uma entidade antes que o modelo de anomalia seja criado para ele. Se o número estiver abaixo do limite, a entidade será considerada muito nova e instável. O valor padrão é 20.
zScoreThreshEntity real O limite mínimo para que a pontuação Z no nível da entidade (número de desvios padrão acima da média) seja sinalizada como anomalia. Ao escolher valores mais altos, apenas anomalias mais significativas são detectadas. O valor padrão é 3.0.
qScoreThreshEntity real O limite mínimo para que a pontuação Q no nível da entidade (número de intervalos interquantiles acima do quantile alto) seja sinalizada como anomalia. Ao escolher valores mais altos, apenas anomalias mais significativas são detectadas. O valor padrão é 2.0.
minNumValueThreshEntity long O limite mínimo para que a variável numérica seja sinalizada como anomalia para uma entidade. Isso é útil para filtrar casos em que um valor é anormal estatisticamente (pontuação Z alta e pontuação em Q), mas o valor em si é muito pequeno para ser interessante. O valor padrão é 0.
minSlicesPerScope int O limite mínimo de 'fatias' (por exemplo, dias) a existir em um escopo antes que o modelo de anomalia seja criado para ele. Se o número estiver abaixo do limite, o escopo será considerado muito novo e instável. O valor padrão é 20.
zScoreThreshScope real O limite mínimo para pontuação Z no nível do escopo (número de desvios padrão acima da média) a ser sinalizado como anomalia. Ao escolher valores mais altos, apenas anomalias mais significativas são detectadas. O valor padrão é 3.0.
qScoreThreshScope real O limite mínimo para que a pontuação Q no nível do escopo (número de intervalos interquantiles acima do quantile alto) seja sinalizada como anomalia. Ao escolher valores mais altos, apenas anomalias mais significativas são detectadas. O valor padrão é 2.0.
minNumValueThreshScope long O limite mínimo para que a variável numérica seja sinalizada como anomalia para um escopo. Isso é útil para filtrar casos em que um valor é anormal estatisticamente (pontuação Z alta e pontuação em Q), mas o valor em si é muito pequeno para ser interessante. O valor padrão é 0.

Definição de função

Você pode definir a função inserindo seu código como uma função definida por consulta ou criando-a como uma função armazenada em seu banco de dados, da seguinte maneira:

  • definidos por consulta
  • Armazenados

Defina a função usando a instrução let a seguir. Nenhuma permissão é necessária.

Importante

Uma instrução não pode ser executada por conta própria. Ele deve ser seguido por uma instrução de expressão tabular . Para executar um exemplo de trabalho de detect_anomalous_spike_fl(), consulte Exemplo.

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.

Exemplo

O exemplo a seguir usa o operador de invocação para executar a função.

  • definidos por consulta
  • Armazenados

Para usar uma função definida por consulta, invoque-a após a definição da função inserida.

executar o de consulta

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
                            )

de saída

t timeSlice countEvents userName deviceId accountName âmbito entidade numVec sliceTime Dataset 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 O valor de countEvents de variável numérica no accountName prodEnvironment é 5079, que é anormalmente alto para este accountName. Com base nas observações dos últimos 60 dias, o valor de linha de base esperado está abaixo de 628,0. {"avg": 1363.22,"stdev": 267.51,"percentile_0.25": 605,"percentile_0.9": 628}

A saída da execução da função são as linhas no conjunto de dados de detecção que foram marcadas como picos anômalos nos níveis de escopo ou entidade. Alguns outros campos são adicionados para maior clareza:

  • dataSet: conjunto de dados atual (é sempre detectSet).
  • firstSeenScope: carimbo de data/hora quando o escopo foi visto pela primeira vez.
  • lastSeenScope: carimbo de data/hora quando o escopo foi visto pela última vez.
  • slicesInTrainingScope: número de fatias (por exemplo, dias) que o escopo existe no conjunto de dados de treinamento.
  • countSlicesEntity: número de fatias (por exemplo, dias) que a entidade existe no escopo.
  • avgNumEntity: média da variável numérica no conjunto de treinamento por entidade no escopo.
  • sdNumEntity: desvio padrão da variável numérica no conjunto de treinamento por entidade no escopo.
  • firstSeenEntity: carimbo de data/hora quando a entidade foi vista pela primeira vez no escopo.
  • lastSeenEntity: carimbo de data/hora quando a entidade foi vista pela última vez no escopo.
  • slicesInTrainingEntity: número de fatias (por exemplo, dias) que a entidade existe no escopo no conjunto de dados de treinamento.
  • countSlicesScope: número de fatias (por exemplo, dias) que o escopo existe.
  • avgNumScope: média da variável numérica no conjunto de treinamento por escopo.
  • sdNumScope: desvio padrão da variável numérica no conjunto de treinamento por escopo.
  • zScoreEntity: pontuação Z para o valor atual da variável numérica com base no modelo de entidade.
  • qScoreEntity: pontuação Q para o valor atual da variável numérica com base no modelo de entidade.
  • zScoreScope: pontuação Z para o valor atual da variável numérica com base no modelo de escopo.
  • qScoreScope: pontuação Q para o valor atual da variável numérica com base no modelo de escopo.
  • isSpikeOnEntity: sinalizador binário para pico anormal com base no modelo de entidade.
  • entityHighBaseline: linha de base alta esperada para valores de variável numérica com base no modelo de entidade.
  • isSpikeOnScope: sinalizador binário para pico anormal com base no modelo de escopo.
  • scopeHighBaseline: linha de base alta esperada para valores de variável numérica com base no modelo de escopo.
  • entitySpikeAnomalyScore: pontuação de anomalias para o pico com base no modelo de entidade; um número no intervalo [0,1], valores mais altos que significam mais anomalias.
  • scopeSpikeAnomalyScore: pontuação de anomalias para o pico com base no modelo de escopo; um número no intervalo [0,1], valores mais altos que significam mais anomalias.
  • anomalyType: mostra o tipo de anomalia (útil ao executar várias lógicas de detecção de anomalias juntas).
  • anomalyScore: pontuação de anomalias para o pico com base no modelo escolhido.
  • anomalyExplainability: wrapper textual para anomalia gerada e sua explicação.
  • anomalyState: recipiente de métricas do modelo escolhido (média, desvio padrão e percentis) que descreve o modelo.

No exemplo acima, executar essa função na variável countEvents usando o usuário como entidade e conta como escopo com parâmetros padrão detecta um pico no nível do escopo. Como o usuário 'H4ck3r' não tem dados suficientes no período de treinamento, a anomalia não é calculada para o nível de entidade e todos os campos relevantes estão vazios. A anomalia no nível do escopo tem uma pontuação de anomalias de 0,998, o que significa que esse pico é anômalo para o escopo.

Se aumentarmos qualquer um dos limites mínimos alto o suficiente, nenhuma anomalia será detectada, pois os requisitos seriam muito altos.

A saída mostra as linhas com picos anômalos junto com campos explicativos em formato padronizado. Esses campos são úteis para investigar a anomalia e para executar a detecção de pico anômalo em várias variáveis numéricas ou executar outros algoritmos juntos.

O uso sugerido no contexto de segurança cibernética está executando a função em variáveis numéricas significativas (quantidades de dados baixados, contagens de arquivos carregados ou tentativas de entrada com falha) por escopos significativos (como assinatura em contas) e entidades (como usuários ou dispositivos). Um pico anormal detectado significa que o valor numérico é maior do que o esperado nesse escopo ou entidade e pode ser suspeito.