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

Поиск масштабной инфраструктуры электроснабжения

Доступ к электричеству является серьезной проблемой в некоторых развивающихся странах. Настолько, что Организация Объединенных Наций специально выделила одну из своих 15 Целей в области устойчивого развития (в частности, № 7) на обеспечение доступа к недорогой, надежной и современной энергии для всех. Многие организации упорно работают над улучшением доступа к энергии по всему миру, но часто бывает трудно найти карты существующей электросети. Без карт инфраструктуры эти группы по существу не знают, куда вкладывать ресурсы для улучшения инфраструктуры или выбирают автономные решения (например, солнечную и ветровую энергию).

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

Обработка 120 миллионов изображений

Боль начала нарастать при выполнении вывода (или прогноза) на основе 120 000 000 спутниковых изображений, охватывающих три наши целевые страны: Нигерию, Пакистан и Замбию. Мы вручную управляли 5 экземплярами графических процессоров на Amazon EC2, каждый из которых обрабатывал новый пакет изображений каждые 5–6 часов. Вскоре после завершения каждого пакета нам нужно было вручную подготовить новый пакет изображений и перезапустить цикл. Наши сроки были сжатыми и требовали, чтобы эти машины работали на полную мощность в течение почти 2 недель подряд. Это был непрактичный, менее чем изящный конвейер, который иногда ощущался так, как будто его скрепляли цифровым эквивалентом слюны и клея.

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

Введение в обслуживание TF

TF Serving помогает упаковать модель Keras (или TensorFlow) как образ Docker. Это дает много преимуществ, но три из них очень практичны:

  1. Параллельное масштабирование. После создания образа Docker вы можете удалить несколько клонов своей модели, чтобы незначительно увеличить скорость вывода. Что касается спутниковых снимков, это позволяет нам очень быстро переходить от общегородского к общегосударственному масштабу.
  2. Совместное использование: Docker имеет службу для размещения изображений на DockerHub. После того, как вы отправили свой образ для обслуживания TF в DockerHub, любой компьютер, на котором можно запустить Docker (то есть почти любой ноутбук, настольный компьютер или облачный сервер), также сможет запустить вашу модель машинного обучения.
  3. Реальное развертывание. Образы для обслуживания TF действуют как небольшой сервер, который предоставляет RESTful API. Это означает, что вы можете отправлять запросы на вывод и получать результаты, используя стандартизованный протокол, который работает через Интернет.

Пошаговое руководство по коду: создание изображения для обслуживания TF из модели Keras

Давайте перейдем к коду. Мы разделим руководство на 3 части:

  1. Экспорт модели, готовой к контейнерному развертыванию.
  2. Упакуйте экспортированную модель в образ TF Serving Docker
  3. Отправьте запросы на вывод в нашу развернутую модель

1. Экспорт модели, готовой к выводу

