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

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

Примечание. Исходный код следующей демонстрации будет доступен в этом репозитории GitHub.

Запуск нейронной сети в браузере

Как и все в Интернете, есть много вариантов поиска библиотеки, которая поможет вам обучить и запустить вашу нейронную сеть в браузере.

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

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

Использование Brain.js

Brain.js можно запустить в Node.js или в веб-браузере. Сети, обученные в Node, можно сохранять и загружать в браузер и наоборот. Инструкции по использованию Brain.js можно найти в репозитории GitHub.

Простой пример из репозитория GitHub создает функцию XOR. Исторически сложилось так, что ранние нейронные сети на основе персептрона не могли объяснить нелинейность функции XOR. И поскольку обучающий набор для этой функции довольно мал (всего 4 возможности), это очень хороший (если коротко) пример для настройки простой нейронной сети.

Вот пример XOR со страницы Brain.js:

const config = {
    binaryThresh: 0.5,
    hiddenLayers: [3],
    activation: ‘sigmoid’
};
const net = new brain.NeuralNetwork(config);
net.train([{input: [0, 0], output: [0]},
    {input: [0, 1], output: [1]},
    {input: [1, 0], output: [1]},
    {input: [1, 1], output: [0]}]);
const output = net.run([1, 0]); // [0.987];

Давайте посмотрим на это по частям. Начнем с объекта конфигурации.

Объект конфигурации имеет три свойства:

1. binaryThresh — (двоичный порог) значение от 0 до 1, определяющее точку, в которой отдельный нейрон в сети переключится с выключенного состояния на включенное. При использовании значения 0,5, когда значение нейрона ниже 50%, оно будет считаться выключенным. Когда значение превышает 50%, оно будет считаться включенным. Этот двоичный порог можно настроить при настройке сети. Регулировка этого значения аналогична регулировке громкости. Включение этого параметра вверх или вниз заставит вашу сеть принимать решения с большей или меньшей энергией. Начальное значение 0,5 является наиболее разумным до тех пор, пока у вас не будет причин для корректировки.

2. hiddenLayers — это массив целых чисел, описывающих размер скрытых слоев внутри сети. Например, если вы хотите иметь три скрытых слоя с 25 нейронами в каждом слое, ваш массив скрытых слоев будет выглядеть как [25, 25, 25]. В этом примере автор использует только один скрытый слой с 3 нейронами в нем, поэтому их массив содержит только одно число: [3].

3. активация — функция, описывающая переход нейрона из выключенного состояния во включенное. Сигмоида — это математическая функция, имеющая гладкую кривую в форме буквы S. Мы используем эту форму, чтобы помочь нейронной сети обобщить и исследовать проблемное пространство. Имея S-образную кривую, а не квадратное двоичное значение включения и выключения, сеть сможет использовать все десятичные значения от 1 до 0. Если бы мы использовали двоичный тип активации, все нейронные значения будет только 1 или 0. Между ними не будет никакого промежуточного значения.



После объекта конфигурации в примере создается новый экземпляр brain.NeuralNetwork и передается этот объект конфигурации в конструктор.

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

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

Когда сеть запущена и при вводе [1, 0] ожидаемый результат должен быть близок к 1. Фактическое значение, выходящее из сети, будет ближе к 0,987 (близко к 1).

Обрамление проблемного пространства

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

Если мы изменим обучающие данные, мы изменим всю функцию сети.

Этот простой пример функции XOR в Brain.js на самом деле представляет собой ту же самую архитектуру, которая нам нужна для создания системы рекомендаций по продуктам. Единственное, что характерно для XOR в этом примере, — это обучающие данные. Если мы изменим обучающие данные, мы изменим всю функцию сети.

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

Преобразование данных

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

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

Например, если у нас есть список товаров из 10 товаров, а в корзине есть товары с индексами 3 и 7, входной набор для нашей нейронной сети будет выглядеть так:

[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]

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

[0.012, 0.014, 0.012, 0.001, 0.9834, 0.011, 0.732, 0.001, 0.32, 0.243]

Этот вывод будет означать рекомендацию 98% для элемента с индексом 4 и рекомендацию 73% для элемента с индексом 6.

