Наука о данных в реальном мире

Развертывание модели машинного обучения как API на AWS Elastic Beanstalk

Пошаговое руководство

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

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

Это руководство предназначено для специалистов по данным и начинающих специалистов по данным с небольшим опытом развертывания приложений и API в Интернете. К концу вы будете знать, как:

  1. Разработайте API модели с помощью Flask, веб-микрофреймворка для Python.
  2. Создавайте контейнеры для API как микросервиса с помощью Docker.
  3. Разверните API модели в Интернете с помощью AWS Elastic Beanstalk.

Зачем создавать API?

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

  • Обновления более просты, поскольку разработчикам каждой системы не нужно беспокоиться о том, чтобы сломать другую.
  • Более устойчивый, поскольку сбой в основном приложении не влияет на API модели и наоборот.
  • Легко масштабируется (при использовании микросервисной архитектуры для API).
  • Легко интегрируется с несколькими системами, например, с веб-страницами и мобильными устройствами.

Модель

Если вы разрабатываете портфель проектов, лучше использовать менее традиционные наборы данных, чтобы выделиться среди работодателей. Однако для этого урока я буду использовать знаменитый набор данных о ценах на жилье в Бостоне. Набор данных содержит несколько характеристик, которые можно использовать для прогнозирования стоимости жилых домов в Бостоне примерно в 1980 году.

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

Ознакомьтесь с моим репозиторием на GitHub, чтобы получить инструкции по быстрому построению и сохранению модели. Следуйте инструкциям и создайте API для своих проектов моделирования!

API

Создайте сценарий app.py в каталоге app/. Этот каталог также должен содержать сохраненную модель (model.pkl), если вы следовали инструкциям в репо.

Эти первые несколько строк в app/app.py импортируют полезные функции из Flask, NumPy и pickle. Мы также импортируем FEATURES из app/features.py, который указан ниже. Затем мы инициализируем приложение и загружаем модель.

# app/app.py
# Common python package imports.
from flask import Flask, jsonify, request, render_template
import pickle
import numpy as np
# Import from app/features.py.
from features import FEATURES
# Initialize the app and set a secret_key.
app = Flask(__name__)
app.secret_key = 'something_secret'
# Load the pickled model.
MODEL = pickle.load(open('model.pkl', 'rb'))

Функции

Я сохранил список функций в отдельном скрипте для обеспечения согласованности между обучением модели и прогнозами в API.

# app/features.py
FEATURES = ['INDUS', 'RM', 'AGE', 'DIS', 'NOX', 'PTRATIO']

Конечные точки

Наш объект приложения Flask (определенный выше как app = Flask(__name__)) имеет полезный метод декоратора, который упрощает определение конечных точек - .route(). В приведенном ниже коде @app.route('/api') указывает серверу выполнять функцию api(), определенную непосредственно под ним, всякий раз, когда http: // {your_ip_address} / api получает запрос.

# app/app.py (continued)
@app.route('/api', methods=['GET'])
def api():
    """Handle request and output model score in json format."""
    # Handle empty requests.
    if not request.json:
        return jsonify({'error': 'no request received'})
    # Parse request args into feature array for prediction.
    x_list, missing_data = parse_args(request.json)
    x_array = np.array([x_list])
    # Predict on x_array and return JSON response.
    estimate = int(MODEL.predict(x_array)[0])
    response = dict(ESTIMATE=estimate, MISSING_DATA=missing_data)
    return jsonify(response)

Запросы на синтаксический анализ

Нам нужно включить функцию parse_args() для анализа наших функций из запросов JSON.

# app/app.py (continued)
def parse_args(request_dict):
    """Parse model features from incoming requests formatted in    
    JSON."""
    # Initialize missing_data as False.
    missing_data = False
# Parse out the features from the request_dict.
    x_list = []
    for feature in FEATURES:
        value = request_dict.get(feature, None)
        if value:
            x_list.append(value)
        else:
            # Handle missing features.
            x_list.append(0)
            missing_data = True
    return x_list, missing_data

