Docker для специалиста по анализу данных. Разбираемся с Контейнерами.
Введение
Часто у начинающих 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
Суть проекта в том, что пользователь загружает картинку с персонажем из “Симпсонов” и получает предположение сети о том, кто на ней изображен.
Сначала я описываю логику приложения, используя библиотеку 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.
Для моего приложения порядок установки выглядит так:
- Install and run Docker
- Build Docker image using
docker build . -t simp_server
- Run Docker container using
docker run --rm -it -p 8888:8888 simp_server
- Go to
http://localhost:8888/
Магия
Если вашей моделью захотят воспользоваться, для этого нужно будет:
- Установить Docker
- Загрузить репозиторий
- Собрать образ (docker image)
- Создать и запустить контейнер
Что дальше?
В статье я расскрыл лишь одну проблему, которую может решить Docker, – доставка приложения до клиента.
Вы можете попробовать задеплоить свою модель, даже если она просто классифицирует ирис.
Таким образом, вы лучше поймете, как работать с Docker, и в дальнейшем сможете применять его в своих проектах.