В разделе « Зачем использовать K-средние для данных временных рядов? (Часть первая) «, я даю обзор того, как использовать различные статистические функции и Кластеризацию K-средних для обнаружения аномалий для данных временных рядов. Я рекомендую проверить это, если вы не знакомы ни с одним из них. В этом посте я поделюсь:

  1. Код, показывающий, как используются K-средние
  2. Почему не следует использовать К-средние для обнаружения аномалий контекстных временных рядов

Код, показывающий, как он используется

Я позаимствовал код и набор данных для этой части из Учебника на фоне рыбы.

Пожалуйста, взгляните на это, это довольно круто.

В этом примере я покажу вам, как вы можете обнаружить аномалии в данных ЭКГ с помощью контекстного обнаружения аномалий с помощью кластеризации K-средних.

Нарушение ритмических данных ЭКГ - это тип коллективной аномалии, но мы будем анализировать аномалию в отношении формы (или контекста) данных.

Рецепт обнаружения аномалий в данных ЭКГ с помощью K-средних

  1. Окно ваших данных

К-средние образуют кластеры.

Но как?

Данные временных рядов не похожи на красивый "кластеризуемый" график рассеяния. Окно ваших данных берет данные, которые выглядят так ...

И превращает его в кучу более мелких сегментов (каждый по 32 точки).

По сути, они представляют собой горизонтальные переводы друг друга.

Они выглядят так ...

Каждый оконный сегмент определяется массивом размером 32.

Затем мы возьмем каждую точку в каждом сегменте и построим график в 32-мерном пространстве.

Мне нравится думать обо всем, что выше трехмерного, как о туманном облаке.

Вы можете представить себе, что теперь мы рисуем связку 32-мерных облаков в большом пространстве.

K-среднее сгруппирует эти 32-мерные облака в группы в зависимости от того, насколько они похожи друг на друга.

Таким образом, кластер будет представлять разные формы, которые принимают данные.

Один кластер может представлять действительно конкретную полиномиальную функцию.

Кластеры также могут представлять собой простой многочлен, например y = x³.

Тип линий, которые представляет каждый кластер, определяется размером вашего сегмента.

Чем меньше размер вашего сегмента, тем больше вы разбиваете данные временного ряда на составляющие - простые полиномы.

Установив для нашего сегмента segment_len значение 32, мы получим множество сложных многочленов.

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

Если мы сделаем небольшое количество кластеров, коэффициенты не будут иметь большого значения.

Вот код для оконной обработки ваших данных, который выглядит следующим образом:

import numpy as np
segment_len = 32
slide_len = 2
segments = []
for start_pos in range(0, len(ekg_data), slide_len):
    end_pos = start_pos + segment_len
     segment = np.copy(ekg_data[start_pos:end_pos])
    # if we're at the end and we've got a truncated segment, drop it
    if len(segment) != segment_len:
        continue
    segments.append(segment)

Наши сегменты будут выглядеть так:

Однако нам нужно нормализовать наши окна.

Позже, когда мы попытаемся предсказать, к какому классу принадлежат наши новые данные, мы хотим, чтобы наш вывод был непрерывной линией. Если кластеры определены оконными сегментами, которые не начинаются с 0 и не заканчиваются на 0, они не будут совпадать, когда мы попытаемся восстановить наши предсказанные данные.

Для этого мы умножаем все оконные сегменты на оконную функцию. Мы берем каждую точку в массиве и умножаем ее на каждую точку в массиве, который представляет эту оконную функцию:

window_rads = np.linspace(0, np.pi, segment_len)
window = np.sin(window_rads)**2
windowed_segments = []
for segment in segments:
    windowed_segment = np.copy(segment) * window
    windowed_segments.append(windowed_segment)

Теперь все наши сегменты будут начинаться и заканчиваться значением 0.

2. Время кластера

from sklearn.cluster import KMeans
clusterer = KMeans(n_clusters=150)
clusterer.fit(windowed_segments)

Центроиды наших кластеров доступны в clusterer.cluster_centers_. Они имеют форму (150,32).

Это потому, что каждый центр на самом деле представляет собой массив из 32 точек, и мы создали 150 кластеров.

3. Время реконструкции

Во-первых, мы создаем массив из нулей такой же длины, как и наш набор аномальных данных.

(Мы взяли наш ekg_data и создали аномалию, вставив несколько нулей ekg_data_anomalous[210:215] = 0). В конечном итоге мы заменим нули в нашем массиве реконструкции предсказанными центроидами.