Запустить приложение

Наконец, запустите приложение на сервере разработки Flask, чтобы убедиться, что оно работает.

# app/app.py (continued)
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

Запустите сервер с $ python app.py. В другом окне терминала отправьте запрос к API, используя curl. Передайте функции с флагом --data в формате JSON.

$ curl -X GET "http://0.0.0.0:5000/api" -H "Content-Type: application/json" --data '{"INDUS":"5.9", "RM":"4.7", "AGE":"80.5", "DIS":"3.7", "NOX":"0.7", "PTRATIO":"13.6"}'
{
  "ESTIMATE": 18,
  "MISSING_DATA": false
}

Производственные веб-стеки

Веб-сервер разработки Flask отлично подходит для тестирования, но по причинам, о которых я не буду говорить здесь, нам придется поискать наш производственный стек в другом месте. Подробнее об этом можно прочитать здесь. Мы будем использовать Gunicorn для нашего сервера приложений и Nginx для нашего веб-сервера. К счастью, AWS Elastic Beanstalk по умолчанию выполняет за нас часть Nginx. Чтобы установить Gunicorn, запустите $ pip install gunicorn. Создайте сценарий app/wsgi.py и добавьте всего две строчки:

# app/wsgi.py
from app import app
app.run()

Теперь запустите $ gunicorn app:app --bind 0.0.0.0:5000. Вы должны иметь возможность выполнить ту же команду curl, что и выше, чтобы получить ответ от API.

$ curl -X GET "http://0.0.0.0:5000/api" -H "Content-Type: application/json" --data '{"INDUS":"5.9", "RM":"4.7", "AGE":"80.5", "DIS":"3.7", "NOX":"0.7", "PTRATIO":"13.6"}'
{
  "ESTIMATE": 18,
  "MISSING_DATA": false
}

Докер

В наши дни Docker в моде, и о его преимуществах написано много. Если вы хотите узнать больше о Docker и получить более подробное введение, прочтите это.

Для этого урока вам нужно будет установить Docker на свой компьютер, следуя инструкциям здесь. Вам также понадобится учетная запись Docker Hub.

Когда все будет готово, приступим! Для создания образа Docker вам понадобятся два основных файла: Dockerfile и requirements.txt.

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

# app/Dockerfile
# Start with a base image
FROM python:3-onbuild
# Copy our application code
WORKDIR /var/app
COPY . .
COPY requirements.txt .
# Fetch app specific dependencies
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
# Expose port
EXPOSE 5000
# Start the app
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:5000"]

requirements.txt содержит все пакеты Python, необходимые для нашего приложения.

# app/requirements.txt
Flask==1.0.2
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
simplejson==3.16.0
Werkzeug==0.15.2
numpy==1.16.4
pandas==0.24.2
scikit-learn==0.19.1
scipy==1.0.0
requests==2.22.0
gunicorn==19.9.0

Внутри каталога app/ запустите:

$ docker build -t <your-dockerhub-username>/model_api .
$ docker run -p 5000:5000 blemi/model_api

Ваше приложение теперь работает в контейнере Docker. Повторите команду curl, и вы получите тот же результат!

$ curl -X GET "http://0.0.0.0:5000/api" -H "Content-Type: application/json" --data '{"INDUS":"5.9", "RM":"4.7", "AGE":"80.5", "DIS":"3.7", "NOX":"0.7", "PTRATIO":"13.6"}'
{
  "ESTIMATE": 18,
  "MISSING_DATA": false
}

Запустите $ docker push <your-dockerhub-username>/model_api, чтобы отправить образ в вашу учетную запись Docker Hub. Этот последний шаг очень пригодится при развертывании в AWS Elastic Beanstalk.

AWS Elastic Beanstalk