Во-первых, нам нужно убедиться, что применяется правильная предварительная обработка, что особенно актуально для моделей компьютерного зрения. Например, модель Xception требует преобразования uint8 значений [0, 255] в float на интервале [-1, 1]. При обучении модели это часто происходит вне самой модели (например, на этапе увеличения изображения. Если это так, нам нужно добавить эту предварительную обработку в наш график вычислений вывода. Не забудьте обновить функцию ниже, если вам нужно масштабировать значения пикселей по-разному.Подробнее о serving_input_receiver_fn читайте в Руководстве TF по сохранению и восстановлению.

После написания функции предварительной обработки изображений мы собираемся экспортировать нашу модель Keras. Мы преобразуем модель в объект tf.estimator, который представляет собой высокоуровневый формат, который упростит экспорт модели как tf.saved_model (подробно объяснено здесь).

Попробуйте запустить строку ниже, чтобы экспортировать наш объект tf.saved_model. К сожалению, TF может не совсем правильно настроить структуру каталогов. Если выполнение следующей строки приводит к тому, что TF выдает ошибку об отсутствии файла контрольной точки, скопируйте .checkpoint file из каталога .../estimator/keras на один уровень выше в каталог .../estimator, а затем повторите попытку.

В зависимости от ваших настроек подробности TensorFlow может распечатать некоторую релевантную информацию на этом этапе. Он сообщит вам, откуда была загружена модель Keras, информацию о TF SignatureDef (здесь нас интересуют Predict и serving_defaultkey) и местонахождение объекта tf.saved_model, который теперь включает вашу функцию предварительной обработки.

TF сохранит вашу модель чтения вывода в каталог с отметкой времени в export_dir. Вы должны увидеть переменные saved_model.pb и их веса в каталоге, подобном /path/to/my_exported_models/001/1548701206.

2. Упаковка вашей модели в сервис TF

Теперь мы создадим образ Docker для обслуживания TF. Это упростит загрузку и развертывание нашей модели машинного обучения на любом компьютере, подключенном к Интернету. Приведенный ниже код в основном заимствован из официального примера TF Docker здесь.

Обычно я копирую содержимое экспортированной модели вверх на один каталог из чего-то вроде .../my_exported_models/001/1548701206/ в .../my_exported_models/001/, тем самым удаляя каталог временных меток. Я рекомендую сделать это, так как это также позволит вам легко вызывать разные версии одной и той же модели из одного контейнера Docker (если вы добавите версии модели 002, 003 и т. Д.).

Выполните в командной строке следующее:

Вы также можете создать образ Docker с поддержкой графического процессора, вместо этого вытащив tensorflow/serving-gpu и повторив приведенный выше код. В этом случае пометьте свою модель графического процессора чем-то вроде v1-gpu. Как и в приведенном выше коде, запустите образ локально на порту 8501, например: docker run -p 8501:8501 -t developmentseed/hv_grid:v1. Убедитесь, что модель работает, и получите ее метаданные, посетив что-то похожее на http://localhost:8501/v1/models/hv_grid/metadata

Вы также можете отправить свой образ Docker в Docker Hub для удобного обмена:
docker push <my_org>/<image_name>:<image_tag>

3. Отправка запросов на вывод в работающую модель.

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

Основная проблема здесь заключается в том, чтобы кодировать Base64 наши данные, то есть способ кодирования двоичных данных в виде строки. Если вы наивно пытались отправить предварительно обработанные изображения (например, как float32 данные изображения), TF позволит вам. Вы наивно передаете данные изображения в виде строки (цифры, десятичные дроби и все такое), а затем заставляете свой сервер вывода преобразовывать все в тензор. Однако это ужасно неэффективно.

Вместо этого сохраняя изображения как тип данных uint8, кодируя данные изображения в base64 и выполняя предварительную обработку с нашим serving_input_reciever_fn на стороне сервера, я эмпирически обнаружил, что прогноз размер полезной нагрузки был примерно в 45 раз меньше для стандартных спутниковых изображений размером 256x256x3 пикселей. Это особенно важно для пакетной обработки больших наборов изображений - меньшая полезная нагрузка означает (1) более быструю передачу данных по сети в ОЗУ вашего контейнера и (2) более быструю передачу памяти из ОЗУ на карту графического процессора для обработки. Жизненно важно, чтобы время передачи было коротким, потому что это означает, что вы сохраните высокую степень использования графического процессора (и пропускную способность логических выводов). Дополнительную справочную информацию об отправке / получении данных см. На странице TF Объяснение RESTful API.

Примечание. Вы можете изменить свой server_endpoint ниже, если хотите запустить контейнер Docker в облаке - просто убедитесь, что соответствующий порт (по умолчанию 8501) открыт, и замените IP-адрес вашего экземпляра на localhost.

payload json должен соответствовать строгой структуре. Если бы мы должны были распечатать его, он должен был бы иметь примерно такую ​​структуру (вероятно, без символов новой строки):

Теперь давайте отправим полезные данные закодированного изображения для вывода:

Если все работает, вы должны вернуть некоторый json-контент, содержащий выходные данные вашей модели! Благодаря довольно стандартной архитектуре XCeption мы смогли обрабатывать около 1 600 000 фрагментов спутниковых изображений в час на двух экземплярах AWS p3.2xlarge (с графическими процессорами V100). Удачи и не забудьте поискать новые решения, поскольку кодовая база обслуживания TF (и вывод ML в целом) быстро развивается.

Другие хитрости

  • TensorFlow полагается на aSignatureDef, чтобы указать, как вы будете предоставлять входные данные. Обычно мы будем использовать определение подписи Predict (обозначено суффиксом :predict в нашем запросе POST в разделе 3). Однако есть несколько разных способов отправки запросов на вывод, и вы можете прочитать более подробную информацию о Classify и Regress SignatureDef на странице TF’s SignatureDef.
  • TensorFlow придирчиво подходит ко многим клавишам, используемым при перемещении данных по этому конвейеру. Вы должны убедиться, что:
    1. Имя тензора в операторе serving_input_receiver_fn return совпадает с именем входного слоя вашей модели Keras. (Здесь это было input_1, как в разделе 1).
    2. Имя тензора в вашем операторе serving_input_receiver_fn return совпадает с ключом, используемым в вашем json-ключе для каждой прогнозируемой выборки. (Здесь это было image_bytes, как в разделах 1 и 3).
    3. Вы сохраняете ключ b64 для данных base64. Это указывает TensorFlow декодировать строковые данные обратно в байты.
    4. Вы сохраняете ключ instances в наших полезных данных json. Это позволяет TF узнать, что идет пакет данных. См. Страницу TF REST API для получения дополнительной информации.
  • REST против gRPC. RESTful API относительно новый для TF. Вызов удаленных процедур Google (gRPC) - это еще один протокол для взаимодействия с моделями обслуживания TF, который предположительно быстрее, но сложнее. Лично мне не удалось запустить его, но есть как минимум одно сравнение, если вы хотите его протестировать.