Введение

Часто у начинающих Data Scientists возникает вопрос, как демонстрировать работу своих моделей другим людям. Банальный пример - прикрепить ссылку на гитхаб репозиторий в отклике на вакансию или показать свое "детище" знакомым со словами "смотрите, что умею".

Проще говоря, мы хотим задеплоить нашу модель, превратить ее в демо нашего исследования.

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

В статье я расскажу простыми словами, что такое Docker и как его можно использовать для реализации своих решений в Machine Learning.

Что такое контейнеризация?

Одна из болей Data Scientist'a — клиенты, которые "запутались в установке", у которых "непонятно написано" или "все сломалось". Контейнеризация позволяет решить эту проблему.

Контейнер — отдельная операционная система, настроенная заранее. Она выполняет указания, которые даются при сборке образа этой системы. Контейнер использует процессорные ядра и оперативную память хост-машины. Другими словами, внутри нашей операционки мы запускаем другую с заранее подготовленной последовательностью действий.

Docker

Прежде чем начинать работу, необходимо установить docker на компьютер или сервер.

Создание образа

Сначала нужно создать образ сервера, который называется docker image. Это можно сделать несколькими способами, но самый простой и понятный - создать файл с названием Dockerfile. Это будет скрипт, содержащий последовательность действий для сборки образа.

В Docker образы могут наследоваться друг от друга, поэтому обычно в первой строчке за основу берут готовый образ:

FROM python:3.8-slim-buster

Готовые образы вы можете найти на docker hub

Теперь следует описать логику инициализации контейнера.

Для этого существует много директив, например RUN - для запуска какой-либо команды на сервере при сборке образа.

Ниже я опишу директивы, которые сам использовал для создания своего первого Docker-образа:

RUN mkdir /app

Команда, которая создает папку app внутри контейнера.

COPY . /app/
WORKDIR /app

Следующей командой мы копируем все файлы из локального диска (директории, где находится Dockerfile) в папку app. Затем мы объявляем app рабочей директорией.

RUN pip install -r requirements.txt

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

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

ENTRYPOINT ["python"]
CMD ["demo.py"]

Команда ENTRYPOINT позволяет объявить точку входа для сервера. Таким образом, при развертке сервера я запускаю Python в контейнере. Директива CMD говорит, что в командной строке/терминале я выполняю код внутри скобок.

Мы можем объединить две эти команды в одну:

CMD ["python", "demo.py"]

Создание контейнера

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

docker build . -t simp_server

Точка означает, что мы хотим собрать образ из Dockerfile, который находится в данной директории.

  • -t - тег для обозначения имени сервера

  • simp_server - любое имя сервера.

Итак, образ собран.

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

docker images

Запускаем сервер следующей командой:

docker run --rm -it -p 8888:8888 simp_server
  • run создает контейнер.

  • --rm удаляет контейнер после завершения его работы.

  • -p 8888:8888 пробрасывает порты между хост-машиной и контейнером (порт на хост соответствует порту в контейнере).

  • simp_server определяет образ запускаемого сервера.

Пример деплоя модели c использованием Docker и flask

github репозиторий проекта

Суть проекта в том, что пользователь загружает картинку с персонажем из "Симпсонов" и получает предположение сети о том, кто на ней изображен.

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

Это тот код, который я прошу запустить docker сервер на старте.

import os

from flask import Flask
from flask import request
from flask import render_template

from model.model import MobNetSimpsons


app = Flask(__name__)
UPLOAD_FOLDER = "static/"

@app.route('/', methods=['GET', 'POST'])
def upload_predict():
    if request.method == "POST":
        image_file = request.files["image"]
        image_location = os.path.join(
            UPLOAD_FOLDER,
            image_file.filename
        )
        if image_file:
            image_file.save(image_location)
            pred = model.predict(image_location)
            return render_template(
                "index.html", 
                prediction=pred[0],
                proba=round(pred[1], 2),
                image_loc=image_file.filename
            )
    return render_template("index.html", prediction=0, image_loc=None)

if __name__ == "__main__":
    model = MobNetSimpsons()
    app.run(port=8888, debug=True, host='0.0.0.0')

Также, мне понадобится класс, в котором инициализируется модель и объявляется метод для получения предсказания:

import pickle
import numpy as np

from torchvision import models
from torch import nn

from model.utils import *

DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'

class MobNetSimpsons():
    def __init__(self):
        print("Loading model...")
        self.model = models.mobilenet_v3_large(pretrained=True)
        num_features = 960
        n_classes = 42
        self.model.classifier = nn.Sequential(
            nn.Linear(num_features, 1280, bias=True),
            nn.Hardswish(),
            nn.Dropout(p=0.2, inplace=True),
            nn.Linear(1280, n_classes, bias=True) 
        )
        print("Seting parameters...")
        self.model.load_state_dict(torch.load('model/mobNetLarge.pth', map_location=DEVICE))
        print("Seting on evaluation mode...")
        self.model.eval()

        self.label_encoder = pickle.loads(open('model/label_encoder.pkl', 'rb').read())
        print("Model is ready!")

    def predict(self, image_path):
        img = prepare_img(image_path)

        proba = predict_one_sample(self.model, img, device=DEVICE)
        predicted_proba = np.max(proba)*100
        y_pred = np.argmax(proba)

        label = self.label_encoder.inverse_transform([y_pred])[0].split('_')
        label = label_to_string(label)

        return [label, predicted_proba]

Затем создаю Dockerfile и собираю образ:

docker build . -t simp_server

Далее создаю контейнер:

docker run --rm -it -p 8888:8888 simp_server

После этого перехожу по адресу http://localhost:8888/.

Если контейнер и приложение работают на вашем компьютере, то они будут работать и на другом компьютере, например, у вашего клиента.

Остается дать порядок команд для Docker'a.

Для моего приложения порядок установки выглядит так:

  1. Install and run Docker

  2. Build Docker image using docker build . -t simp_server

  3. Run Docker container using docker run --rm -it -p 8888:8888 simp_server

  4. Go to http://localhost:8888/

Магия

Если вашей моделью захотят воспользоваться, для этого нужно будет:

  1. Установить Docker

  2. Загрузить репозиторий

  3. Собрать образ (docker image)

  4. Создать и запустить контейнер

Что дальше?

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

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

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

Полезные ссылки

Подробнее про Docker

Docker Documentation

Flask Documentation

Комментарии (6)


  1. DaryaTsvetkova
    24.07.2021 13:05

    Спасибо за статью!


  1. Kopilov
    24.07.2021 14:13

    Пользовались ли вы когда нибудь Docker? — Да

    Считаете ли вы docker удобным для передачи продукта? — Да

    Вы Data Scientist — Нет

    (Фактически, плаваю где-то между бэкендером и инженером данных с навыками девопса, но не сайентист точно)


    1. pavelkochkin1 Автор
      24.07.2021 14:24

      Да, сейчас такое время, что границы ds очень размыты :) я думаю так можно сказать не только про сферу данных


  1. TyVik
    24.07.2021 19:15
    +3

    Как-то такая статья про Docker в 2021 году слабовата.


  1. AmberSP
    06.08.2021 22:02

    Если надо заказчику ирисы показать, проще stremlit использовать с его хостингом


    1. pavelkochkin1 Автор
      07.08.2021 21:28

      Посмотрел библиотеку, выглядит интересно, большое спасибо за совет!