Эта статья была написана Грегори Скафарто, стажером специалиста по обработке данных в Supralog, в сотрудничестве с DevRel из InfluxData Анаис Дотис-Джеоргиу.

В InfluxData мы гордимся нашим замечательным сообществом InfluxDB. Мы благодарны за ваш вклад и отзывы. Будь то плагины Telegraf, шаблоны сообщества, потрясающие проекты InfluxDB или сторонние пакеты Flux, ваш вклад продолжает впечатлять и унижать нас. Сегодня давайте прольем свет на Грегори Сафарто из Супралога.

Грегори Скафарто создал и поддерживает онлайн-конвейер планирования ресурсов с помощью Kapacitor, Python и InfluxDB. Грегори Скафарто живет во Франции и заканчивает последний год обучения на степень магистра в области компьютерных систем, сетей и телекоммуникаций в ENSEIRB-MATMECA. Он также работает стажером в Supralog в качестве специалиста по анализу данных, занимаясь исследованиями в области решений для профилактического обслуживания и планирования емкости.

Как Supralog использует Kapacitor для планирования емкости

Планирование мощности является одним из наиболее важных видов анализа в мониторинге временных рядов. Инструменты планирования мощностей помогают оценить производственные мощности, необходимые организации. В частности, в сфере ИТ планирование емкости включает в себя оценку объема хранилища, бизнес-потребностей и сметы затрат для достижения согласованных целевых показателей уровня обслуживания.

Установка:

Супралог решил использовать TICK Stack InfluxDB по многим причинам. Он высокопроизводителен и прост в использовании, но один элемент стека выделяется: Kapacitor, механизм оповещения и потоковой обработки данных в реальном времени, который работает с Python и обрабатывает наше прогнозирование временных рядов. Мы используем Kapacitor и InfluxDB для машинного обучения в режиме онлайн, чтобы генерировать большое количество прогнозов использования ЦП, памяти и диска. Мы планируем расширить эту архитектуру, чтобы генерировать прогнозы трафика веб-сайта, электронной почты и других показателей производительности приложений.

Мы пишем и запрашиваем данные с помощью InfluxDB-Python Client и используем Python для подготовки данных и прогнозирования. Наш скрипт Python также вычисляет остатки, разницу между прогнозом и реальным значением. Kapacitor предупреждает об этих остатках прогноза, чтобы применить непрерывное обучение к нашей модели машинного обучения (ML). Если остатки прогноза слишком велики и точность модели дрейфует, модель повторно обучается на новом обучающем наборе данных, чтобы оптимизировать модель для обеспечения точности. Grafana используется только для целей визуализации.

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

Недостатки FB Prophet для масштабного планирования емкости

Наша архитектура прогнозирования развивалась с течением времени. Сначала прогнозы делались с помощью Facebook Prophet, процедуры прогнозирования, реализованной на R и Python, которая имеет высокоуровневый API и позволяет пользователям очень быстро делать прогнозы. Однако в итоге мы удалили Prophet из нашего пайплайна, потому что он представляет некоторые недостатки для нашего варианта использования:

  • Имеет склонность к плавным изгибам.
  • У него нет онлайн-обучения. Вместо этого модель необходимо обучать на полном цикле данных каждый раз, когда модель дрейфует. Этот недостаток обходится очень дорого, потому что требует много ресурсов ЦП и ОЗУ, особенно с учетом сотен прогнозов, которые мы делаем.

Подготовка данных и проверка данных

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

Шаг первый: подготовка данных

Первый шаг — извлечь данные из базы данных с помощью клиента InfluxDB-Python и преобразовать их в Pandas DataFrame. Затем мы выполняем некоторую аналитику данных временных рядов, чтобы сгруппировать наши данные в две отдельные группы, чтобы определить, какие методы прогнозирования следует применять к данным. В частности, мы используем декомпозицию временных рядов для группировки наших данных. Мы решили разделить наши временные ряды на две отдельные группы:

  • Группа 1: данные с трендом, но без сезонности. Прогнозы данных Группы 1 сделаны с помощью классической модели. В частности, прогнозы данных Группы 1 выполняются с помощью линейной регрессии.
  • Вторая группа: данные с трендом и сезонностью. Делаются прогнозы данных группы 2, которые будут использовать модель машинного обучения. В частности, прогнозы данных второй группы выполняются с помощью сети с долговременной кратковременной памятью (LSTM).

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