#placeholder reconstruction array
reconstruction = np.zeros(len(ekg_data_anomalous))

Затем нам нужно разделить наш набор аномальных данных на перекрывающиеся сегменты. Мы будем делать прогнозы на основе этих сегментов.

slide_len = segment_len/2
# slide_len = 16 as opposed to a slide_len = 2. Slide_len = 2 was used to create a lot of horizontal translations to provide K-Means with a lot of data. 
#segments were created from the ekg_data_anomalous dataset from the code above
for segment_n, segment in enumerate(segments):
    # normalize by multiplying our window function to each segment
    segment *= window
    # sklearn uses the euclidean square distance to predict the centroid
    nearest_centroid_idx = clusterer.predict(segment.reshape(1,-1))[0]
    centroids = clusterer.cluster_centers_
    nearest_centroid = np.copy(centroids[nearest_centroid_idx])
    
    # reconstructed our segments with an overlap equal to the slide_len so the centroids are       
    stitched together perfectly. 
    pos = segment_n * slide_len
    reconstruction[int(pos):int(pos+segment_len)] += nearest_centroid

Наконец, определите, есть ли ошибка больше 2%, и построите график.

error = reconstruction[0:n_plot_samples] - ekg_data_anomalous[0:n_plot_samples]
error_98th_percentile = np.percentile(error, 98)

4. Предупреждение

Теперь, если мы хотим предупредить о нашей аномалии, все, что нам нужно сделать, это установить порог 13,1 для нашей ошибки реконструкции.

Каждый раз, когда оно превышает 13,1, мы обнаруживаем аномалию.

Теперь, когда мы потратили все это время на изучение и понимание K-средних и их применения для обнаружения контекстных аномалий, давайте обсудим, почему его использование может быть плохой идеей.

Чтобы помочь вам пройти через эту часть, я предлагаю вам этот небольшой перерыв:

Почему не следует использовать K-средние для обнаружения аномалий контекстных временных рядов

Из этого поста я много узнал о недостатках использования K-Means. В этом посте много деталей, но есть три основных недостатка:

1. K-средние сходятся только на локальных минимумах, чтобы найти центроиды.

Когда K-Means находит центроиды, он сначала строит 150 случайных «точек» (в нашем случае это на самом деле 32-мерный объект, но давайте на секунду сведем эту проблему к 2-мерной аналогии).

Он использует это уравнение для вычисления расстояния между всеми 150 центроидами и каждой другой «точкой».

Затем он смотрит на все эти расстояния и группирует объекты вместе на основе их расстояния.

Sklearn вычисляет расстояние с помощью Евклидовой разности в квадрате (это быстрее, чем использование просто евклидовой разности).

Затем он обновляет положение центроида на основе этого уравнения (вы можете думать об этом как о среднем расстоянии от объектов в кластере).

Как только он больше не может быть обновлен, он определяет, что эта точка является центроидом.

Однако K-Means не может «видеть лес сквозь деревья».

Если начальные случайно размещенные центроиды находятся в плохом месте, то метод K-средних не назначит правильный центроид. Вместо этого он будет сводиться к локальному минимуму и обеспечивать плохую кластеризацию.

Плохая кластеризация приводит к плохим прогнозам. Чтобы узнать больше о том, как K-Means находит центроиды, я рекомендую прочитать this. Посмотрите здесь, чтобы узнать больше о том, как K-среднее сходится к локальным минимумам.

2. Каждый временной шаг отображается как измерение.

Это нормально, если наши временные шаги одинаковы. Однако представьте, если бы мы использовали К-средние для данных датчиков. Предположим, данные вашего датчика поступают нерегулярно. К-средние действительно могут легко создавать кластеры, которые являются прототипами поведения вашего базового временного ряда.

3. Использование евклидова расстояния в качестве меры сходства может ввести в заблуждение.

То, что объект находится близко к центроиду, не обязательно означает, что он должен принадлежать этому кластеру. Если вы осмотритесь, то можете заметить, что все остальные близлежащие объекты принадлежат к другому классу. Вместо этого вы можете рассмотреть возможность применения KNN с использованием объектов в кластерах, созданных K-Means.

Я надеюсь, что этот и предыдущий блог помогут вам в поиске аномалий. Пожалуйста, дайте мне знать, если вы обнаружили что-то непонятное, или не стесняйтесь попросить меня о помощи.

Вы можете перейти на сайт сообщества InfluxData или написать нам в Твиттере @InfluxDB.