Платформа и среда

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

В браузере TensorFlow.js поддерживает как мобильные, так и настольные устройства. Каждое устройство имеет определенный набор ограничений, например доступные API WebGL, которые автоматически определяются и настраиваются для вас.

В Node.js TensorFlow.js поддерживает привязку непосредственно к API TensorFlow или работу с более медленными реализациями ванильного процессора.

Окружающая среда

Когда программа TensorFlow.js выполняется, конкретная конфигурация называется средой. Среда состоит из единого глобального бэкэнда, а также набора флагов, которые управляют детальными функциями TensorFlow.js.

Серверные части

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

Чтобы узнать, какой бэкэнд вы используете:

console.log(tf.getBackend());

Если вы хотите вручную изменить серверную часть:

tf.setBackend('cpu');
console.log(tf.getBackend());

Серверная часть WebGL

Серверная часть WebGL, «webgl», на данный момент является самой мощной внутренней частью браузера. Этот бэкэнд работает до 100 раз быстрее, чем ванильный процессор. Тензоры хранятся в виде текстур WebGL, а математические операции реализуются в шейдерах WebGL. Вот несколько полезных вещей, которые следует знать при использовании этого бэкэнда: \

Избегайте блокировки потока пользовательского интерфейса

Когда вызывается операция, например tf.matMul(a, b), результирующий tf.Tensor возвращается синхронно, однако вычисление умножения матрицы может быть еще не готово. Это означает, что возвращаемый tf.Tensor — это всего лишь дескриптор вычисления. Когда вы вызываете x.data() или x.array() , значения будут разрешены, когда вычисление фактически завершится. Поэтому важно использовать асинхронные методы x.data() и x.array() вместо их синхронных аналогов x.dataSync() и x.arraySync() , чтобы избежать блокировки потока пользовательского интерфейса во время завершения вычислений.

Управление памятью

Одним из предостережений при использовании серверной части WebGL является необходимость явного управления памятью. WebGLTextures, где в конечном итоге хранятся данные Tensor, не собираются автоматически браузером как мусор.

Чтобы уничтожить память tf.Tensor , вы можете использовать метод dispose() :

const a = tf.tensor([[1, 2], [3, 4]]);
a.dispose();

Очень часто в приложении объединяют несколько операций в одну цепочку. Сохранение ссылки на все промежуточные переменные для их удаления может снизить читаемость кода. Чтобы решить эту проблему, TensorFlow.js предоставляет метод tf.tidy() , который очищает все tf.Tensor , которые не возвращаются функцией после ее выполнения, аналогично тому, как очищаются локальные переменные при выполнении функции:

const a = tf.tensor([[1, 2], [3, 4]]);
const y = tf.tidy(() => {
  const result = a.square().log().neg();
  return result;
});
Точность

На мобильных устройствах WebGL может поддерживать только 16-битные текстуры с плавающей запятой. Однако большинство моделей машинного обучения обучаются с использованием 32-битных весов и активаций с плавающей запятой. Это может вызвать проблемы с точностью при переносе модели на мобильное устройство, поскольку 16-битные числа с плавающей запятой могут представлять только числа в диапазоне [0.000000059605, 65504] . Это означает, что вам следует следить за тем, чтобы веса и активации в вашей модели не превышали этот диапазон. Чтобы проверить, поддерживает ли устройство 32-битные текстуры, проверьте значение tf.ENV.getBool('WEBGL_RENDER_FLOAT32_CAPABLE') . Если оно неверно, то устройство поддерживает только 16-битные текстуры с плавающей запятой. Вы можете использовать tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED') , чтобы проверить, использует ли TensorFlow.js в настоящее время 32-битные текстуры.

Компиляция шейдеров и загрузка текстур

TensorFlow.js выполняет операции на графическом процессоре, запуская шейдерные программы WebGL. Эти шейдеры собираются и компилируются лениво, когда пользователь запрашивает выполнить операцию. Компиляция шейдера происходит на ЦП в основном потоке и может быть медленной. TensorFlow.js автоматически кэширует скомпилированные шейдеры, что значительно ускоряет второй вызов той же операции с входными и выходными тензорами одной и той же формы. Обычно приложения TensorFlow.js используют одни и те же операции несколько раз за время существования приложения, поэтому второй проход через модель машинного обучения происходит намного быстрее.

TensorFlow.js также хранит данные tf.Tensor как WebGLTextures. Когда создается tf.Tensor , мы не загружаем данные сразу в графический процессор, а храним данные в процессоре до тех пор, пока tf.Tensor не будет использован в операции. Если tf.Tensor используется второй раз, данные уже находятся в графическом процессоре, поэтому затраты на загрузку не требуются. В типичной модели машинного обучения это означает, что веса загружаются во время первого прогноза через модель, а второй проход через модель будет намного быстрее.

Если вас волнует производительность первого прогноза с помощью вашей модели или кода TensorFlow.js, мы рекомендуем прогреть модель, передав входной тензор той же формы, прежде чем использовать реальные данные.

Например:

const model = await tf.loadLayersModel(modelUrl);

// Warmup the model before using real data.
const warmupResult = model.predict(tf.zeros(inputShape));
warmupResult.dataSync();
warmupResult.dispose();

// The second predict() will be much faster
const result = model.predict(userData);

Серверная часть Node.js TensorFlow

