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

Инженерные препятствия на последнем этапе часто отнимают много времени и требуют решения. Фактически, отчет Algorithima показал, что во многих организациях более 50% времени их специалистов по данным тратится на развертывание моделей машинного обучения в производстве.

Я узнал об этом из первых рук во время 48-часового медицинского хакатона, в котором я принимал участие в течение 2020 года. В этом посте я поделюсь своим путешествием (кхм, моими *ошибками*) и уроками, извлеченными из развертывания модели классификации кашля COVID, чтобы вы не становитесь жертвой одних и тех же ловушек.

Немного предыстории проекта

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

В течение первых 24 часов мы натренировали модель до такой степени, что смогли с точностью более 80 % обнаруживать COVID при кашле. Когда модель близилась к завершению, казалось, что победа уже не за горами! Мы сделали невозможное, и мир будет ликовать, когда мы будем спасать жизни по одному кашлю за раз. 😉

Когда пришло время интегрировать модель в сервер, я экспортировал веса модели, предварительно обработал данные кашля с микрофона и использовал модель для классификации данных кашля. Оценка модели на моем Macbook показала точность около 83%. Однако когда тот же набор данных был оценен на сервере, где мы планировали его разместить, модель работала ужасно!

Что я пробовал

Первое, что я сделал, это последовал рекомендации PyTorch по сохранению весов модели (`model.state_dict()`).

Это не сработало.

Затем я попытался замариновать всю «модель» с помощью «pytorch.save», думая, что если бы методы класса были упакованы вместе с весами, я мог бы избежать проблем.

Это тоже не сработало.

Я рассудил про себя, что если версии пакетов одинаковые, то и модель должна работать одинаково. Поэтому я использовал «заморозку пипа», чтобы найти точные зависимости в среде и отправил упакованную версию, и…

Модель снова не прошла оценку.

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

Блин… ну, в игре было слишком поздно, чтобы компенсировать это, поэтому в итоге мы просто разместили модель на моем ноутбуке и вызывали к ней API с сервера. 🤷

Что я выучил

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

Итак, что я узнал в итоге?

Урок №1: Сохраните модель на основе формата фреймворка

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

Урок № 2: Используйте одинаковые версии зависимостей

Замораживание и привязка к точным версиям и транзитивным зависимостям, которым была обучена модель, — хороший способ получить воспроизводимые результаты. Это снижает риск того, что разные библиотеки Python обрабатывают аргументы по-разному, независимо от того, изменяются ли аргументы по умолчанию или какие операции применяются к этим аргументам. Такие инструменты, как поэзия и pip-tools, также могут помочь организовать ваши зависимости.

Урок № 3. Используйте контейнерные среды

Когда вы разрабатываете модель в одной операционной системе, а затем развертываете ее в другой (в нашем случае, MacOS и Windows соответственно), рекомендуется использовать контейнерные среды, такие как Docker, Nix или другой обработчик пакетов. Если операционная система и машина остаются неизменными от разработки до производства, результаты должны быть воспроизводимыми. Во время хакатона к тому времени, когда мы поняли, что должны были сделать, для нас было уже слишком поздно — поэтому в итоге мы запустили классификатор на моем ноутбуке.

Урок № 4. Версируйте каждую модель

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

Обслуживание моделей ML стало проще благодаря BentoML

Во время моей ретроспективы после хакатона я обнаружил, что я не единственный, кто сталкивается с этими проблемами — и, что еще лучше, я обнаружил, что есть решение!

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

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

Давайте воссоздадим описанную выше ситуацию, используя BentoML для обслуживания модели.

bentoml.pytorch.save("my_model", модель)

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

список моделей Bentoml

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

bentoml.pytorch.load(“my_model”)

Так легко загрузить модель обратно в любом контексте, который вам нужен.

Теперь, чтобы развернуть свой сервис, вы будете использовать простой файл конфигурации.

# bentofile.yaml
service: “service.py:svc” # A convention for locating your service:
description: “file: ./README.md”
include:
- “*.py” # A pattern for matching which files to include in the bento
python:
  packages:
  - pytorch # Additional libraries to be included in the bento
docker:
  distro: debian
  gpu: True

bentofile.yaml определяет, какие файлы принадлежат развертываемому и соответствующие зависимости. Таким образом, сервис максимально облегчен.

сборка бентомла

бентомл контейнеризовать

Наконец, при создании вашего бенто библиотека заморозит ваши версии зависимостей и упакует их в один развертываемый пакет вместе с вашей моделью и сервисным кодом. Затем, чтобы уменьшить любые дополнительные сложности, BentoML создает док-контейнер, который предоставляет собственные библиотеки и может запускать ваш код независимо от каких-либо нюансов операционной системы.

Заключение

Хотя это был просто дружеский хакатон, призванный научить нас и бросить вызов нам во время пандемии, это также реальность для многих проектов машинного обучения. Библиотеки машинного обучения продвинулись далеко вперед за последние 10 лет, но операционализация этих моделей по-прежнему требует инженерной работы, которая имеет множество нюансов и создает уникальные проблемы, которые отличаются от традиционных программных операций.

Хорошей новостью является то, что такие решения, как BentoML, появились на рынке, чтобы решить именно эти проблемы, чтобы больше специалистов по данным могли запускать больше моделей в производство — и в конечном итоге изменить мир!

Присоединяйтесь к нам в Slack, чтобы узнать больше об отправке моделей в производство.