Добро пожаловать во второй выпуск GitHub All-Stars. На прошлой неделе у нас была возможность проанализировать бот, созданный для Saliens - веб-игру, созданную в поддержку летней распродажи Steam. Теперь мы попробуем взглянуть на что-нибудь более тяжелое и более сложное. Мы снова попытаемся провести анализ кодовой базы, на этот раз сосредоточившись на justadudewhohacks / face-api.js - отличном примере использования TensorFlow в веб-приложении.

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

Глубокое обучение - определенно крутая штука, наверное, большинство из вас со мной согласятся. Я интересуюсь Tensor Flow в течение более длительного периода времени. Я даже немного поэкспериментировал с этим, пытаясь взломать приложение с помощью TensorFlow Mobile (в настоящее время называется TensorFlow lite). Тем не менее, я действительно не потратил достаточно времени, чтобы стать посредственным пользователем этой мощной библиотеки глубокого обучения от Google - поэтому чтение кода реального проекта, который был не просто примером использования, было очень сложным, но еще и очаровательный. Я многому научился, просто готовя этот пост в блоге, и надеюсь, что у вас возникнут точно такие же чувства после его прочтения.

Когда в конце марта был представлен TensorFlow.js (браузерная версия Tensor Flow), появление великих проектов, использующих его, было вопросом времени. Как всем известно, экосистема JavaScript на удивление плодородна. Тем не менее, насколько мне известно, до прошлой недели не было ни одного интересного проекта с открытым исходным кодом, основанного на этой библиотеке. face-api.js - первый сайт, который стал популярным в сообществе.

Прежде чем мы начнем обзор, давайте опишем, какова на самом деле цель нашего анализа. face-api.js - это библиотека, которая использует CNN (сверточные нейронные сети) для обнаружения (и сопоставления) лиц на изображении. Он является потомком face-распознавания.js - на этот раз с использованием ранее упомянутого, недавно выпущенного tensorflow.js. Стоит упомянуть, что в библиотеке есть отличное руководство, написанное ее создателем, в котором описаны возможные варианты использования - я настоятельно рекомендую прочитать его. Но что особенно полезно, так это отличный набор примеров. Их открытие дает множество замечательных вариантов использования (все они посвящены теории Большого взрыва - я перестал смотреть это шоу много лет назад, но все еще остались приятные воспоминания). Не могу сказать, что количество образцов меня не впечатлило. Есть довольно простое распознавание лиц, как на статическом изображении, так и на видео. Есть поиск совпадений конкретных лиц. Также есть возможность рисовать ориентиры того, как компьютер видит лицо.

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

В качестве входа я начал с файла examples / views / faceDetection.html. Помимо связующего кода, есть еще две интересные функции. Первый из них - run - выполняется при запуске приложения.

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

Там происходят действительно интересные вещи:

Помимо некоторых манипуляций с холстом, у нас есть две функции, запускаемые API: первая (faceapi.locateFaces) (что удивительно ...) обнаруживает лица, вторая (faceapi.drawDetection) помещает результаты в холст, созданный поверх изображения, на котором мы ищем лица. В следующей части этой записи блога мы рассмотрим три упомянутые функции faceapi и то, как они были реализованы.

Заглянем в саму библиотеку.

Первое наблюдение - целая библиотека написана на TypeScript. Второе замечание - необходимо скачать около 90 МБ моделей, которые хранятся в каталоге веса. Еще стоит упомянуть, что библиотека имеет только одну производственную зависимость: сам TensorFlow.js.

Если углубиться, основной API для всего модуля, который хранится в globalApi.ts, очень короткий и лаконичный - четыре метода для загрузки моделей для определенных видов вычислений и еще три для этих вычислений. Как я уже писал ранее, мы сосредоточимся только на тех, кто связан с процессом распознавания лиц.

Метод globalApi.loadFaceDetectionModel - это просто прокси для загрузки метода в экземпляре FaceDetectionNet. Нам нужно пойти глубже.

Вот и первый сюрприз и интересное открытие - переменная, которая выглядела как простой URL-адрес каталога моделей, может быть чем-то большим, чем просто String. Это также может быть Float32Array. Строгая типизация сохраняется с помощью Union Types - очень мощной конструкции, которая позволяет создавать сложные типы, которые имеют несколько идентичностей. Хотя TypeSafe не полностью реализован, как в таких языках, как ex. Ceylon - нам все еще нужно использовать оператор instanceof - это хороший компромисс гибкости и безопасности.