Мы легко видим, что данные кажутся кусочно-линейными — они состоят из прямолинейных отрезков — но нам нужно определить, к какой группе они принадлежат. Мы написали тест, который может автоматически определять характер временного ряда. Этот тест сначала вычисляет функцию автокорреляции. Автокорреляция — это мера корреляции временного ряда с самим собой в предыдущий момент времени. Графики автокорреляции могут помочь нам определить, имеет ли временной ряд сезонность. Чтобы узнать больше об автокорреляции, о том, что она нам говорит, и о ее влиянии на модели машинного обучения, загляните в этот блог. График автокорреляции приведенных выше данных дает нам такой сигнал:

Шаг второй: проверка данных

Визуальным осмотром график подтверждает, что наши данные не проявляют сезонности, что помещает их в первую группу. Однако нам нужен количественный способ сделать этот вывод, чтобы мы могли автоматически определить, имеют ли данные сезонность. Мы берем Коэффициент корреляции Пирсона (R) автокорреляционной функции, чтобы автоматизировать группировку данных нашего временного ряда и определить, будет ли наш временной ряд хорошо моделироваться линейной моделью или моделью ML. Коэффициент корреляции Пирсона — это мера величины линейной корреляции между двумя переменными. Он определяется по следующей математической формуле:

где,

R=±1 указывает на сильную линейную корреляцию, тогда как R=0 указывает на отсутствие линейной корреляции.

Модель прогнозирования линейной регрессии с InfluxDB и Python

Теперь, когда мы определили, что для наших данных потребуется модель линейного прогнозирования, мы должны решить, какие данные вводить в нашу модель. Мы не можем использовать все данные для построения линейной модели, поскольку наклон изменяется со временем. Использование всех данных скроет недавние и соответствующие изменения наклона. Одним из решений этой проблемы является определение последнего значимого изменения наклона и выбор только той части временного ряда, которая содержит самое последнее значительное изменение. Это гарантирует, что наш прогноз будет сделан на основе наиболее актуального и значимого тренда.

Чтобы найти эти полезные изменения наклона, мы возьмем абсолютную производную ряда, а затем найдем значимые пики производной. Мы определяем пики значимых производных с помощью простого порога, который используется в функции signal.find_peaks из SciPy, как показано в коде ниже. Чтобы уменьшить шум сигнала и гарантировать, что выбираются только относительно значимые пики, используется цикл while, чтобы ограничить количество пиков, возвращаемых в верхние 10% самых больших пиков. В каждом цикле мы увеличиваем высоту пикового порога на один процент, чтобы отбросить шумные, более мелкие пики. Мы повторяем, пока не получим верхние 10% пиков.

Вот пример результатов таких операций в нашем сигнале:

Вот код для обучения нашей линейной модели, Make_Linear_Model.py, дополненный циклом while для устранения меньших относительно незначительных пиков. После того, как мы проанализировали производные пики, мы применяем линейную регрессию к нашему временному ряду при наличии этих пиков и генерируем линейный прогноз.

import numpy as np
import matplotlib.pyplot as plt 
from sklearn.linear_model import LinearRegression
from numpy import diff
from scipy.signal import find_peaks
 
