Прогнозирование машинного обучения Python с помощью Flask REST API
Опубликовано: 2022-03-11Эта статья посвящена использованию Python в контексте системы машинного обучения или искусственного интеллекта (ИИ) для прогнозирования в реальном времени с помощью Flask REST API. Представленную здесь архитектуру можно рассматривать как путь от проверки концепции (PoC) к минимально жизнеспособному продукту (MVP) для приложений машинного обучения.
Python — не первый выбор, о котором можно подумать при разработке решения для работы в реальном времени. Но поскольку Tensorflow и Scikit-Learn являются одними из наиболее часто используемых библиотек машинного обучения, поддерживаемых Python, их удобно использовать во многих PoC Jupyter Notebook.
Что делает это решение выполнимым, так это тот факт, что обучение занимает много времени по сравнению с прогнозированием. Если рассматривать обучение как процесс просмотра фильма и предсказания ответов на вопросы о нем, то вполне эффективным представляется отсутствие необходимости пересматривать фильм после каждого нового вопроса.
Обучение — это своего рода сжатое представление этого «фильма», а прогнозирование — это извлечение информации из сжатого представления. Это должно быть очень быстро, независимо от того, был ли фильм сложным или длинным.
Давайте реализуем это с помощью быстрого примера [Flask] на Python!
Общая архитектура машинного обучения
Давайте начнем с описания общего потока архитектуры обучения и прогнозирования:
Во-первых, создается обучающий конвейер для изучения прошлых данных в соответствии с целевой функцией.
Это должно вывести два ключевых элемента:
- Функции разработки признаков : преобразования, используемые во время обучения, должны повторно использоваться во время прогнозирования.
- Параметры модели : окончательно выбранный алгоритм и гиперпараметры должны быть сохранены, чтобы их можно было повторно использовать во время прогнозирования.
Обратите внимание, что разработка функций, выполненная во время обучения, должна быть тщательно сохранена, чтобы ее можно было использовать для прогнозирования. Одна обычная проблема среди многих других, которые могут возникнуть на этом пути, — это масштабирование признаков , необходимое для многих алгоритмов.
Если функция X1 масштабируется от значения 1 до 1000 и масштабируется до диапазона [0,1] с помощью функции f(x) = x/max(X1)
, что произойдет, если набор прогнозов будет иметь значение 2000?
О некоторых тщательных корректировках следует подумать заранее, чтобы функция сопоставления возвращала согласованные выходные данные, которые будут правильно вычислены во время прогнозирования.
Обучение машинному обучению против прогнозирования
Здесь необходимо решить главный вопрос. Почему мы разделяем обучение и прогнозирование с самого начала?
Совершенно верно, что в контексте примеров и курсов по машинному обучению, где все данные известны заранее (включая данные для прогнозирования), очень простой способ построить предиктор — это сложить данные обучения и прогнозирования (обычно называемые тестовый набор).
Затем необходимо тренироваться на «тренировочном наборе» и прогнозировать на «тестовом наборе», чтобы получить результаты, в то же время выполняя разработку функций как для обучающих, так и для тестовых данных, обучения и прогнозирования в одном и том же уникальном конвейере. .
Однако в реальных системах у вас обычно есть обучающие данные, и прогнозируемые данные поступают сразу после их обработки. Другими словами, вы смотрите фильм один раз, а потом у вас возникают вопросы по нему, а это значит, что ответы должны быть легкими и быстрыми.
Кроме того, как правило, нет необходимости повторно обучать всю модель каждый раз, когда поступают новые данные, поскольку обучение требует времени (для некоторых наборов изображений это может занять несколько недель) и должно быть достаточно стабильным с течением времени.
Вот почему обучение и прогнозирование могут быть или даже должны быть четко разделены во многих системах, и это также лучше отражает то, как обучается интеллектуальная система (искусственная или нет).
Связь с переоснащением
Разделение обучения и прогнозирования также является хорошим способом решения проблемы переобучения.
В статистике переоснащение — это «производство анализа, который слишком близко или точно соответствует определенному набору данных и, следовательно, может не соответствовать дополнительным данным или надежно предсказывать будущие наблюдения».
Переобучение особенно заметно в наборах данных со многими функциями или в наборах данных с ограниченными обучающими данными. В обоих случаях данные содержат слишком много информации по сравнению с тем, что может быть проверено предиктором, а некоторые из них могут даже не быть связаны с прогнозируемой переменной. В этом случае сам шум можно интерпретировать как сигнал.
Хороший способ контролировать переобучение — тренироваться на части данных и прогнозировать на другой части, по которой у нас есть основная правда. Поэтому ожидаемая ошибка для новых данных примерно равна измеренной ошибке для этого набора данных при условии, что данные, на которых мы тренируемся, представляют реальность системы и ее будущие состояния.
Таким образом, если мы разработаем правильный конвейер обучения и прогнозирования вместе с правильным разделением данных, мы не только решим проблему переобучения, но и сможем повторно использовать эту архитектуру для прогнозирования новых данных.
Последним шагом будет контроль того, чтобы ошибка в новых данных была такой же, как и ожидалось. Всегда есть сдвиг (фактическая ошибка всегда ниже ожидаемой), и нужно определить, какой сдвиг является приемлемым, но это не тема данной статьи.
REST API для прогнозирования
Вот здесь-то и пригодится четкое разделение обучения и прогнозирования. Если мы сохранили наши методы разработки функций и параметры модели, то мы можем создать простой REST API с этими элементами.
Ключевым моментом здесь является загрузка модели и параметров при запуске API. После запуска и сохранения в памяти каждый вызов API запускает инженерный расчет функций и метод «прогнозирования» алгоритма ML. Оба обычно достаточно быстры, чтобы обеспечить ответ в реальном времени.

API может быть спроектирован таким образом, чтобы он принимал один уникальный пример для прогнозирования или несколько разных (пакетные прогнозы).
Вот минимальный код Python/Flask, который реализует этот принцип, с JSON на входе и выходе JSON (вопрос на входе, ответ на выходе):
app = Flask(__name__) @app.route('/api/makecalc/', methods=['POST']) def makecalc(): """ Function run at each API call No need to re-load the model """ # reads the received json jsonfile = request.get_json() res = dict() for key in jsonfile.keys(): # calculates and predicts res[key] = model.predict(doTheCalculation(key)) # returns a json file return jsonify(res) if __name__ == '__main__': # Model is loaded when the API is launched model = pickle.load(open('modelfile', 'rb')) app.run(debug=True)
Обратите внимание, что API можно использовать для прогнозирования по новым данным, но я не рекомендую использовать его для обучения модели. Его можно было бы использовать, но это усложняет код обучения модели и может быть более требовательным к ресурсам памяти.
Пример реализации — совместное использование велосипедов
В качестве примера возьмем набор данных Kaggle, совместное использование велосипедов. Скажем, мы компания по прокату велосипедов, которая хочет прогнозировать количество прокатов велосипедов каждый день, чтобы лучше управлять обслуживанием велосипедов, логистикой и другими аспектами бизнеса.
Арендная плата в основном зависит от погодных условий, поэтому с помощью прогноза погоды эта компания может лучше понять, когда арендная плата достигнет пика, и постараться избежать обслуживания в эти дни.
Сначала мы обучаем модель и сохраняем ее как объект рассола, который можно увидеть в блокноте Jupyter.
Обучение и производительность модели здесь не рассматриваются, это всего лишь пример для понимания всего процесса.
Затем пишем преобразование данных, которое будет выполняться при каждом вызове API:
import numpy as np import pandas as pd from datetime import date def doTheCalculation(data): data['dayofyear']=(data['dteday']- data['dteday'].apply(lambda x: date(x.year,1,1)) .astype('datetime64[ns]')).apply(lambda x: x.days) X = np.array(data[['instant','season','yr','holiday','weekday','workingday', 'weathersit','temp','atemp','hum','windspeed','dayofyear']]) return X
Это просто вычисление переменной (день года), включающей как месяц, так и точный день. Существует также выбор столбцов и их соответствующий порядок, который необходимо сохранить.
Затем нам нужно написать REST API с помощью Flask:
from flask import Flask, request, redirect, url_for, flash, jsonify from features_calculation import doTheCalculation import json, pickle import pandas as pd import numpy as np app = Flask(__name__) @app.route('/api/makecalc/', methods=['POST']) def makecalc(): """ Function run at each API call """ jsonfile = request.get_json() data = pd.read_json(json.dumps(jsonfile),orient='index',convert_dates=['dteday']) print(data) res = dict() ypred = model.predict(doTheCalculation(data)) for i in range(len(ypred)): res[i] = ypred[i] return jsonify(res) if __name__ == '__main__': modelfile = 'modelfile.pickle' model = pickle.load(open(modelfile, 'rb')) print("loaded OK") app.run(debug=True)
Запустите эту программу, она по умолчанию будет обслуживать API на порту 5000.
Если мы протестируем запрос локально, все еще с помощью Python:
import requests, json url = '[http://127.0.0.1:5000/api/makecalc/](http://127.0.0.1:5000/api/makecalc/)' text = json.dumps({"0":{"instant":1,"dteday":"2011-01-01T00:00:00.000Z","season":1,"yr":0,"mnth":1,"holiday":0,"weekday":6,"workingday":0,"weathersit":2,"temp":0.344167,"atemp":0.363625,"hum":0.805833,"windspeed":0.160446}, "1":{"instant":2,"dteday":"2011-01-02T00:00:00.000Z","season":1,"yr":0,"mnth":1,"holiday":0,"weekday":3,"workingday":0,"weathersit":2,"temp":0.363478,"atemp":0.353739,"hum":0.696087,"windspeed":0.248539}, "2":{"instant":3,"dteday":"2011-01-03T00:00:00.000Z","season":1,"yr":0,"mnth":1,"holiday":0,"weekday":1,"workingday":1,"weathersit":1,"temp":0.196364,"atemp":0.189405,"hum":0.437273,"windspeed":0.248309}})
Запрос содержит всю информацию, которая была передана модели. Поэтому наша модель будет отвечать прогнозом прокатов велосипедов на указанные даты (здесь их у нас три).
headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'} r = requests.post(url, data=text, headers=headers) print(r,r.text) <Response [200]> { "0": 1063, "1": 1028, "2": 1399 }
Вот и все! Эту услугу можно легко использовать в приложении любой компании для планирования технического обслуживания или для того, чтобы пользователи знали о движении велосипедов, спросе и наличии велосипедов напрокат.
Собираем все вместе
Основной недостаток многих систем машинного обучения, особенно PoC, заключается в том, что они смешивают обучение и прогнозирование.
Если они тщательно разделены, прогнозы в реальном времени могут быть выполнены довольно легко для MVP с довольно низкими затратами на разработку и усилиями с помощью Python / Flask, особенно если для многих PoC он изначально был разработан с помощью Scikit-learn, Tensorflow, или любую другую библиотеку машинного обучения Python.
Однако это может оказаться невозможным для всех приложений, особенно для приложений, в которых разработка функций является тяжелой, или приложений, извлекающих наиболее близкое совпадение, которым необходимо иметь самые последние данные, доступные при каждом вызове.
В любом случае, вам нужно смотреть фильмы снова и снова, чтобы ответить на вопросы о них? То же правило применимо и к машинному обучению!