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

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

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

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

Примечание

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

Предварительные требования

Необходимые разрешения

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

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

Используйте оператор 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 указывает количество точек в конце ряда, исключаемых из процесса обучения (регрессии).

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

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

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 июня существенно не отличается от приема этого ресурса в другие дни. Тем не менее, так как 15 июня ch1-GEARAMAAKS не принимает данные, прием из NSG-TESTSQLMI519 составляет значительно больший процент от общего приема данных на дату аномалии по сравнению с другими днями.

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

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

Примечание

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

Дальнейшие действия

См. также: