- PVSM.RU - https://www.pvsm.ru -
Привет всем, коллеги!
Возможно, поклонники библиотеки Tensorflow, уже заметившие у нас в предзаказе эту книгу [1], также присматривались к возможностям машинного и глубокого обучения в браузере, тем более, что тему не обошел вниманием [2] и сам Франсуа Шолле [3]. Интересующихся приглашаем под кат, где рассказано, как при помощи библиотеки Tensorflow.js распознаются изображения.
TensorFlow.js [4] – новая версия популярной опенсорсной библиотеки, обогащающей язык JavaScript возможностями глубокого обучения. Теперь разработчики могут определять, обучать и запускать модели при помощи высокоуровневого библиотечного API [5].
Благодаря предобученным моделям [6] разработчики теперь могут с легкостью решать такие сложные задачи, как распознавание образов [7], генерация музыки [8] или определение человеческих поз [9] всего в нескольких строках JavaScript.
Tensorflow.js начиналась как фронтендовая библиотека для работы в браузере, но в этом году в нее была добавлена экспериментальная поддержка [10] Node.js. Таким образом, TensorFlow.js можно использовать и в бэкендовых приложениях на JavaScript, что совершенно избавляет нас от необходимости прибегать к Python.
Читая об этой библиотеке, я решил испробовать ее на простой задаче…
Использовать TensorFlow.js для визуального распознавания образов на изображениях при применении JavaScript из Node.js
К сожалению, документация [11] и примеры кода [12] в основном описывают использование этой библиотеки в браузере, Проектные утилиты [13], призванные упростить загрузку и использование предобученных моделей на момент написания статьи еще не поддерживали Node.js. Мне пришлось потратить немало времени, чтобы хорошенько прочитать исходники на Typescript для этой библиотеки.
Однако, через несколько дней долбежки я все-таки это сделал [14]! Ура!
Прежде чем перейти к подробному разбору кода, давайте поговорим о других реализациях библиотеки TensorFlow.
TensorFlow
TensorFlow [15] — это свободно распространяемая программная библиотека для приложений из области машинного обучения. TensorFlow можно применять для создания нейронных сетей и реализации других алгоритмов глубокого обучения.
Это библиотека, выпущенная Google в ноябре 2015, исходно была написана на Python [16]. Для обучения и оценки создаваемых моделей в ней применяются вычисления на CPU или GPU. Изначально эта библиотека создавалась для работы на высокопроизводительных серверах с использованием ресурсозатратных GPU.
Последние обновления позволили оптимизировать эту библиотеку и использовать в средах с более ограниченными ресурсами – например, на мобильных устройствах и в веб-браузерах.
TensorFlow Lite
Tensorflow Lite [17], облегченная версия этой библиотеки для мобильных устройств и встраиваемых систем, была выпущена в мае 2017 года. Вместе с ней предоставляется новый набор предобученных глубоких моделей для задач, связанных с распознаванием образов; эта коллекция именуется MobileNet [18]. Модели MobileNet были разработаны специально для эффективной работы в окружениях с ограниченным количеством ресурсов, например, на мобильных устройствах.
TensorFlow.js
Вслед за Tensorflow Lite в марте 2018 года была анонсирована [19] TensorFlow.js. Эта версия библиотеки предназначена для работы в браузере и базируется на более раннем проекте под названием deeplearn.js [20]. WebGL обеспечивает GPU-доступ к библиотеки. Разработчики используют API на JavaScript для обучения, загрузки и запуска моделей.
Позже TensorFlow.js была расширена для работы с Node.js, для этого применяется библиотечное дополнение [10] tfjs-node.
Импорт имеющихся моделей в TensorFlow.js
Готовые модели TensorFlow и Keras можно выполнять при помощи библиотеки TensorFlow.js. Перед выполнением модели необходимо перевести в новый формат при помощи этого инструмента [21]. Предобученные и преобразованные модели для классификации изображений, определения поз и обнаружения k-ближайших соседей доступны на Github [22].
Использование TensorFlow.js с Node.js
Установка библиотек TensorFlow
TensorFlow.js можно установить из реестра NPM [23].
@tensorflow/tfjs – библиотека Core TensorFlow.js [24]@tensorflow/tfjs-node — расширение TensorFlow.js Node.js [25]@tensorflow/tfjs-node-gpu – расширение TensorFlow.js Node.js с поддержкой вычислений на GPU [26]npm install @tensorflow/tfjs @tensorflow/tfjs-node
// или...
npm install @tensorflow/tfjs @tensorflow/tfjs-node-gpu
В обоих расширениях для Node.js используются нативные зависимости, которые будут компилироваться по запросу.
Загрузка библиотек TensorFlow
API на JavaScript [5] для Tensorflow предоставляется из core-библиотеки. В модулях-расширениях, обеспечивающих поддержку Node.js, дополнительные API не предоставляются.
const tf = require('@tensorflow/tfjs')
// Загружаем привязку (вычисления CPU)
require('@tensorflow/tfjs-node')
// Или загружаем привязку (вычисления GPU)
require('@tensorflow/tfjs-node-gpu')
Загрузка моделей TensorFlow
В TensorFlow.js предоставляется библиотека NPM [22] (tfjs-models), упрощающая загрузку предобученных и преобразованных моделей для классификации изображений [13], определения поз [27] и обнаружения k-ближайших соседей [28].
Модель MobileNet [13] для классификации изображений – это глубокая нейронная сеть, обученная различать 1000 различных классов изображений [29].
В файле README к проекту в качестве примера [30] приведен следующий код, используемый для загрузки модели.
import * as mobilenet from '@tensorflow-models/mobilenet';
// загрузить модель
const model = await mobilenet.load();
Одна из первых проблем, с которыми мне довелось столкнуться – оказывается, этот код не работает с Node.js.
Error: browserHTTPRequest is not supported outside the web browser.
Изучив исходный код [31], видим, что библиотека mobilenet – это обертка для класса tf.Model. При вызове метод load() автоматически загружает нужные файлы моделей, расположенные по внешнему HTTP-адресу, и инстанцирует модель TensorFlow.
Расширение Node.js на момент написания статьи еще не поддерживало HTTP-запросы для динамического извлечения моделей. Оставалось только вручную загружать модели в файловую систему.
Однако, вчитавшись в исходный код библиотеки, я нашел обходной путь…
Загрузка моделей из файловой системы
В случае, если класс MobileNet создается вручную, можно не вызывать метод load модуля, а перезаписать автоматически генерируемую переменную path, содержащую HTTP-адрес модели, заменив этот адрес на локальный путь в файловой системе. После этого при вызове метода load в экземпляре класса будет срабатывать класса загрузчика файловой системы [32]; в таком случае мы отказываемся от использования браузерного HTTP-загрузчика.
const path = "mobilenet/model.json"
const mn = new mobilenet.MobileNet(1, 1);
mn.path = `file://${path}`
await mn.load()
Круто, все работает!
Но откуда же берутся файлы моделей?
Модели MobileNet
Модели для TensorFlow.js состоят из файлов двух типов: файл конфигурации модели, хранимый в формате JSON, и веса моделей, хранимые в двоичном формате. Веса моделей зачастую фрагментируются на множество частей для оптимизации кэширования в браузерах.
Рассмотрев автоматический код загрузки [33] для моделей MobileNet, видим, что модели, их конфигурации и весовые фрагменты извлекаются из общедоступного контейнера по следующему адресу.
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v${version}_${alpha}_${size}/
Шаблонные параметры в URL описывают версии моделей, перечисленные здесь [34]. Результирующая точность классификации также выводится на той же странице.
В исходном коде указано, что только модели версии MobileNet v1 можно загружать при помощи библиотеки tensorflow-models/mobilenet.
Код извлечения по HTTP загружает файл model.json из места хранения, а затем рекурсивно выбирает все фрагменты моделей с весовыми коэффициентами, на которые стоят ссылки. Это файлы в формате groupX-shard1of1.
Скачивание моделей вручную
Если нужно сохранить все файлы моделей в файловой системе, то можно поступить так: извлечь конфигурационный файл модели, разобрать синтаксис всех весовых файлов, на которые стоят ссылки в конфигурационном файле, после чего скачать каждый весовой файл вручную.
Я собирался использовать модуль MobileNet V1 с альфа-значением 1.0 и изображение размером 224 пиксела. Так я получаю следующий URL [35] для конфигурационного файла модели.
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/model.json
Как только этот файл будет скачан локально, можно воспользоваться инструментом [36] jq для синтаксического разбора имен всех весовых файлов.
$ cat model.json | jq -r ".weightsManifest[].paths[0]"
group1-shard1of1
group2-shard1of1
group3-shard1of1
...
При помощи инструмента sed можно поставить перед именем каждого элемента HTTP URL, чтобы сгенерировать URL для каждого весового файла.
$ cat model.json | jq -r ".weightsManifest[].paths[0]" | sed 's/^/https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224//'
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group1-shard1of1
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group2-shard1of1
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group3-shard1of1
...
Команды parallel и curl позволяют затем скачать все эти файлы в мой локальный каталог.
cat model.json | jq -r ".weightsManifest[].paths[0]" | sed 's/^/https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224//' | parallel curl -O
Классификация изображений
Этот пример кода [30], предоставляемый с TensorFlow.js, демонстрирует, как вернуть результат классификации изображения.
const img = document.getElementById('img');
// Классифицируем изображение
const predictions = await model.classify(img);
Это не работает в Node.js из-за отсутствия поддержки DOM.
Метод [37] classify принимает разнообразные элементы DOM (canvas, video, image) и автоматически извлекает и преобразует «картиночные» байты из этих элементов в класс tf.Tensor3D, используемый в качестве ввода модели. В качестве альтернативы входную информацию tf.Tensor3D можно передавать напрямую.
Я решил не пытаться использовать внешний пакет для имитации DOM-элемента вручную, а обнаружил, что tf.Tensor3D проще собрать вручную.
Генерируем Tensor3D из изображения
Читая исходный код [38] метода, применяемого для преобразования элементов DOM в классы Tensor3D, находим, что для генерации класса Tensor3D используются следующие входные параметры.
const values = new Int32Array(image.height * image.width * numChannels);
// заполняем пикселы информацией пиксельных каналов, взятой с картинки
const outShape = [image.height, image.width, numChannels];
const input = tf.tensor3d(values, outShape, 'int32');
pixels – это двухмерный массив типа (Int32Array), содержащий последовательный список канальных значений для каждого пиксела. numChannels – это количество канальных значений на пиксел.
Создание входных значений для JPEG
Библиотека [39] jpeg-js – это JPEG-кодировщик/декодировщик для Node.js, написанный на чистом JavaScript. При помощи этой библиотеки можно извлечь RGB-значения для каждого пиксела.
const pixels = jpeg.decode(buffer, true);
В результате получим Uint8Array с четырьмя канальными значениями (RGBA) на каждый пиксел (width * height). В модели MobileNet для классификации используется всего три цветовых канала (RGB), альфа-канал игнорируется. Этот код преобразует четырехканальный массив в верную трехканальную версию.
const numChannels = 3;
const numPixels = image.width * image.height;
const values = new Int32Array(numPixels * numChannels);
for (let i = 0; i < numPixels; i++) {
for (let channel = 0; channel < numChannels; ++channel) {
values[i * numChannels + channel] = pixels[i * 4 + channel];
}
}
Требования ко входным значениям для моделей MobileNet
Используемая здесь модель MobileNet [40] классифицирует изображения высотой и шириной по 224 пиксела. Входные тензоры должны содержать значения с плавающей точкой в диапазоне от -1 до 1 для каждого из трех канальных значений каждого пиксела.
Входные значения для изображений с другой размерностью перед классификацией нужно пересчитать в правильный размер. Кроме того, пиксельные значения, получаемые от JPEG-декодера, находятся в диапазоне 0 — 255, а не -1 — 1. Эти значения также необходимо преобразовать перед классификацией.
В TensorFlow.js есть библиотечные методы, упрощающие этот процесс, но, что еще лучше, есть специальная библиотека tfjs-models/mobilenet, автоматически решающая эту проблему [41]!
Разработчик может передавать входные Tensor3D типа int32, а также различные размерности методу classify, который перед классификацией переводит входные значения в правильный формат. То есть, нам здесь ничего делать не приходится. Супер!
Получение прогнозов
Модели MobileNet в Tensorflow обучаются распознаванию объектов из 1000 важнейших классов [29] из множества данных ImageNet [42]. На выходе модели дают вероятностные значения, характеризующие, каковы шансы найти данные объекты на классифицируемом изображении.
Полный список обученных классов для используемой модели находится в этом файле [29].
Библиотека tfjs-models/mobilenet предлагает метод classify в классе MobileNet, возвращающий топ-X наиболее вероятных классов, исходя из того, что изображено на картинке.
const predictions = await mn_model.classify(input, 10);
predictions – это массив из X классов и вероятностей в следующем формате.
{
className: 'panda',
probability: 0.9993536472320557
}
Пример
Итак, мы разобрались, как использовать библиотеку TensorFlow.js и модели MobileNet в Node.js, а теперь рассмотрим, как этот скрипт классифицирует изображение, заданное в качестве аргумента командной строки.
Исходный код
Сохраните этот файл скрипта и дескриптор пакета в локальных файлах.
{
"name": "tf-js",
"version": "1.0.0",
"main": "script.js",
"license": "MIT",
"dependencies": {
"@tensorflow-models/mobilenet": "^0.2.2",
"@tensorflow/tfjs": "^0.12.3",
"@tensorflow/tfjs-node": "^0.1.9",
"jpeg-js": "^0.3.4"
}
}
const tf = require('@tensorflow/tfjs')
const mobilenet = require('@tensorflow-models/mobilenet');
require('@tensorflow/tfjs-node')
const fs = require('fs');
const jpeg = require('jpeg-js');
const NUMBER_OF_CHANNELS = 3
const readImage = path => {
const buf = fs.readFileSync(path)
const pixels = jpeg.decode(buf, true)
return pixels
}
const imageByteArray = (image, numChannels) => {
const pixels = image.data
const numPixels = image.width * image.height;
const values = new Int32Array(numPixels * numChannels);
for (let i = 0; i < numPixels; i++) {
for (let channel = 0; channel < numChannels; ++channel) {
values[i * numChannels + channel] = pixels[i * 4 + channel];
}
}
return values
}
const imageToInput = (image, numChannels) => {
const values = imageByteArray(image, numChannels)
const outShape = [image.height, image.width, numChannels];
const input = tf.tensor3d(values, outShape, 'int32');
return input
}
const loadModel = async path => {
const mn = new mobilenet.MobileNet(1, 1);
mn.path = `file://${path}`
await mn.load()
return mn
}
const classify = async (model, path) => {
const image = readImage(path)
const input = imageToInput(image, NUMBER_OF_CHANNELS)
const mn_model = await loadModel(model)
const predictions = await mn_model.classify(input)
console.log('classification results:', predictions)
}
if (process.argv.length !== 4) throw new Error('incorrect arguments: node script.js <MODEL> <IMAGE_FILE>')
classify(process.argv[2], process.argv[3])
Тестирование
Скачайте файлы модели в каталог mobilenet, следуя вышеизложенным инструкциям.
Установите зависимости проекта при помощи NPM
npm install
Скачайте образец JPEG-файла для классификации
wget http://bit.ly/2JYSal9 -O panda.jpg

Запустите скрипт, аргументами которого послужат файл модели и входное изображение.
node script.js mobilenet/model.json panda.jpg
Если все сработало верно, то в консоли должен появиться следующий вывод.
classification results: [ {
className: 'giant panda, panda, panda bear, coon bear',
probability: 0.9993536472320557
} ]
Изображение верно классифицировано как содержащее панду с вероятностью 99.93%!
Заключение
Библиотека TensorFlow.js открывает перед JavaScript-разработчиками возможности глубокого обучения. Использование предобученных моделей с библиотекой TensorFlow.js позволяет без труда надстраивать в JavaScript-приложениях новые возможности для решения сложных задач машинного обучения, обходясь минимальными усилиями и лаконичным кодом.
Библиотека TensorFlow.js создавалась сугубо для работы в браузере, но сейчас уже взаимодействует и с Node.js, хотя, не все инструменты и утилиты поддерживают эту новую среду исполнения. Повозившись с библиотекой несколько дней, я научился использовать ее с моделями MobileNet для визуального распознавания изображений из локального файла.
Автор: ph_piter
Источник [43]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/302006
Ссылки в тексте:
[1] эту книгу: https://www.piter.com/product_by_id/124311707
[2] не обошел вниманием: https://www.manning.com/books/deep-learning-with-javascript
[3] Франсуа Шолле: https://www.piter.com/collection/all/product/glubokoe-obuchenie-na-python
[4] TensorFlow.js: https://js.tensorflow.org
[5] высокоуровневого библиотечного API: https://js.tensorflow.org/api/0.12.0/
[6] предобученным моделям: https://github.com/tensorflow/tfjs-models/
[7] распознавание образов: https://emojiscavengerhunt.withgoogle.com
[8] генерация музыки: https://magenta.tensorflow.org/demos/performance_rnn/index.html#2%7C2,0,1,0,1,1,0,1,0,1,0,1%7C1,1,1,1,1,1,1,1,1,1,1,1%7C1,1,1,1,1,1,1,1,1,1,1,1%7Cfalse
[9] определение человеческих поз: https://storage.googleapis.com/tfjs-models/demos/posenet/camera.html
[10] экспериментальная поддержка: https://github.com/tensorflow/tfjs-node
[11] документация: https://js.tensorflow.org/#getting-started
[12] примеры кода: https://js.tensorflow.org/tutorials/webcam-transfer-learning.html
[13] Проектные утилиты: https://github.com/tensorflow/tfjs-models/tree/master/mobilenet
[14] это сделал: https://gist.github.com/jthomas/145610bdeda2638d94fab9a397eb1f1d
[15] TensorFlow: https://www.tensorflow.org
[16] написана на Python: https://www.tensorflow.org/api_docs/python/
[17] Tensorflow Lite: https://www.tensorflow.org/lite/
[18] MobileNet: https://ai.googleblog.com/2017/06/mobilenets-open-source-models-for.html
[19] анонсирована: https://medium.com/tensorflow/introducing-tensorflow-js-machine-learning-in-javascript-bf3eab376db
[20] deeplearn.js: https://twitter.com/deeplearnjs
[21] этого инструмента: https://github.com/tensorflow/tfjs-converter
[22] доступны на Github: https://github.com/tensorflow/tfjs-models
[23] реестра NPM: https://www.npmjs.com
[24] Core TensorFlow.js: https://www.npmjs.com/package/@tensorflow/tfjs
[25] TensorFlow.js Node.js: https://www.npmjs.com/package/@tensorflow/tfjs-node
[26] TensorFlow.js Node.js с поддержкой вычислений на GPU: https://www.npmjs.com/package/@tensorflow/tfjs-node-gpu
[27] определения поз: https://github.com/tensorflow/tfjs-models/tree/master/posenet
[28] обнаружения k-ближайших соседей: https://github.com/tensorflow/tfjs-models/tree/master/knn-classifier
[29] 1000 различных классов изображений: https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/imagenet_classes.ts
[30] в качестве примера: https://github.com/tensorflow/tfjs-models/tree/master/mobilenet#via-npm
[31] исходный код: https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/index.ts#L27
[32] загрузчика файловой системы: https://js.tensorflow.org/tutorials/model-save-load.html
[33] автоматический код загрузки: https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/index.ts#L68-L76
[34] здесь: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md#pre-trained-models
[35] следующий URL: https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/model.json
[36] инструментом: https://stedolan.github.io/jq/
[37] Метод: https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/index.ts#L143-L155
[38] исходный код: https://github.com/tensorflow/tfjs-core/blob/master/src/kernels/backend_cpu.ts#L126-L140
[39] Библиотека: https://www.npmjs.com/package/jpeg-js
[40] модель MobileNet: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md#mobilenet_v1
[41] автоматически решающая эту проблему: https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/index.ts#L103-L114
[42] ImageNet: http://image-net.org
[43] Источник: https://habr.com/post/432984/?utm_campaign=432984
Нажмите здесь для печати.