Другая интересная, хотя и скрытая, часть используемого здесь JavaScript - это Float32Array, TypedArray, который является экземплярами ArrayBuffer. Добавлен в ECMAScript 2015, в основном используется для обработки двоичных данных. Как мы видим, если weightsOrUrl имеет значение Float32Array, параметры для распознавания лиц извлекаются с помощью метода extractWeights, который выполняется синхронно. Тем не менее, в анализируемом нами примере используется простой URL-адрес, поэтому мы перейдем строго к асинхронным параметрам loadQuantizedParams.

Поскольку в нашу функцию явно не передана модель, будут загружены модели по умолчанию. И манифест, и модель извлекаются с помощью функции loadWeightMap. Кроме того, модели сегментированы для лучшего использования кеша браузера. В результате в браузер загружаются face_detection_model-shard1, face_detection_model-shard2 и face_detection_model-weights_manifest.json. Это также первое использование Tensor Flow API - tf.io.loadWeights - он используется для чтения данных моделей и заполнения параметров. Там также есть некоторые базовые проверки - мы можем заметить, что наши веса должны быть в тензоре определенного измерения.

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

И снова у нас есть тип объединения с поддержкой трех различных типов ввода. Первый из них - Tensor из библиотеки tf.Tensor, NetInput или TNetInput. В анализируемом примере используется TNetInput - поскольку мы используем ввод мультимедиа, в нашем случае изображение будет преобразовано в NetInput.

Здесь поддерживаются три типа элементов - HTMLImageElement, HTMLVideoElement и HTMLCanvasElement:

После загрузки они преобразуются в объект Canvas в конструкторе NetInput:

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

На дальнейших этапах необходимо обработать NetInput и преобразовать его в Tensor. Для этого используется функция getImageTensor:

На дальнейших этапах NetInput необходимо обработать и преобразовать в Tensor. Для этого используется функция getImageTensor.

Там мы можем найти больше вариантов использования TensorFlow.js - все выполнение оркестровано с помощью tf.tidy, который контролирует доступ к памяти и очищает промежуточные тензоры, созданные во время расчета окончательного.

Внутри этого замыкания, если наш ввод - тензор, выполняется некоторая проверка ранга тензора (его размерности). Как видим, разрешены только определенные ранги. Мы увидим дальше, почему.

В случае NetInput мы конвертируем его в Tensor с помощью tf.fromPixels. Наконец, мы объединяем тензор, созданный из холста, чтобы получить экземпляр tf.Tensor4D - Tensor с четырьмя измерениями - строками, столбцами, глубиной и глубиной2.

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

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

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

Теперь, когда у нас есть тензоры в определенной форме, мы готовы к реальной работе - сначала изменяется размер нашего тензора с использованием алгоритма билинейной интерполяции (поэтому нам нужен экземпляр Tensor4D в форме «квадрата»), чтобы получить не только конкретную форму, но и также размер. Наконец, мы можем обрабатывать его с помощью mobileNetV1 - очага в нашем приложении - уровня глубокого обучения, который специализируется на поиске функций на изображениях. После их получения вычисляется predictionLayer (в сложном процессе). В конечном итоге outputLayer создается из predictionLayer.

Данные рассчитываются - и если прогноз соответствует заданному порогу, они возвращаются пользователю. Теперь приступим к простым шагам - каждый объект FaceDetection содержит параметры, которые используются со стандартным Canvas API для рисования поверх изображения.

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

По сравнению с Saliens face-api.js был намного сложнее, и для него требовалось много копаться в документации Tensor Flow. Парень justadudewhohacks проделал большую работу, чтобы использовать этот API для получения впечатляющего конечного результата. Я уверен, что во многих различных проектах эта библиотека будет использоваться для получения впечатляющих результатов (у меня есть собственная идея объединить ее с веб-камерой в стиле снэпчата). Как я уже писал ранее, я многому научился, и хотя я не смог бы написать копию функциональности этого инструмента с нуля, он помог мне понять несколько увлекательных частей Tensor Flow.

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