Количество данных, которые получает наш мониторинг выросло настолько, что для их обработки мощности только человеческого разума уже не хватает. Поэтому мы надрессировали искусственный интеллект помогать нам искать аномалии в полученных данных. И теперь у нас есть Кибер-Оракул.  Здравствуйте, меня зовут Иван Хозяинов, я работаю в ITSumma. Компания занимается поддержкой и администрированием большого количества сайтов, основная задача — оперативно реагировать на происшествия и предотвращать их. Для этого у нас есть штат дежурных администраторов и система мониторинга, которая собирает важные показатели с серверов и сохраняет эти данные в общую базу.  Система мониторинга отправляет оповещение, когда достигнуто предельное значение одного из важных параметров. Какие параметры считать важными и какие именно значения будут для них предельными решают дежурные системные администраторы, когда настраивают мониторинг. Но ведь бывает явно странное поведение, которое проходит мимо системы мониторинга, потому что предельные значения не были достигнуты. 
Рис.1 Система мониторинга Чтобы заметить такие аномалии, надо постоянно просматривать огромный объем данных, физических возможностей человека для этого просто не хватит. Поэтому на помощь приходит всегда бодрое компьютерное железо и математика. Про задачу поиска аномалий в накопленном массиве данных о жизни серверов я и хочу вам рассказать. Задача поиска аномалий достаточно общая и она интуитивно понятна: мы ищем что-то необычное среди данных. Тема будоражит сознание, поэтому логично, что существуют коммерческие продукты, которые этим занимаются, например http://anomaly.io и http://grokstream.com.  Но вернемся к самой задаче. Будем считать, что данные представлены набором измерений и каждое измерение представлено набором параметров, например текущие показания Load Average для CPU, HDD Free Space и т.д. С точки зрения математики это можно представить в виде вектор-функции:   Где t — это время, а n — количество измеряемых параметров.
Существует множество статей на тему математических формализаций и подходов к ее решению. В обзорной статье  приводится классификация для типов аномалий: Нахождение точечной аномалии чаще всего сводится к нахождению математического ожидания и стандартного отклонения, а затем все, что выбивается за определенный порог после стандартного отклонения, считается аномалией. Но все становится интереснее с контекстуальными и коллективными аномалиями.  В качестве контекста для временных рядов чаще всего выступает сезонность (англ. Seasonality) и тренды. Для фильтрации сезонности может использоваться экспоненциальное сглаживание Хольта-Винтера  . Но пока этот контекст, как и тренды, мы затрагивать не будем. Так же в качестве контекста могут выступать дополнительные данные вроде логов на сервере . Пока этого касаться тоже не будем, а посмотрим на возможность находить коллективные аномалии, вроде колебаний и пиков. Среди публикаций можно найти подсказки насчет использования нейронных сетей  и конкретных архитектур при поиске аномалий во временных рядах . Попробуем максимально учесть рекомендации, хотя грабли все равно ожидаемо будут. Первое, что нам необходимо — это выбор программных и математических инструментов. В качестве математического инструмента будем использовать наивные формулы для детектирования сильных колебаний на графике, типа такой:   На идейном уровне это означает сумму всех возможных колебаний за определенный промежуток времени  Так как функция у нас дискретная, то и формула соответственно будет выглядеть как-то так:   Теперь можно определить порог и это будет готовый детектор всяких всплесков. Но определять будем не самостоятельно, а поручим это нейронной сети. В качестве языка для прототипирования был выбран Python, сервера баз данных Clickhouse от компании Яндекс с довольно удобным использованием его из Python. Для работы с нейронными сетями был выбран фреймворк Tensorflow от Google. Считать все будем на двух GPU Nvidia GTX1080, потратив драгоценные мощности не на майнинг, а на настоящие исследовательские задачи. С точки зрения математики нейронная сеть — это функция:   где Y — ответ на вопрос, а X — входные данные. Функция достаточно сложная, но её можно обучать, например, методом обратного распространения ошибки. Недавно на хабре была опубликована неплохая статья на тему нейронных сетей, где отдельно выделен пункт препроцессинга. Хотелось бы дополнить, что помимо нормализации данных, на этом этапе можно еще озаботиться пополнением данных (например, интерполяцией), чтобы не было никаких пропусков. В идеальном мире измерения должны приходить с серверов и писаться в базу каждые 15 секунд, но в реальном мире какие-то метрики по различным причинам могут не доходить или доходить невовремя, поэтому требуется как-то восполнять эту пропущенную информацию различными допущениями. Например, мы хотим узнать изменение параметра Load Average для CPU, и в базе это будет выглядеть условно так: Представим, что каждые 15 секунд — это одна единица времени t. Тогда, если совершить достаточно простое заполнение массива, то все будет выглядить так: Конечно, можно построить линейную интерполяцию, но тогда время предварительной обработки данных может вырастать в разы (не говоря уже о более сложных способах), что критично для больших объемов данных. Даже, казалось бы, простое дополнение с учетом таких допущений может также сильно замедлить процесс подготовки данных: Поэтому в итоговом варианте был выбран самый тупой способ из возможных (он же и самый быстрый) — если значение пропущено, то берем ближайшее предыдущее.: Итак, у нас есть какая-то функция и можно начать формировать тензоры для обучения. С точки зрения программы на python, тензоры — это просто многомерные массивы. Согласно одному из законов Кларка: «Любая достаточно развитая технология неотличима от магии». А учитывая, что нейронная сеть достаточно сложна для понимания за счет своей объективной структурной сложности и способна на невероятные вещи, то можно начать испытывать некоторое благоговение перед этим инструментом и совершать опрометчивые поступки. С самого начала захотелось попытаться обучить нейронную сеть на всех существующих данных, разделив примерно таким образом: 90% для данных, которые мы будем считать нормальными. И 10% данных просто для проверки — вдруг там будет что-то аномальное. Ну и наоборот, выбрать самый скучный период жизни сервера в 10%, где ничего не происходит и после обучения проверить на оставшихся 90% данных. Хотелось верить, что если сделать достаточно много слоев и загнать туда все данные, она сама выделит контексты и все сможет, если брутфорсом перебрать достаточное количество архитектур. Но для начала попробуем как это работает на простых функциях типа синусов-косинусов с небольшими изменениями. В качестве входа на нейронную сеть подавался набор данных в логарифмическом масштабе или проще говоря так: текущее показание, 15 секунд назад, минуту назад, пять минут назад, полчаса и так далее до недели. Казалось, что это поможет выявить сезонность. После небольшого успеха на простой синусоиде — настало время прототипов. Чтобы проще было писать прототипы использовался фреймворк tflearn на базе tensorflow. Как оказалось, там сложнее распределять задачи между GPU и это можно делать только через переменные окружения (https://github.com/tflearn/tflearn/issues/32), в отличие от tensorflow, где можно считать часть задач на одном GPU, часть — на другом в рамках одной программы. Итак, для каждого сервера — своя нейронная сеть для обучения. После беспорядочного перебора архитектур из фреймворка, стало понятно, что терабайтного винчестера на машине с нейронками недостаточно. Нейронная сеть для каждого сервера занимала больше 100 МБ и обучалась в среднем 40 минут, не считая предварительной подготовки, которая тоже занимала время. Набор данных для обучения после всей предварительной обработки занимал примерно 5—6 ГБ.  Конечно, было приятно смотреть в консоль и видеть как там что-то происходит. Была вера в волшебную силу этого черного ящика: вдруг брутфорсом получится подобрать волшебные параметры сети, не вникая в происходящее. Как можно догадаться — такой подход оказался неудачным. Начнем с простых архитектур и многослойного перцептрона. Будем считать, что у нас задача классификации и есть два класса — «аномалия» и «не аномалия». Соответственно, нам нужны обучающие данные, которые требуется самостоятельно подготовить. Такой подход описан в  и нам необходимо подготовить наборы данных с «лейблами». Логарифмический масштаб был убран и на вход стал подаваться просто 25-минутный промежуток времени (100 измерений) + значение S из формулы (1).  График функции на которой производилось обучение представлен на рис. 4. На графике данные не нормализованные, но обучение производилось на нормализованных. Кстати, здесь представлена свободная память на одном из серверов — это то, что довольно часто меняется. 
Рис. 4. Обучение с учителем, серым выделены аномалии Входной слой у нас есть, там будет 101 нейрон, на первом скрытом слое — 55 нейронов, на втором скрытом слое — 1024, на выходе — класс «аномалия», «не аномалия». Архитектура сети была выбрана, можно сказать, случайно, поэтому наверняка возможен лучший вариант. 
Рис 5. Обучение перцептрона с двумя скрытыми слоями Сеть обучалась методом стохастического градиентного спуска Адама с параметром learning_rate = 0.00005, остальные параметры оставлены по умолчанию. В качестве меры была выбрана перекрестная энтропия между выходом нейронной сети и обучающим набором, функция активации — сигмоида или логистическая функция. Так как скрытых слоев два, то это уже можно сказать deep learning. Поиск аномалий при помощи обученной сети на том же сервере, но за больший промежуток времени — за два дня, видно, что надо избавляться от сезонности (рис. 6). 
Рис 6. Результат работы нейронной сети — график посередине, снизу — вход, сверху — интерпретация на ненормализованных данных Интересно проверить как обученная нейронная сеть на одном параметре (свободная память), будет справляться с другим (load average) на совершенно другом сервере. Результат можно видеть на картинке. За счет того, что данные нормализованы — можно видеть, что «аномалии» находятся и на другом масштабе (рис. 7). 
Рис. 7. Результат работы нейронной сети на другом наборе данных Наиболее эффективным образом нейронные сети проявили себя после подходящей подготовки данных. Собственно, в  упоминается, что «машинное обучение — это мета-инструмент, который можно использовать поверх других инструментов работы с данными». Надеюсь, кому-то этот опыт будет полезен. Спасибо за прочтение, буду рад комментариям. [1] http://cucis.ece.northwestern.edu/projects/DMS/publications/AnomalyDetection.pdf — Varun Chandola, Arindam Banerjee, and Vipin Kumar. 2009. Anomaly detection: A survey. ACM Comput. Surv. 41, 3, Article 15 (July 2009), 58 pages.
[2] http://www.imvu.com/technology/anomalous-behavior.pdf Evan Miller. 2007. Aberrant Behavior Detection in Time Series for Monitoring Business-Critical Metrics (DRAFT)
[3] http://www.oreilly.com/webops-perf/free/files/anomaly-detection-monitoring.pdf
Preetam Jinka & Baron Schwartz. 2015. Anomaly detection for monitoring: A statistical approach to time series anomaly detection. O’Reilly.
[4] https://wwЛw.microsoft.com/en-us/research/publication/context-aware-time-series-anomaly-detection-for-complex-systems/ Gupta M., Sharma A.B., Chen H., Jiang G. 2013 Context-Aware Time Series Anomaly Detection for Complex Systems. Proceedings of the SDM Workshop.
[5] https://www.elen.ucl.ac.be/Proceedings/esann/esannpdf/es2015-56.pdf Pankaj Malhotra, Lovekesh Vig, Gautam Shroff, Puneet Agarwal. Long Short Term Memory Networks for Anomaly Detection in Time Series. ESANN 2015 Proceedings. https://clickhouse.yandex/ — база данных, удобная для хранения и работы с большими объемами однотипных данных
https://github.com/Infinidat/infi.clickhouse_orm – python библиотека для работы с clickhouse
http://tensorflow.org/ — опенсурс-библиотека от Google для работы с нейронными сетями
http://tflearn.org – удобный фреймворк для работы с tensorflow
http://anomaly.io, http://grokstream.com – продукты, занимающиеся детектированием аномалий