Compartilhar via


Tutorial: Detectar e analisar anomalias usando os recursos de aprendizado de máquina da KQL no Azure Monitor

A KQL (Linguagem de Consulta Kusto) inclui operadores de aprendizado de máquina, funções e plug-ins para análise de séries temporais, detecção de anomalias, previsão e análise de causa raiz. Use esses recursos da KQL para realizar análises de dados avançadas no Azure Monitor sem a sobrecarga de exportar dados para ferramentas externas de aprendizado de máquina.

Neste tutorial, você aprenderá como:

  • Criar uma série temporal
  • Identificar anomalias em uma série temporal
  • Ajustar as configurações de detecção de anomalias para refinar os resultados
  • Analisar a causa raiz de anomalias

Observação

Este tutorial fornece links para um ambiente de demonstração do Log Analytics no qual é possível executar os exemplos de consulta KQL. Os dados no ambiente de demonstração são dinâmicos, portanto, os resultados da consulta não são iguais aos resultados da consulta mostrados neste artigo. No entanto, é possível implementar as mesmas entidades de segurança e consultas KQL no seu próprio ambiente e em todas as ferramentas do Azure Monitor que usam KQL.

Pré-requisitos

Permissões necessárias

Você deve ter permissões Microsoft.OperationalInsights/workspaces/query/*/read para os workspaces do Log Analytics que você consulta, conforme fornecido pela função interna Leitor do Log Analytics, por exemplo.

Criar uma série temporal

Use o operador make-series da KQL para criar uma série temporal.

Crie uma série temporal com base nos logs da tabela de uso, que contém informações sobre a quantidade de dados ingerida por cada tabela em um workspace a cada hora, incluindo dados faturáveis e não faturáveis.

Esta consulta usa make-series para traçar a quantidade total de dados faturáveis ingeridos por cada tabela no workspace todos os dias, nos últimos 21 dias:

Clique para executar a consulta

let starttime = 21d; // The start date of the time series, counting back from the current date
let endtime = 0d; // The end date of the time series, counting back from the current date
let timeframe = 1d; // How often to sample data
Usage // The table we’re analyzing
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) // Time range for the query, beginning at 12:00 AM of the first day and ending at 12:00 AM of the last day in the time range
| where IsBillable == "true" // Include only billable data in the result set
| make-series ActualUsage=sum(Quantity) default = 0 on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by DataType // Creates the time series, listed by data type 
| render timechart // Renders results in a timechart

No gráfico resultante, é possível ver claramente algumas anomalias, por exemplo, nos tipos de dados AzureDiagnostics e SecurityEvent:

GIF animado mostrando um gráfico do total de dados ingeridos por cada tabela no workspace todos os dias, durante 21 dias. O cursor se move para realçar três anomalias de uso no gráfico.

Em seguida, use uma função KQL para listar todas as anomalias em uma série temporal.

Observação

Para saber mais sobre a sintaxe e o uso de make-series, confira Operador make-series.

Encontrar anomalias em uma série temporal

A função series_decompose_anomalies() recebe uma série de valores como entrada e extrai as anomalias.

Forneça o conjunto de resultados da consulta de série temporal como entrada para a função series_decompose_anomalies():

Clique para executar a consulta

let starttime = 21d; // Start date for the time series, counting back from the current date
let endtime = 0d; // End date for the time series, counting back from the current date
let timeframe = 1d; // How often to sample data
Usage // The table we’re analyzing
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) // Time range for the query, beginning at 12:00 AM of the first day and ending at 12:00 AM of the last day in the time range
| where IsBillable == "true" // Includes only billable data in the result set
| make-series ActualUsage=sum(Quantity) default = 0 on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by DataType // Creates the time series, listed by data type
| extend(Anomalies, AnomalyScore, ExpectedUsage) = series_decompose_anomalies(ActualUsage) // Scores and extracts anomalies based on the output of make-series 
| mv-expand ActualUsage to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double),AnomalyScore to typeof(double), ExpectedUsage to typeof(long) // Expands the array created by series_decompose_anomalies()
| where Anomalies != 0  // Returns all positive and negative deviations from expected usage
| project TimeGenerated,ActualUsage,ExpectedUsage,AnomalyScore,Anomalies,DataType // Defines which columns to return 
| sort by abs(AnomalyScore) desc // Sorts results by anomaly score in descending ordering

Essa consulta retorna todas as anomalias de uso de todas as tabelas nas últimas três semanas:

Uma captura de tela de uma tabela mostrando uma lista de anomalias em uso para todas as tabelas no workspace.

Nos resultados da consulta, é possível ver que a função:

  • Calcula um uso diário esperado para cada tabela.
  • Compara o uso diário real com o uso esperado.
  • Atribui uma pontuação de anomalia a cada ponto de dados, indicando a extensão do desvio entre o uso real e o esperado.
  • Identifica anomalias positivas (1) e negativas (-1) em cada tabela.

Observação

Para saber mais sobre a sintaxe e o uso de series_decompose_anomalies(), confira series_decompose_anomalies().

Ajustar as configurações de detecção de anomalias para refinar os resultados

É uma boa prática revisar os resultados da consulta inicial e fazer ajustes nela, se necessário. Os valores discrepantes nos dados de entrada podem afetar o aprendizado da função e exigir ajustes nas configurações de detecção de anomalias da função para obter resultados mais precisos.

Filtre os resultados da consulta series_decompose_anomalies() em busca de anomalias no tipo de dados AzureDiagnostics:

Tabela mostrando os resultados da consulta de detecção de anomalias, filtrada para resultados do tipo de dados do Diagnóstico do Azure.

Os resultados mostram duas anomalias em 14 e 15 de junho. Compare esses resultados com o gráfico de nossa primeira consulta make-series, em que é possível ver outras anomalias em 27 e 28 de maio:

Captura de tela mostrando um gráfico do total de dados ingeridos pela tabela do Diagnóstico do Azure com anomalias realçadas.

A diferença nos resultados ocorre porque a função series_decompose_anomalies() pontua anomalias em relação ao valor de uso esperado, que a função calcula com base no intervalo completo de valores na série de entrada.

Para obter resultados mais refinados da função, exclua o uso em 15 de junho (que é um valor discrepante em comparação com os outros da série) do processo de aprendizado da função.

A sintaxe da funçãoseries_decompose_anomalies() é:

series_decompose_anomalies (Series[Threshold,Seasonality,Trend,Test_points,AD_method,Seasonality_threshold])

O Test_points especifica o número de pontos no final da série para excluir do processo de aprendizagem (regressão).

Para excluir o último ponto de dados, defina Test_points como 1:

Clique para executar a consulta

let starttime = 21d; // Start date for the time series, counting back from the current date
let endtime = 0d; // End date for the time series, counting back from the current date
let timeframe = 1d; // How often to sample data
Usage // The table we’re analyzing
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) // Time range for the query, beginning at 12:00 AM of the first day and ending at 12:00 AM of the last day in the time range
| where IsBillable == "true" // Includes only billable data in the result set
| make-series ActualUsage=sum(Quantity) default = 0 on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by DataType // Creates the time series, listed by data type
| extend(Anomalies, AnomalyScore, ExpectedUsage) = series_decompose_anomalies(ActualUsage,1.5,-1,'avg',1) // Scores and extracts anomalies based on the output of make-series, excluding the last value in the series - the Threshold, Seasonality, and Trend input values are the default values for the function 
| mv-expand ActualUsage to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double),AnomalyScore to typeof(double), ExpectedUsage to typeof(long) // Expands the array created by series_decompose_anomalies()
| where Anomalies != 0  // Returns all positive and negative deviations from expected usage
| project TimeGenerated,ActualUsage,ExpectedUsage,AnomalyScore,Anomalies,DataType // Defines which columns to return 
| sort by abs(AnomalyScore) desc // Sorts results by anomaly score in descending ordering

Filtre os resultados para o tipo de dados AzureDiagnostics:

Tabela mostrando os resultados da consulta de detecção de anomalias modificada, filtrada para os resultados do tipo de dados do Diagnóstico do Azure. Os resultados agora mostram as mesmas anomalias do gráfico criado no início do tutorial.

Todas as anomalias no gráfico da primeira consulta make-series agora aparecem no conjunto de resultados.

Analisar a causa raiz de anomalias

Comparar valores esperados com valores anormais ajuda você a entender a causa das diferenças entre os dois conjuntos.

O plug-in diffpatterns() da KQL compara dois conjuntos de dados da mesma estrutura e encontra padrões que caracterizam as diferenças entre eles.

Esta consulta compara o uso de AzureDiagnostics em 15 de junho, o valor atípico extremo no exemplo, com o uso da tabela em outros dias:

Clique para executar a consulta

let starttime = 21d; // Start date for the time series, counting back from the current date
let endtime = 0d; // End date for the time series, counting back from the current date
let anomalyDate = datetime_add('day',-1, make_datetime(startofday(ago(endtime)))); // Start of day of the anomaly date, which is the last full day in the time range in our example (you can replace this with a specific hard-coded anomaly date)
AzureDiagnostics	
| extend AnomalyDate = iff(startofday(TimeGenerated) == anomalyDate, "AnomalyDate", "OtherDates") // Adds calculated column called AnomalyDate, which splits the result set into two data sets – AnomalyDate and OtherDates
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) // Defines the time range for the query
| project AnomalyDate, Resource // Defines which columns to return
| evaluate diffpatterns(AnomalyDate, "OtherDates", "AnomalyDate") // Compares usage on the anomaly date with the regular usage pattern

A consulta identifica que cada entrada na tabela ocorreu em AnomalyDate (15 de junho) ou OtherDates. O plug-in diffpatterns() divide esses conjuntos de dados, chamados A (OtherDates no exemplo) e B (AnomalyDate no exemplo), e retorna alguns padrões que contribuem para as diferenças nos dois conjuntos:

Captura de tela mostrando uma tabela com três linhas, em que cada uma mostra uma diferença entre o uso anômalo e o uso de linha de base.

Observando os resultados da consulta, é possível ver as seguintes diferenças:

  • Há 24.892.147 instâncias de ingestão do recurso CH1-GEARAMAAKS em todos os outros dias no intervalo de tempo da consulta e nenhuma ingestão de dados desse recurso em 15 de junho. Os dados do recurso CH1-GEARAMAAKS representam 73,36% da ingestão total em outros dias no intervalo de tempo da consulta e 0% da ingestão total em 15 de junho.
  • Há 2.168.448 instâncias de ingestão do recurso NSG-TESTSQLMI519 em todos os outros dias no intervalo de tempo de consulta e 110.544 instâncias de ingestão desse recurso em 15 de junho. Os dados do recurso NSG-TESTSQLMI519 representam 6,39% da ingestão total em outros dias no intervalo de tempo da consulta e 25,61% da ingestão em 15 de junho.

Observe que, em média, há 108.422 instâncias de ingestão do recurso NSG-TESTSQLMI519 durante os 20 dias que compõem o período de outros dias (divida 2.168.448 por 20). Portanto, a ingestão do recurso NSG-TESTSQLMI519 em 15 de junho não é significativamente diferente daquela em outros dias. No entanto, como não há ingestão de CH1-GEARAMAAKS em 15 de junho, a ingestão de NSG-TESTSQLMI519 representa uma porcentagem significativamente maior da ingestão total na data da anomalia em comparação com outros dias.

A coluna PercentDiffAB mostra a diferença de ponto percentual absoluto entre A e B (|PercentA - PercentB|), que é a principal medida da diferença entre os dois conjuntos. Por padrão, o plug-in diffpatterns() retorna uma diferença de mais de 5% entre os dois conjuntos de dados, mas é possível ajustar esse limite. Por exemplo, para retornar somente diferenças de 20% ou mais entre os dois conjuntos de dados, é possível definir | evaluate diffpatterns(AnomalyDate, "OtherDates", "AnomalyDate", "~", 0.20) na consulta acima. A consulta agora retorna somente um resultado:

Captura de tela mostrando uma tabela com uma linha que apresenta uma diferença entre o uso anômalo e o uso de linha de base. Desta vez, a consulta não retornou diferenças inferiores a 20% entre os dois conjuntos de dados.

Observação

Para saber mais sobre a sintaxe e o uso de diffpatterns(), confira Plug-in de padrões de diferença.

Próximas etapas

Saiba mais sobre: