- PVSM.RU - https://www.pvsm.ru -

Машинное обучение с Node.js при помощи библиотеки Tensorflow.js

Привет всем, коллеги!

Возможно, поклонники библиотеки 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].

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.js при помощи библиотеки Tensorflow.js - 1

Запустите скрипт, аргументами которого послужат файл модели и входное изображение.

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