Пора разместить наш API в Интернете, чтобы наши друзья и коллеги могли получить к нему доступ! Создайте AWS account и войдите в консоль. Примечание. Для создания учетной записи вам потребуется предоставить кредитную карту. Если вы будете следовать приведенным ниже инструкциям, не изменяя ни один из параметров, ваше приложение будет иметь право на бесплатное использование, а затраты будут минимальными. Запустив приложение, перейдите на панель управления платежами, где вы сможете увидеть свои ориентировочные ежемесячные расходы.

Затем мы создадим файл, который сообщает AWS, где получить доступ к нашему изображению. Назовите это Dockerrun.aws.json. Важнейшим элементом здесь является значение «Имя». Мое имя пользователя Docker Hub - «blemi», и я назвал изображение «model_api», поэтому я бы поставил blemi/model_api:latest в качестве значения «Name».

{
  "AWSEBDockerrunVersion": "1",
  "Image": {
    "Name": "<your-dockerhub-username>/model_api:latest",
    "Update": "true"
  },
  "Ports": [
    {
      "ContainerPort": "5000"
    }
  ],
  "Logging": "/var/log/nginx"
}

В консоли AWS найдите «Elastic Beanstalk» и выберите его.

Выберите «Создать новое приложение» в правом верхнем углу, добавьте имя и описание к своему приложению и нажмите «Создать».

Нажмите «Создать сейчас».

Оставьте выделенным «Среда веб-сервера» и нажмите «Выбрать», чтобы продолжить.

Если хотите, введите произвольное значение для «Домен» и «Описание». Для «Платформа» выберите «Предварительно настроенная платформа» и выберите «Докер» в раскрывающемся списке. В поле «Код приложения» выберите «Загрузить свой код» и нажмите кнопку «Загрузить».

Нажмите кнопку «Выбрать файл» и откройте файл Dockerrun.aws.json, который мы создали выше. Примечание. Это сработает, только если вы отправили образ Docker в Docker Hub.

Нажмите «Загрузить», затем «Создать среду», чтобы развернуть приложение.

Примечание. Если вы создаете API производственного уровня, вы, вероятно, захотите выбрать здесь «Настроить дополнительные параметры», прежде чем выбирать «Создать среду» - если вам интересно узнать о некоторых из другие варианты повышения безопасности и масштабируемости, свяжитесь со мной. Моя информация находится внизу этой статьи.

Для развертывания приложения потребуется несколько минут, но как только оно произойдет, вы сможете получить к нему доступ по URL-адресу, указанному в верхней части экрана:

Теперь давайте запустим команду curl в нашем API, размещенном в Интернете.

$ curl -X GET "http://api-demo-dattablox.us-west-2.elasticbeanstalk.com/api" -H "Content-Type: application/json" --data '{"INDUS":"5.9", "RM":"4.7", "AGE":"80.5", "DIS":"3.7", "NOX":"0.7", "PTRATIO":"13.6"}'
{"ESTIMATE":18,"MISSING_DATA":false}

Резюме и заключительные мысли

Мы создали API простой модели с помощью Python Flask, поместили его в контейнер с помощью Docker и развернули в Интернете с помощью AWS Elastic Beanstalk. Теперь вы можете воспользоваться этими знаниями и разработать API для своих моделей и проектов! Это делает сотрудничество с другими разработчиками более доступным. Им нужно только научиться использовать ваш API для интеграции вашей модели в свои приложения и системы.

Чтобы сделать этот API готовым к эксплуатации, необходимо сделать еще кое-что. В AWS и в самом приложении Flask можно настроить множество конфигураций для повышения безопасности. Есть также много вариантов, которые помогут с масштабируемостью при интенсивном использовании. Flask-RESTful - это расширение Flask, которое упрощает соблюдение лучших практик REST API. Если вы заинтересованы в использовании Flask-RESTful, ознакомьтесь с этим замечательным руководством.

Связаться

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

Если вы хотите связаться, свяжитесь со мной в LinkedIn. Спасибо за прочтение!