def train_linear_model(df,severity):
    '''
    Determine the last linear part of data to consider and then train the model of this subsequence of data.
 
    Parameters
    ----------
    df : dataframe
        data.
    severity : int
       how insensitive will the model be to small changes 
 
    Returns
    -------
    model : linear_model
        linear model trained on the last trend of the signal.
    r_sq : int
        Pearson's coefficient (caracterize how well the model represent the data).
    df_l : dataframe
        last linear part of the data.
 
    '''
    dx = 1
    dy = diff(df["y"])/dx
    i=1
    mini=np.amin(df["y"])
    maxi=np.amax(df["y"])
    j=0
    nb_peaks = len(df["y"])+1
    while nb_peaks > len(df["y"])/10 :
        peaks, _ = find_peaks(abs(dy), height=(severity+j)*(maxi-mini)/100)
        nb_peaks=len(peaks)
        j=j+1
        plt.plot(peaks, abs(dy[peaks]), "x")
        plt.plot(abs(dy))
        plt.plot(df["y"])
        plt.title("Slope changes")
        plt.show()
    i=peaks[-1]+1
    df_l=df["y"][i+1 :]
    x=np.linspace(1,len(df_l)-1,len(df_l))
    model = LinearRegression()
    model.fit(x.reshape(-1,1),np.asarray(df_l).reshape(-1,1))
    r_sq=model.score(x.reshape(-1,1), np.asarray(df_l).reshape(-1,1))
    return model,r_sq,df_l

Подготовка данных для нашей модели прогнозирования машинного обучения с помощью InfluxDB и Python

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

Сначала мы выполняем декомпозицию временного ряда и разделяем наш ряд на три составляющие временного ряда: тренд, сезонность и остаток или «шум. Мы выполняем декомпозицию, поскольку обрабатываем каждый компонент в отдельной сети LSTM. Мы используем функцию seasonal_decompose из Statsmodels, модуль Python, который предоставляет классы и функции для оценки множества различных статистических моделей, для выполнения декомпозиции. Эта функция сезонной декомпозиции Statsmodels требует, чтобы пользователь указал период. Мы выбрали период 24*7 +1 (неделя, если данные отбираются с частотой 1 час), поскольку данные имеют недельные сезонные периоды. Очень важно убедиться, что ваш сезонный период точен. Если вы используете неправильный период, то разложение будет плохим, и наша модель станет неточной. Вот почему так важна проверка присутствия и сезонного периода с помощью графиков автокорреляции.

Мы также использовали аддитивный метод функции сезонной декомпозиции Statsmodels, чтобы упростить реконструкцию окончательного временного ряда. Поскольку мы выбираем аддитивный метод для нашей декомпозиции, наш окончательный прогноз делается путем сложения прогнозов отдельных компонентов вместе для восстановления временного ряда. Используя метод декомпозиции, мы теряем 12 баллов из-за краевого эффекта из-за фильтра, используемого функцией сезонной декомпозиции Statsmodels (которая использует фильтр скользящего среднего размера 13). Поэтому прогнозируемые точки смещаются назад на 12 временных шагов в прошлое, чтобы быть точными по времени.

Краткое объяснение LSTM

Прежде чем мы углубимся в то, как мы использовали Keras, платформу ML и LSTM для добавочного обучения, давайте уделим немного времени, чтобы кратко представить LSTM. В этом разделе мы рассмотрим только поверхность LSTM. Если вы заинтересованы в более подробном изучении LSTM, этот блог дает отличное объяснение того, как работают LSTM.

Мы используем три LSTM для вывода прогнозов для каждого из наших декомпозированных компонентов нашего временного ряда. Мы используем сети LSTM (долговременная кратковременная память) для моделирования наших сезонных временных рядов, поскольку они специализируются на изучении зависимостей в шаблонах временных рядов за длительные периоды. Следующие внутренние свойства делают LSTM очень мощной моделью временных рядов:

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

Следующая диаграмма представляет ячейку LSTM и суммирует функцию сети:

Сетевая ячейка LSTM состоит из трех основных компонентов или вентилей:

  • Входные ворота решают, какие значения и компоненты нашего временного ряда будут обновляться при прохождении через ячейку.
  • Шлюз забывания определяет, какие значения забыты или сохраняются в «памяти» сети. Этот шлюз забывания делает LSTM хорошими для сохранения шаблонов временных рядов обучения в течение длительных периодов времени. Это характерная часть LSTM.
  • Выходной элемент, определяющий информацию, которая должна быть передана в следующую ячейку, и в конечном итоге выдающий прогноз.

Использование Keras для прогнозирования временных рядов для InfluxDB