В бэкэнде TensorFlow Node.js, «узле», API TensorFlow C используется для ускорения операций. При этом будет использоваться доступное аппаратное ускорение компьютера, например CUDA, если оно доступно.

В этом бэкэнде, как и в бэкэнде WebGL, операции возвращают tf.Tensor синхронно. Однако, в отличие от бэкэнда WebGL, операция завершается до того, как вы получите обратно тензор. Это означает, что вызов tf.matMul(a, b) заблокирует поток пользовательского интерфейса.

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

Дополнительную информацию о Node.js см. в этом руководстве.

Серверная часть WASM

TensorFlow.js предоставляет бэкэнд WebAssembly ( wasm ), который обеспечивает ускорение ЦП и может использоваться в качестве альтернативы ванильному процессору JavaScript ( cpu ) и бэкэнду с ускорением WebGL ( webgl ). Чтобы использовать его:

// Set the backend to WASM and wait for the module to be ready.
tf.setBackend('wasm');
tf.ready().then(() => {...});

Если ваш сервер обслуживает файл .wasm по другому пути или с другим именем, используйте setWasmPath перед инициализацией серверной части. Дополнительную информацию см. в разделе «Использование бандлеров» в README:

import {setWasmPath} from '@tensorflow/tfjs-backend-wasm';
setWasmPath(yourCustomPath);
tf.setBackend('wasm');
tf.ready().then(() => {...});
Почему ВАСМ?

WASM был представлен в 2015 году как новый двоичный веб-формат, предоставляющий программам, написанным на JavaScript, C, C++ и т. д., цель компиляции для запуска в Интернете. WASM поддерживается Chrome, Safari, Firefox и Edge с 2017 года и поддерживается 90% устройств по всему миру.

Производительность

Серверная часть WASM использует библиотеку XNNPACK для оптимизации реализации операторов нейронных сетей.

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

По сравнению с WebGL : WebGL быстрее, чем WASM для большинства моделей, но для крошечных моделей WASM может превзойти WebGL из-за фиксированных накладных расходов на выполнение шейдеров WebGL. В разделе «Когда мне следует использовать WASM» ниже обсуждаются эвристики для принятия этого решения.

Портативность и стабильность

WASM имеет портативную 32-битную арифметику с плавающей запятой, обеспечивающую точную четность на всех устройствах. WebGL, с другой стороны, зависит от аппаратного обеспечения, и разные устройства могут иметь разную точность (например, возврат к 16-битным числам с плавающей запятой на устройствах iOS).

Как и WebGL, WASM официально поддерживается всеми основными браузерами. В отличие от WebGL, WASM может работать в Node.js и использоваться на стороне сервера без необходимости компилировать собственные библиотеки.

Когда мне следует использовать WASM?

Размер модели и потребность в вычислениях

В общем, WASM — хороший выбор, когда модели меньшего размера или вам нужны устройства более низкого уровня, в которых отсутствует поддержка WebGL (расширение OES_texture_float ) или которые имеют менее мощные графические процессоры. На диаграмме ниже показано время вывода (на момент TensorFlow.js 1.5.2) в Chrome на MacBook Pro 2018 года для 5 наших официально поддерживаемых моделей в WebGL, WASM и серверных модулях ЦП:

Меньшие модели

Модель ВебГЛ ВАСМ Процессор Память
BlazeFace 22,5 мс 15,6 мс 315,2 мс 0,4 МБ
FaceMesh 19,3 мс 19,2 мс 335 мс 2,8 МБ

Большие модели

Модель ВебГЛ ВАСМ Процессор Память
ПосеНет 42,5 мс 173,9 мс 1514,7 мс 4,5 МБ
БодиПикс 77 мс 188,4 мс 2683 мс 4,6 МБ
МобилНет v2 37 мс 94 мс 923,6 мс 13 МБ

В приведенной выше таблице показано, что WASM в 10–30 раз быстрее, чем простой серверный процессор JS для разных моделей, и конкурирует с WebGL для меньших моделей, таких как BlazeFace , который легкий (400 КБ), но имеет приличное количество операций (~ 140). Учитывая, что программы WebGL имеют фиксированные накладные расходы на выполнение операции, это объясняет, почему такие модели, как BlazeFace, работают быстрее на WASM.

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

Вывод против обучения

Чтобы решить основной вариант использования предварительно обученных моделей, при разработке серверной части WASM приоритет отдается выводам , а не поддержке обучения . Просмотрите актуальный список поддерживаемых операций в WASM и сообщите нам, если в вашей модели есть неподдерживаемая операция. Для обучения моделей мы рекомендуем использовать серверную часть Node (TensorFlow C++) или серверную часть WebGL.

Серверная часть процессора

Серверная часть ЦП, «cpu», является наименее производительной, однако она и самая простая. Все операции реализованы на стандартном JavaScript, что делает их менее распараллеливаемыми. Они также блокируют поток пользовательского интерфейса.

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

Флаги

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

  • tf.enableProdMode(): включает производственный режим, в котором будут удалены проверка модели, проверки NaN и другие проверки правильности в пользу производительности.
  • tf.enableDebugMode() : включает режим отладки, в котором на консоль будет записываться каждая выполняемая операция, а также информация о производительности во время выполнения, такая как объем памяти и общее время выполнения ядра. Обратите внимание, что это сильно замедлит работу вашего приложения, не используйте это в производстве.