Машинное обучение с TensorFlow.js

За последние пару лет мы стали свидетелями появления ряда библиотек машинного обучения в сообществе JavaScript, позволяющих интерфейсным разработчикам переносить ИИ в Интернет и решать сложные проблемы с помощью простых в использовании API.

TensorFlow - одна из самых популярных платформ с открытым исходным кодом для машинного обучения, в основном используемая с Python. В 2018 году Google анонсировал первую версию TensorFlow для JavaScript под названием TensorFlow.js. Это то, что мы собираемся изучить в этом уроке.

Мы собираемся построить классификатор изображений. В конце урока наш классификатор сможет различать образы Санты и обычного старика. Но для начала нам сначала нужно понять, что такое нейронные сети.

Как работают нейронные сети

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

Нейронные сети

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

Модель

Когда эти алгоритмы машинного обучения обучаются на данных, на выходе мы получаем модель. Это представление тренировочного процесса. Модели можно использовать для будущих прогнозов.

Характеристики и этикетки

Данные, которые вы вводите в модель, обычно состоят из элементов и меток. Функции - это атрибуты, связанные с каждым образцом в вашем наборе данных. Классическим примером является классификация цветов ириса на основе таких характеристик, как ширина чашелистиков и лепестков.

Этикетки показывают, как бы вы классифицировали каждый образец. Оставаясь с примером, на основе функций вы относите вход к одному из бесплатных видов. Либо он получает этикетку «Ирис сетоса», «Ирис вирджиника» или «Ирис разноцветный».

Теперь, когда у нас есть все необходимое, давайте посмотрим, что у нас будет в итоге.

Окончательный результат этого руководства

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

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

Вы можете перетащить изображения на элемент холста и обучить классификатор, нажав одну из кнопок ниже. Когда у вас будет достаточно данных, вы можете перетащить новое изображение и предсказать, что на нем будет.

Получение зависимостей

Наш самый первый шаг - втянуть все, что нам нужно. Мы собираемся использовать TensorFlow.js и популярную предварительно обученную модель под названием MobileNet для классификации изображений. Затем мы воспользуемся техникой, называемой трансферным обучением, где мы расширим предварительно обученную модель нашим собственным настраиваемым обучающим набором. Для этого нам понадобится классификатор. Мы будем использовать модуль K-ближайшего соседа. Это позволит нам категоризировать изображения, и как только мы воспользуемся прогнозированием, он выберет категорию, наиболее подходящую для изображения.

Это означает, что у нас будет 3 зависимости: TensorFlow, MobileNet и модуль классификатора KNN. Вот весь документ, с которым мы будем работать:

Перед закрытием тела у нас будет два разных скрипта. Один для классификатора и один для обработки событий пользовательского интерфейса.

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

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

Создание UI

Создайте новый файл с именем ui.js. Мы собираемся заставить холст принимать изображения путем перетаскивания. Сначала получите холст и его контекст и прикрепите к нему несколько слушателей событий:

Нам нужно обработать два события: dragover и _3 _._ 4_ будут рисовать изображение на холсте, а dragover только предотвращает выполнение действия по умолчанию. Это необходимо, потому что без него изображение открывалось бы на той же вкладке.

Посмотрим, что находится внутри функции onDrop.

Самое первое - предотвратить действие по умолчанию, как мы это сделали для dragover. Затем мы хотим получить данные из файла, который мы поместили на холст. Мы можем получить это с помощью e.dataTransfer.files[0]. Затем мы хотим создать новый объект FileReader и прочитать файл как URL-адрес данных. Мы также хотим создать функцию для его события onload. Когда reader завершит чтение файла, мы создаем новый объект Image и устанавливаем его источник на file.target.result. Это будет содержать данные изображения в кодировке base64. И как только изображение создано, мы рисуем его на холсте. Помните, нам нужно масштабировать его до 224x244.

Пока мы также здесь, давайте быстро добавим прослушиватели событий к кнопкам, а затем мы сможем приступить к созданию классификатора. У нас есть 3 кнопки и 3 слушателя событий:

У нас будет addExample функция, которая будет принимать один параметр: метку для категории. И у нас будет функция predict.

Построение классификатора

Мы создадим обе функции в новом файле с именем classifier.js. Но сначала нам нужно создать классификатор и загрузить его в MobileNet. Для этого добавьте в classifier.js следующую функцию и вызовите ее:

И классификаторы, и модель будут использоваться позже. Поэтому я создал для них две переменные вне функции. Для загрузки модели требуется некоторое время, и она выполняется асинхронно, поэтому мы используем async / await. После загрузки мы можем скрыть сообщение о загрузке.

Чтобы расширить модель нашим собственным набором данных, мы должны добавить наши собственные примеры. Для этого каждый раз, когда пользователь нажимает на «Санта» или «Пожилой», мы вызываем функцию addExample.

Добавление примеров

Функция будет делать следующее:

  • Получить данные изображения с холста
  • использовать MobileNet, чтобы получить возможности извлеченных данных
  • Обучите классификатор, добавив к нему пример, используя функции и соответствующую метку.

Сначала мы получаем значения пикселей с холста, используя ft.browser.fromPixels. Затем мы получаем функции, используя infer метод MobileNet. После этого, чтобы добавить примеры, мы можем просто вызвать addExample классификатора с функцией и меткой.

Затем нам нужно обновить элементы DOM. Мы очищаем холст, чтобы можно было добавить новый пример. Нам также необходимо обновить кнопки, чтобы сообщить пользователю, сколько примеров было добавлено в каждый набор. Для этого я создал новый массив, который считает количество примеров.

Наконец, мы можем вызвать dispose на image, чтобы освободить память.

Прогнозирование

Как только у нас появятся необходимые данные, мы можем протестировать модель. При нажатии на «Прогнозировать» будет вызвана функция predict, которая очень похожа на addExample:

Самое первое - проверить, есть ли у нас примеры в нашем наборе данных. Если мы не добавили никаких примеров, он ни за что не предскажет. Это то, что classifier.getNumClasses проверяет.

Как и в случае с addExample, нам нужны функции изображения, которые мы можем получить таким же образом. Затем нам нужно вызвать classifier.predictClass передачу функций, чтобы получить прогноз изображения. Получив результат, мы очищаем холст, распечатываем прогноз и удаляем объект image.

Резюме

И теперь наш классификатор изображений работает как положено. После обучения модели с парой изображений для обоих наборов мы можем с уверенностью предсказать следующее изображение.

Чтобы получить полный исходный код и немного поработать с проектом, посетите codeandbox.io. Вы также можете клонировать его с GitHub. Это мой последний урок в этом году, но я вернусь к новым темам в следующем десятилетии. Спасибо, что прочитали!

В заключение хочу поздравить вас с Рождеством и Новым годом! 🎄 🎉