Руководство. Обнаружение и анализ аномалий с помощью возможностей машинного обучения KQL в Azure Monitor

Язык запросов Kusto (KQL) включает операторы машинного обучения, функции и подключаемые модули для анализа временных рядов, обнаружения аномалий, прогнозирования и анализа первопричин. Используйте эти возможности KQL для выполнения расширенного анализа данных в Azure Monitor без дополнительных затрат на экспорт данных во внешние средства машинного обучения.

В этом руководстве описано следующее:

  • Создание временных рядов
  • Определение аномалий в временных рядах
  • Настройка параметров обнаружения аномалий для уточнения результатов
  • Анализ первопричин аномалий

Примечание.

В этом руководстве содержатся ссылки на демонстрационную среду Log Analytics, в которой можно запустить примеры запросов KQL. Данные в демонстрационной среде являются динамическими, поэтому результаты запроса не совпадают с результатами запроса, показанным в этой статье. Однако вы можете реализовать одни и те же запросы KQL и субъекты в собственной среде, а также все средства Azure Monitor, использующие KQL.

Необходимые компоненты

Требуемые разрешения

У вас должны быть Microsoft.OperationalInsights/workspaces/query/*/read разрешения на запрашиваемые рабочие области Log Analytics, как указано встроенной ролью Log Analytics Reader, например.

Создание временных рядов

Используйте оператор KQL make-series для создания временных рядов.

Давайте создадим временные ряды на основе журналов в таблице "Использование", в которой содержатся сведения о том, сколько данных каждой таблицы в рабочей области приема каждый час, включая оплачиваемые и неплатежественные данные.

Этот запрос используется make-series для диаграммы общего объема оплачиваемых данных, принятых каждой таблицей в рабочей области каждый день за последние 21 дней:

Щелкните, чтобы выполнить запрос

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

На результирующей диаграмме можно четко увидеть некоторые аномалии, например в AzureDiagnostics типах данных и SecurityEvent типах данных:

Анимированный GIF-файл, показывающий диаграмму общих данных, принятых каждой таблицей в рабочей области каждый день, более 21 дней. Курсор перемещается, чтобы выделить три аномалии использования на диаграмме.

Затем мы будем использовать функцию KQL для перечисления всех аномалий в временных рядах.

Примечание.

Дополнительные сведения о синтаксисе и использовании см. в make-series операторе make-series.

Поиск аномалий в временных рядах

Функция series_decompose_anomalies() принимает ряд значений в качестве входных данных и извлекает аномалии.

Давайте предоставим результирующий набор запроса временных рядов в качестве входных данных функции series_decompose_anomalies() :

Щелкните, чтобы выполнить запрос

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

Этот запрос возвращает все аномалии использования для всех таблиц за последние три недели:

Снимок экрана: таблица со списком аномалий в использовании для всех таблиц в рабочей области.

Глядя на результаты запроса, можно увидеть, что функция:

  • Вычисляет ожидаемое ежедневное использование для каждой таблицы.
  • Сравнивает фактическое ежедневное использование с ожидаемым использованием.
  • Назначает оценку аномалий каждой точке данных, указывающую степень отклонения фактического использования от ожидаемого использования.
  • Определяет положительные (1) и отрицательные (-1) аномалии в каждой таблице.

Примечание.

Дополнительные сведения о синтаксисе и использовании см. в series_decompose_anomalies() разделе series_decompose_anomalies().

Настройка параметров обнаружения аномалий для уточнения результатов

Рекомендуется просмотреть начальные результаты запроса и при необходимости внести настройки в запрос. Выбросы входных данных могут повлиять на обучение функции, и может потребоваться настроить параметры обнаружения аномалий функции, чтобы получить более точные результаты.

Отфильтруйте результаты series_decompose_anomalies() запроса на аномалии в типе AzureDiagnostics данных:

Таблица с результатами запроса обнаружения аномалий, отфильтрованной для результатов из типа данных Диагностика Azure.

Результаты показывают две аномалии 14 июня и 15 июня. Сравните эти результаты с диаграммой из первого make-series запроса, где можно увидеть другие аномалии 27 мая и 28:

Снимок экрана: диаграмма общих данных, принятых Диагностика Azure таблицей с выделенными аномалиями.

Разница в результатах возникает, так как series_decompose_anomalies() функция оценивает аномалии относительно ожидаемого значения использования, которое функция вычисляет на основе полного диапазона значений входного ряда.

Чтобы получить более подробные результаты из функции, исключите использование 15 июня - которое является вылитым по сравнению с другими значениями в серии - из процесса обучения функции.

Синтаксис series_decompose_anomalies() функции:

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

Test_points указывает количество точек в конце ряда, которые следует исключить из процесса обучения (регрессии).

Чтобы исключить последнюю точку данных, установите значение Test_points 1:

Щелкните, чтобы выполнить запрос

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

Отфильтруйте результаты для AzureDiagnostics типа данных:

Таблица с результатами измененного запроса обнаружения аномалий, отфильтрованной для результатов из типа данных Диагностика Azure. Теперь результаты показывают те же аномалии, что и диаграмма, созданная в начале руководства.

Все аномалии на диаграмме из первого make-series запроса теперь отображаются в результирующем наборе.

Анализ первопричин аномалий

Сравнение ожидаемых значений с аномальными значениями помогает понять причину различий между двумя наборами.

Подключаемый модуль KQL diffpatterns() сравнивает два набора данных одной структуры и находит шаблоны, характеризующие различия между двумя наборами данных.

Этот запрос сравнивает AzureDiagnostics использование 15 июня, крайнее вылитее в нашем примере с использованием таблицы в другие дни:

Щелкните, чтобы выполнить запрос

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

Запрос идентифицирует каждую запись в таблице, как происходит в AnomalyDate (15 июня) или OtherDates. Затем diffpatterns() подключаемый модуль разделяет эти наборы данных с именем A (OtherDates в нашем примере) и B (AnomalyDate в нашем примере) и возвращает несколько шаблонов, которые способствуют различиям в двух наборах:

Снимок экрана: таблица с тремя строками. Каждая строка показывает разницу в использовании аномалий и базовом использовании.

Глядя на результаты запроса, вы можете увидеть следующие различия:

  • Существует 24 892 147 экземпляров приема из ресурса CH1-GEARAMAAKS в течение всех остальных дней в диапазоне времени запроса, а прием данных из этого ресурса не выполняется 15 июня. Данные из ресурса CH1-GEARAMAAKS составляют 73,36% от общего приема в другие дни в диапазоне времени запроса и 0% от общего приема 15 июня.
  • Существует 2 168 448 экземпляров приема из ресурса NSG-TESTSQLMI519 в течение всех остальных дней в диапазоне времени запроса и 110 544 экземпляров приема из этого ресурса 15 июня. Данные из ресурса NSG-TESTSQLMI519 составляют 6,39% от общего приема в другие дни в диапазоне времени запроса и 25,61% приема 15 июня.

Обратите внимание, что в среднем существует 108 422 экземпляров приема из ресурса NSG-TESTSQLMI519 в течение 20 дней, составляющих другие дни (деление 2 168 448 на 20). Поэтому прием из ресурса NSG-TESTSQLMI519 15 июня существенно отличается от приема этого ресурса в другие дни. Тем не менее, потому что приема из CH1-GEARAMAAKS 15 июня нет, прием из NSG-TESTSQLMI519 составляет значительно больший процент общего приема на дату аномалии по сравнению с другими днями.

В столбце PercentDiffAB отображается абсолютное процентное различие между A и B (|PercentA — PercentB|), которая является основной мерой разницы между двумя наборами. По умолчанию подключаемый diffpatterns() модуль возвращает разницу в размере более 5 % между двумя наборами данных, но этот порог можно изменить. Например, чтобы вернуть только различия в 20 % или более между двумя наборами данных, можно задать | evaluate diffpatterns(AnomalyDate, "OtherDates", "AnomalyDate", "~", 0.20) в приведенном выше запросе. Теперь запрос возвращает только один результат:

Снимок экрана: таблица с одной строкой, представляющую разницу между использованием аномалий и базовым использованием. На этот раз запрос не вернул различия менее 20 процентов между двумя наборами данных.

Примечание.

Дополнительные сведения о синтаксисе и использовании см. в diffpatterns() подключаемых модулях diff.

Следующие шаги

Дополнительные сведения: