Руководство для начинающих по развертыванию с Dash: развертывание модели, которая классифицирует картины из Метрополитен-музея в Нью-Йорке

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

Этот пост предназначен в качестве учебного пособия для специалистов по данным, заинтересованных в развертывании своих моделей и изучении самых основ Plotly Dash и Heroku.

Как развернуть?

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

Один из самых простых вариантов - развернуть вашу модель как простое веб-приложение. Это означает, что ваша модель становится веб-службой, которая может принимать запросы. Вы можете думать о запросе как о заказе для прогноза от вашей модели, учитывая те же входные функции, которые использовались для обучения модели изначально. Затем веб-служба запускает эти входные функции через модель и возвращает прогноз в качестве выходных данных. Прелесть веб-сервисов в том, что вывод может быть использован практически любым другим приложением, включая приложения, написанные на других языках. Благодаря этому простые веб-службы отлично подходят для взаимодействия с другими компонентами компьютера. Мы рассмотрим, как развернуть вашу модель в виде простого веб-приложения здесь.

Но что, если мы не слишком заботимся о том, чтобы наши прогнозы легко обрабатывались другими приложениями? Что, если нас больше беспокоит то, что они легко потребляются людьми? В частности, распознавание изображений - это очень наглядная область машинного обучения. В этом случае часто имеет смысл не просто вернуть предсказание в виде строки текста из веб-службы, а, скорее, отобразить его вместе с соответствующим изображением.

Для этого нам нужно создать интерфейсное визуальное приложение. Для этого мы будем использовать Plotly Dash, популярный фреймворк для создания интерфейсных приложений и панелей мониторинга на Python. По сути, Dash позволяет писать код HTML и CSS на Python. Эти два языка обычно используются при создании веб-сайтов для добавления таких элементов, как кнопки и полосы прокрутки, а также для изменения цвета и шрифтов на веб-странице.

Архитектура нашего приложения

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

Вход модели

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

В конечном итоге мы решили, что было бы лучше просто позволить пользователям генерировать случайное изображение из коллекции Met и просматривать прогноз, основанный на этом.

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

Выход модели

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

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

Другая идея заключалась в том, чтобы показать относительную вероятность каждой метки культуры, предсказанной моделью, а не просто метку с наивысшим правдоподобием (окончательный прогноз). Это было бы особенно интересно посмотреть для картин, которые модель предсказывала неверно.

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

Создание приложения

Теперь, когда мы определились с входами и выходами нашего приложения, то есть с тем, что оно должно показывать и как пользователи приложения должны с ним взаимодействовать, пора начать создавать его с помощью Plotly Dash.

Самым первым шагом в создании нашего приложения Dash является его инициализация. Это приложение будет вызываться всякий раз, когда запускается скрипт Python, содержащий код приложения (обычно называемый app.py). Когда приложение вызывается, оно отображается через локальный порт. Адрес порта будет показан в терминале после запуска скрипта app.py в командной строке. Щелчок по этой ссылке локального порта запускает страницу браузера, показывающую приложение. Ниже показан простой стартовый код для этого шага. Запустить приложение так просто!

В вызове app.run_server() мы устанавливаем debug=True, чтобы наше приложение автоматически обновлялось при внесении изменений.

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

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

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

Код для этого первого компонента, а также аналогичная часть описания под заголовком показаны ниже.

Мы начинаем с добавления строки, а затем столбца для каждого из наших элементов. Наконец, мы добавляем компонент HTML, в котором указывается тип текста, который мы хотели бы иметь. Заголовок будет иметь самый большой размер заголовка, предлагаемый в HTML, H1. Описание будет в обычном тексте, здесь оно называется Font.

Затем мы используем словари аргументов ключевых слов, чтобы указать такие вещи, как цвета и выравнивание текста. Вывод этого кода показан ниже.

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

Работа с обратными вызовами приложений

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

Для этого нам нужно использовать важную функцию Dash, которая называется @app.callback(). Эта функция представляет собой декоратор Python, который можно увидеть по символу @ в начале функции. Декоратор помещается прямо перед функцией, чтобы добавить к ней некоторые дополнительные функции. В этом случае декоратор заставляет любую написанную нами функцию работать с компонентами нашего приложения Dash. Он использует компоненты Dash Input() и Output(), чтобы сделать входные и выходные данные любой функции, которую мы пишем, обновляемой другими компонентами в приложении. Например, когда нажимается наша кнопка «генератор случайных изображений», мы можем использовать это действие щелчка в качестве входных данных для функции, сообщая ей о запуске. Результат каждого нажатия кнопки затем может быть возвращен в другие части приложения. В приведенном ниже коде показано, как это работает для кнопки «генератор случайных изображений». Он использует простой CSV-файл, созданный на основе наших обучающих данных, который содержит всего 2 столбца для каждого рисунка: один с его меткой, культурой, которой он был помечен, и один с гиперссылкой на соответствующее изображение на серверах Met.

Поскольку мы не хотим, чтобы image_culture отображался сразу, а просто сохранялся, мы возвращаем его как компонент, в котором аргументы ключевого слова не отображаются. Мы делаем то же самое для другого вывода, называемого random_image. Это потому, что на самом деле это просто URL-ссылка на изображение, а не само изображение. Нам нужно будет передать его в другой компонент Dash, чтобы полностью отобразить изображение.

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

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

Включение модели машинного обучения

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

Следующим шагом будет использование нашей модели для прогнозирования этих новых данных. Для этого мы загрузим нашу сохраненную модель из этапа обучения модели, обсуждаемого в части 1 этой серии, и будем использовать его метод .predict() для генерации прогнозов для вновь созданных изображений.

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

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

Наше обновленное приложение (показано ниже) теперь отображает прогноз модели под изображением.

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

Теперь наше приложение делает все, что мы от него хотим: автоматически генерирует случайное изображение из коллекции для пользователя, визуализирует изображение и возвращает прогноз с цветовой кодировкой. Последний шаг в создании приложения - это просто улучшить внешний вид приложения и исправить выравнивание. Для этого мы регулируем ширину и выравнивание различных компонентов. Затем мы добавляем таблицу стилей CSS для изменения шрифтов.

И вуаля! Приложение готово:

Развертывание приложения с помощью Heroku

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

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

Однако мы столкнулись с несколькими небольшими ошибками и проблемами при отправке нашего кода в Heroku. Мы получили ошибку о том, что «размер слага» был слишком большим, что по сути означает, что наш код и зависимости слишком велики для бесплатного развертывания на Heroku. TensorFlow может быть слишком большим, и поскольку Heroku в настоящее время не предлагает поддержку графического процессора (что в значительной степени делает стандартную библиотеку TensorFlow такой большой, как зависимость), мы переключились на tensorflow-cpu вместо стандартного Tensorflow. Это также заставило нас перейти на другую версию CV2, библиотеки предварительной обработки, которую мы использовали, под названием opencv-python-headless. Эти изменения уменьшили размер нашего проекта настолько, чтобы его можно было бесплатно развернуть на Heroku. Просто о чем следует помнить, если вы планируете в будущем развернуть свой проект распознавания изображений на Heroku.

Теперь, когда приложение размещено на Heroku, вы можете попробовать его здесь: Попробуйте приложение!