Обучение сети

Чтобы создать обучающий набор, я беру список примерно из 100 000 заказов, удаляя заказы, в которых в корзине был только один товар, у нас остается около 70 000 заказов, содержащих несколько товаров.

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

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

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

Нейронные сети с прямой связью

То, что у нас есть, называется нейронная сеть с прямой связью. В основном это означает, что когда данные перемещаются по сети, они никогда не возвращаются в систему. Это простой односторонний процесс, показанный в этой нисходящей форме:



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

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

Тюнинг

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

Вы хотите попытаться выполнить задачу с минимальным количеством скрытых слоев и нейронов на слое. Единственный способ найти правильный баланс — это обучить и запустить сеть несколько раз с разными настройками.

На верхнем уровне у меня было три скрытых слоя, состоящих примерно из 100 нейронов в каждом слое. Эта конфигурация дала отличные результаты по выходным значениям, но данные, необходимые для создания этой сети в браузере, превышали 50 мегабайт. Это слишком много для нашего простого небольшого рекомендательного приложения.

Я сократил систему до одного скрытого слоя всего с 3 нейронами в этом слое. Это привело бы к тому, что наши сетевые данные стали бы меньше 5 килобайт. Много маленьких! Но на этом уровне вы можете легко увидеть, что сеть не «играет с полной колодой». Он дает слишком общие рекомендации и не очень хорошо реагирует на различные входные данные. Этого и следовало ожидать, когда в сети буквально недостаточно нейронов, чтобы составить целостное мнение о входных данных.

Поэкспериментировав с этими значениями, мы пришли к выводу, что оптимальная область для нашей рекомендательной системы должна состоять из 2 скрытых слоев: первый с 15 нейронами и второй слой только с 10 нейронами. Это приведет нас к диапазону 25 килобайт, который будет нормально работать в браузере.

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

Сохранение обученной сети

После обучения вы можете получить объект JSON, представляющий вашу обученную сеть, вызвав метод toJSON() сети Brain.js:

var json = net.toJSON();

Затем вы можете сохранить эту строку JSON в файл, который вы загрузите в браузер.

Чтобы загрузить данные JSON обратно в новый экземпляр Brain.js, вы можете сделать следующее:

var brain = require(‘brain.js’);
var net = new brain.NeuralNetwork();
net.fromJSON(json);

Известные проблемы

У меня была одна небольшая проблема при попытке загрузить данные в браузер. Если в вашем веб-приложении есть другой код, который внес какие-либо изменения в Array.prototype, у вас могут возникнуть проблемы с загрузкой вашей модели.

По неизвестной причине Brain.js будет выдавать ошибки, если Array.prototype не является девственным. Итак, не связывайтесь с этим. Если у вас есть функции-прототипы, вам, возможно, придется пойти другим путем, если ваши модели отказываются загружаться.

Вывод в функцию

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

net.toFunction();

Хотя это прекрасно в теории и интересно посмотреть на практике, вывод этого файла может потребовать ОЧЕНЬ интенсивного использования памяти. В более крупных конфигурациях скрытых слоев я не мог вывести эту функцию, пока не закончилась системная память в Node.js.

Кроме того, результирующая функция ОГРОМНА. В то время как наше обученное состояние сети составляет около 25 килобайт, выходные данные функции создают файл размером в несколько мегабайт.

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

Вывод

Использование Brain.js для этой системы рекомендаций на практике сработало очень хорошо. Мы использовали именно эту систему для создания системы рекомендаций по продуктам для крупной компании по производству пищевых добавок.

Вы можете увидеть простую демонстрацию этой системы рекомендаций на http://preese.com/recommendation-demo

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

Запустив сеть в браузере, мы можем предоставлять рекомендации по новым продуктам каждый раз, когда пользователи добавляют новый товар в свою корзину.

Исходные файлы (репозиторий GitHub)

Исходный код этой демонстрации доступен в этом репозитории GitHub.

Нужна профессиональная помощь?

Если у вас есть ИИ или веб-проект и вам нужна профессиональная помощь, вы можете связаться с нами в Preese Interactive Media.