Теперь мы готовы взглянуть на код, который делает прогнозы для наших трех компонентов временных рядов. Мы использовали Keras, библиотеку нейронной сети с открытым исходным кодом, которая является оболочкой для TensorFlow для применения LSTM. Мы решили использовать Keras, потому что он легко обеспечивает инкрементное обучение. Инкрементное обучение — это метод машинного обучения, при котором в модель постоянно вводятся новые данные для дальнейшего обучения модели и эффективного повышения ее точности. Например, нам нужно всего лишь запросить данные за неделю из нашего экземпляра InfluxDB, чтобы переобучить нашу модель. Инкрементное обучение более эффективно, ресурсоемко и дешевле, чем традиционный подход машинного обучения, который требует запроса всех наших исторических данных для переобучения нашей модели. Вот наш код Making_ML_Model.py, который реализует сеть LSTM с инкрементным обучением с помощью Keras:

from keras.models import Sequential,save_model
from keras.layers import Dense,LSTM,Dropout
 
 
def make_models(nb_layers,loss,,nb_features,optimizer,look_back) :
    '''  
    Create an LSTM model depending on the parameters selected by the user
   
    Parameters
    ----------
    nb_layers : int
        nb of layers of the lstm model.
    loss : str
        loss of the model.
    metric : str
        metric to evaluate the model.
    nb_features : int
        size of the ouput (one for regression).
    optimizer : str
        gradient descend's optimizer.
   look_back : int
       windows to be process by the network to make the prediction
    trend : bool
          Distinguish trend signal from others (more complicated to modelise).
  
    Returns
    -------
    model : Sequential object
        model
    '''
    model=Sequential()
    model.add(LSTM(nb_layers,return_sequences=True,activation='relu',input_shape=(nb_features,.look_back)))
    model.add(Dropout(0.2))
    model.add(LSTM(nb_layers))
    model.add(Dropout(0.2))
    model.add(Dense(int(nb_layers/2),activation='relu'))
    model.add(Dense(1))
    model.compile(loss=loss,optimizer=optimizer,metrics=["mse"])
    print("model_made")
    return model

Чтобы узнать больше о том, как работает этот код и как использовать LSTM для генерации одномерного прогноза временных рядов, я рекомендую проверить этот блог.

Оценка нашей модели машинного обучения

Всегда полезно отображать потери наших моделей, чтобы оценить, было ли обучение успешным. Мы можем посмотреть на потери при обучении нашей модели для наших трех компонентов: тренда, сезонности и остатков. Потери при обучении — это мера штрафа за плохой прогноз во время обучения. Чем выше потери, тем хуже прогноз.

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

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

Когда потери при обучении достигают минимума насыщения или перестают уменьшаться, модель «изучила все, что могла» и является максимально точной. Из приведенных выше графиков мы видим, что мы могли бы сделать больше эпох, чтобы повысить точность прогноза, поскольку потери выглядят так, как будто они еще не закончили уменьшаться. Однако увеличение эпох увеличивает продолжительность обучения. Нам нужно найти компромисс между повышением точности и продолжительностью тренировки.

Остатки помогают определить доверительные интервалы модели

Остатки временных рядов или шум, как правило, вносят наибольшую неопределенность в модель из-за самой природы сезонного разложения и самих остатков. Однако, поскольку остатки обычно имеют гауссово распределение, мы можем легко вычислить 95% доверительный интервал по следующей формуле:

Вычисление интервалов прогнозирования для шума дает нам хороший способ измерить минимальную неопределенность всей модели, поскольку она вносит наибольшую ошибку. Однако стоит отметить, что измерение неопределенности с помощью этого подхода эффективно только для коротких прогнозов, но теряет точность для более длинных прогнозов. Этот подход используется потому, что он обеспечивает хорошее приближение неопределенности и более эффективен в вычислительном отношении, чем методология расчета истинной неопределенности окончательной модели, описанная в Глубокое и надежное прогнозирование временных рядов в Uber.

Прогнозы нашего составного LSTM, добавление отдельных прогнозов

После того, как сделаны прогнозы тренда, сезонности и остатков, эти прогнозы складываются. Вот результаты нашего прогноза на одну неделю вперед с доверительными интервалами:

График слева увеличен для части нашего прогноза, чтобы мы могли лучше визуализировать, насколько хорошо наш прогноз соответствует нашему реальному значению — он выглядит довольно хорошо! Наконец, теперь, когда у нас есть прогноз, мы записываем его в наш экземпляр InfluxDB вместе с клиентом. Вот вывод на нашем дашборде Grafana:

Оповещение об остатках с помощью Kapacitor и выполнение добавочного обучения с помощью узла exec()

Мы почти в конце! Все, что осталось сделать, это:

  • Создайте оповещение Kapacitor для отслеживания ошибки остатков
  • Создайте сценарий онлайн-инкрементного метода обучения, который правильно выполняет наш сценарий переобучения.

Мы используем Kapacitor для выполнения математических операций с измерениями и запуска предупреждений. В частности, мы будем использовать Kapacitor для отслеживания невязок наших прогнозов и повторного запуска обучения, если модель дрейфует. Kapacitor умеет многое, но сегодня мы сосредоточимся на узле exec(), который способен запускать скрипты, если выполняются условия нашего алерта. Это сердце нашего скрипта TICK. Вот наш скрипт TICK:

import os
 
path_to_kap = os.environ['kapacitor']
path_to_script = os.environ['script']
 
 
class Alert():
    def __init__(self,host,measurement) :
        self.host=host
        self.measurement=measurement      
        self.texte=""
 
       
    def create(self,message,form):
        self.form=form
        '''
        Create the tick alert
        Note : NEED TO DEFINE the path of the script, which will be launched when an alert is triggered, as a variable environnement
 
        Parameters
        ----------
        message : str
            Message to be shown as an alert on slack etc ; need to be written with kapacitor syntax
        form : str
            ex ,host=GSCA,device=device1  group by conditions with comma to separate + start with a comma
               
        Returns
        -------
        None.
 
        '''
        where_condition=""
        where=[[element.split("=") for element in form[1 :].split(",")][i][0]for i in range(len(form[1 :].split(",")))]
        for ele in where :
            where_condition=where_condition+ele+"="+ele +" AND "
        texte=""
        cond=["var "+(form[1 :].replace(","," AND").split("AND")[i]).replace("=","='")+"'" for i in range(len(form[1 :].replace(","," AND").split("AND")))]
        for element in cond :
            texte=texte+element+"\n"
        texte = texte +"\n\n"+ """var realtime = batch
                |query('SELECT mean(yhat) as real_value FROM "telegraf"."autogen".real_"""+self.measurement+""" WHERE """+where_condition[: -5]+"""')
                    .period(6h)
                    .every(6h)
                    .align()
                |last('real_value')
                    .as('real_value')
 
 
           
            var predicted = batch
                |query('SELECT mean(yhat) as prediction FROM "telegraf"."autogen".pred_"""+self.measurement+""" WHERE """+where_condition[: -5]+"""')
                    .period(6h)
                    .every(6h)
                    .align()
                |last('prediction')
                    .as('prediction')
    
           
           
            var joined_data = realtime
                |join(predicted)
                    .as('realtime', 'predicted')
                    .tolerance(20m)
           
           
            var performance_error = joined_data
                |eval(lambda: abs("realtime.real_value" - "predicted.prediction"))
                    .as('performance_error')
                |alert()
                    .crit(lambda: "performance_error" > 10 )
                    .message('""" +message+"""')
                    .slack()
                    .exec('"""+path_to_script+"""', '"""+self.host+"'"+""", '"""+str(form[1 :])+"'"+""")
 
           
        self.texte=texte
    def save_alert(self):
        self.form=self.form[1 :].replace("=",".")
        self.form=self.form.replace(",","_")
        self.form=self.form.replace(":","")
        self.path=r"Alertes/alerte_"+self.host+"_"+self.form+".tick"   #path to modifie as you want
        with open(self.path,"w") as f :
            f.write(self.texte)
        f.close()
   
   
       
    def define_alert(self):
        self.form=self.form.replace("=",".")
        self.form=self.form.replace(",","_")
        self.form=self.form.replace(":","")
        cmd_define_alert=path_to_kap+" define "+"alerte_"+self.host+"_"+self.form+" -type batch -tick "+self.path+" -dbrp telegraf.autogen"
        print(cmd_define_alert)
        os.system('cmd /c '+cmd_define_alert)
       
       
    def enable_alert(self):
        self.form=self.form.replace("=",".")
        self.form=self.form.replace(",","_")
        self.form=self.form.replace(":","")
        cmd_enable_alert=path_to_kap+" enable "+"alerte_"+self.host+"_"+self.form
        os.system('cmd /c '+cmd_enable_alert)
       
    def launch(self):
        self.define_alert()
        self.enable_alert()

Класс Alert() создает оповещение, адаптированное к созданной нами модели, чтобы отслеживать наш прогноз и перезапускать обучение, если модель дрейфует. Функция create() объединяет наше измерение прогноза с нашими фактическими данными, вычисляет остаток прогноза или дрейф модели, отправляет предупреждение в Slack, когда происходит событие переобучения, и запускает наш промежуточный скрипт с узлом exec(). Обратите внимание, что путь к Kapacitor и промежуточным скриптам записывается как переменные окружения, потому что в нашем случае скрипт будет запущен в продакшн на контейнере Docker.

Теперь давайте объясним, что делает промежуточный скрипт, но сначала помните, что наш скрипт Python Keras должен импортировать нетривиальные модули и библиотеки. В начале проекта в корне каталога проекта была создана виртуальная среда с: python -m venv name_venv. Наш промежуточный скрипт активирует эту виртуальную среду, которая содержит несколько пакетов (включая pip) и каталоги (включая Script, который содержит activate.bat). Промежуточный скрипт содержит одну строку:

Для Windows:

%activate_project% && cd %ml_project_directory% &&  python Retrain.py %1 %2 %3

где %activate_project% это путь к \Scripts\activate.bat от продавца directory

и %ml_project_directory% путь к корню вашего проекта, содержащему каталог Retrain.py, добавленный к вашим переменным пути.

Для Linux или MacOS:

%activate_project && cd %ml_project_directory &&  python Retrain.py %1 %2 %3

где %activate_project — это путь к \Scripts\bin\activate.bat от venv directory, а %ml_project_directory — путь к корню вашего проекта, содержащего каталог Retrain.py, добавленный к вашим переменным пути.

Промежуточный сценарий — это .bat, который активирует виртуальную среду, а затем запускает код переобучения Python, Retrain.py. В Retrain.py повторно используются функции из первого обучающего файла, но модели продолжаются с того места, где они остановились. Они делают это, применяя последние файлы HDF5, в которых хранятся веса каждой модели (т. е. Trend.h5, Seasonity.h5 и Остатки.h5) для каждого прогнозируемого ряда во время переобучения. Эти файлы хранятся в соответствующем каталоге прогнозируемого ряда. Например, предположим, что мы прогнозируем CPU. Затем в приведенном выше сценарии:

  • %1 строка, представляющая интересующий хост host1
  • %2 строка, представляющая измерение cpu
  • %3 строка, представляющая серийный ключ в линейном протоколе host=host1,cpu=cpu-total. Он представляет форму в классе Existing_Predictor() в сценарии ниже.

Для каждой серии соответствующий каталог имеет следующее имя: primaryTag_measurement_seriesKey. В приведенном выше примере каталог весов нашей модели для этой серии будет host1_cpu_host1_cpu-total.

Вот скрипт, который содержит функцию load_models(). Функция load_modules() разбивает форму %3 и указывает каталог, из которого будут загружаться веса.

class Existing_Predictor(Predictor):
   
    def __init__(self,df,host,measurement,look_back,nb_epochs,nb_batch,form,freq_period) :
        Predictor.__init__(self)
        self.df=df
        self.host=host
        self.measurement=measurement
        self.form=form
        self.look_back=look_back
        trend_x, trend_y,seasonal_x,seasonal_y,residual_x,residual_y=self.prepare_data(df,look_back,freq_period)
        model_trend,model_seasonal,model_residual=self.load_models()
        model_trend=self.train_model(model_trend,trend_x,trend_y,nb_epochs,nb_batch,"trend")
        model_seasonal=self.train_model(model_seasonal,seasonal_x,seasonal_y,nb_epochs,nb_batch,"seasonal")
        model_residual=self.train_model(model_residual,residual_x,residual_y,nb_epochs,nb_batch,"residual")
        self.model_trend=model_trend    
        self.model_seasonal=model_seasonal
        self.model_residual=model_residual
 
    def load_models(self):
        file=""
        for element in self.form :
            value=element.split("=")
            file=file+'_'+value[1]
        file=file[1 :].replace(":","")
        model_trend=load_model("Modeles/"+file+"_"+self.measurement+"/"+"trend"+".h5")
        model_seasonal=load_model("Modeles/"+file+"_"+self.measurement+"/"+"seasonal"+".h5")
        model_residual=load_model("Modeles/"+file+"_"+self.measurement+"/"+"residual"+".h5")
        return model_trend,model_seasonal,model_residual

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

Как упоминалось ранее, в этом блоге публикуется только часть кода, используемого в этом проекте. Пожалуйста, взгляните на этот репозиторий, чтобы узнать о методах prepare_data(), train_model() и save_model(), которые дополняют этот подход ML.

Машинное онлайн-обучение с InfluxDB 2 и заключительные мысли от InfluxData

Следующий раздел представляет собой заметку Анаис Дотис-Джеоргиу, DevRel в InfluxData.

Очевидно, что Грегори — ниндзя Data Science, и я благодарен ему за то, что он поделился своей историей. Однако, если вы посмотрите на его код и его подход, вы поймете, что он использует InfluxDB 1.x и Kapacitor. Грегори начал свое путешествие со стеком 1.x TICK. Те из вас, кто начинает сейчас, могут спросить: «Как я могу добиться этого с помощью InfluxDB 2.0?» Вот как вы можете создать аналогичный конвейер прогнозирования с InfluxDB 2.0:

  1. Используйте новый Клиент Python. Это позволяет вам запрашивать и записывать данные pandas DataFrames напрямую. Он также совместим с 1.x, если вам неудобно использовать 2.0, которая в настоящее время находится в стадии бета-тестирования. (Недавно на InfluxDays мы объявили, что намерены сделать InfluxDB 2.0 OSS общедоступной осенью, а InfluxDB Cloud — уже сейчас!).
  2. Рассчитайте остатки предсказания с помощью Flux и объединений. Я бы также рассчитал коэффициент корреляции Пирсона с помощью функции pearsonr().
  3. Запишите остатки в новое ведро или измерение с помощью функции to(). Я бы выполнял этот скрипт Flux через регулярные промежутки времени с Задачей.
  4. Используйте Оповещения и функцию http.post() для запуска переобучения и запуска скриптов. В качестве альтернативы вы также можете рассмотреть возможность использования Node-RED, инструмента программирования для соединения аппаратных устройств, чтобы инициировать переобучение. Вам также может быть интересно прочитать этот блог, который имеет некоторое сходство с предложенным подходом к архитектуре. В нем описывается, как Node-RED используется для непрерывного развертывания конфигураций Telegraf.

Независимо от того, какой выпуск или версию InfluxDB вы используете, я надеюсь, что эта история вдохновит вас на интеграцию машинного обучения в ваше решение на базе InfluxDB. Еще раз, я хотел бы поблагодарить Грегори за то, что он поделился своей историей, и поблагодарить сообщество InfluxData за все ваши вклады. Как всегда, поделитесь своими мыслями, опасениями или вопросами в разделе комментариев, на нашем сайте сообщества или на нашем канале Slack. Мы будем рады получить ваши отзывы и помочь вам с любыми проблемами, с которыми вы столкнетесь!

Также, поскольку тема этой статьи вам интересна, посмотрите вебинар No-Code ML for Forecasting and Anomaly Detection. В нем описывается POC для контейнерного подхода без кода к ML с InfluxDB. Вы также можете поделиться своими мыслями с ведущим Dean Sheehan.