Изучение модуля neural-redis с Node.js

Это часть 27.2 моей серии Node/Redis. Предыдущая часть была Я мод для нейросетей (Глава 1).

В прошлой записи мы говорили о настройке окружения для neuro-redis. Вернитесь и прочтите его первым. Если вы прошли первую часть, у вас должна быть хорошая рабочая среда, чтобы начать возиться с Node.js, Redis Unstable и Neural Redis.

Наша цель в этой части статьи — разработать полноценную игровую площадку для модуля Neural Redis и немного глубже погрузиться в API модуля. Пока мы строим эту игровую площадку, некоторые концепции могут показаться чуждыми, но в Интернете есть много ресурсов. Я наткнулся на это видео во время написания, в котором концептуально подробно рассказывается о том, как работают нейронные сети и как их применять для решения проблем.

Но давайте начнем с кода. Как и в некоторых моих предыдущих статьях, мы будем использовать стек NEAR — Node.js/Express/Angular/Redis. Идите вперед и загрузите репо и запустите его на своем компьютере:

$ npm install
$ node --credentials ../path-to-your-credentials.json
Server running.

Как и в предыдущих частях, файл JSON представляет собой объект конфигурации node_redis, хранящийся в виде файла JSON. Удостоверьтесь и держите его за пределами вашего репозитория — никто не хочет, чтобы его учетные данные распространялись по общедоступной сети. После запуска сервер должен быть доступен по адресу http://localhost:8781/

Эта игровая площадка должна иметь возможность создавать нейронную сеть (NR.CREATE), вводить элементы в сеть (NR.OBSERVE), инициировать обучение (NR.TRAIN), получать информацию о нейронной сети (NR.INFO) и запустить нейронную сеть по значениям (NR.RUN/NR.CLASS). Как правило, вы работаете с этими командами на одной клавише, поэтому на игровой площадке вы можете установить одну клавишу для использования для всех операций.

Когда вы щелкнете по входу (также известному как размытие), игровая площадка немедленно запустит NR.INFO. NR.INFO оказывается очень важным для нашего скрипта. Эта команда дает вам список информации о настройке и состоянии сети. В нашей ситуации мы хотим иметь возможность манипулировать любой конфигурацией сети — очень важно настроить сеть. После успешного запуска NR.INFO мы извлечем следующую информацию:

  • тип — либо регрессор, либо классификатор, который будет определять, как мы будем проверять выходные данные, и если мы включим команду NR.CLASS.
  • макет — указывает, сколько входов и выходов нам нужно показать.
  • автонормализация — определяет, нужно ли нам проверять входные данные на -/+1 или мы можем оставить их в покое.

NR.INFO будет сопоставлен с вызовом HTTP по адресу /info/[your-key]. По умолчанию node_redis просто предоставит вам чередующийся список свойств и значений. Мы разобьем его на более удобную коллекцию и отправим обратно по сети. На стороне клиента мы получим соответствующую информацию и отобразим ее с помощью всегда полезного модуля ng-tables. Соответствующая информация будет спрятана в области действия — большая часть нашей работы по изменению макета выполняется в самом шаблоне Angular.

API-интерфейс Neural Redis не очень точно сопоставляется с операциями CRUD, но по большей части мы можем приблизительно сопоставить его с интерфейсом RESTful. Вышеупомянутый информационный вызов не входит в этот шаблон, но все остальное мы можем как бы сопоставить с HTTP-глаголами. Давайте посмотрим, как мы можем это выложить:

  • PUT /networkNR.CREATE на основе объекта JSON, переданного в теле запроса HTTP.
{ 
   networkType : ['CLASSIFIER' || 'REGRESSOR'],
   numberOfInputs : [number of inputs],
   outputs : [number of outputs],
   hiddenLayers : [number of hidden layers] optional,
   dataset : [dataset size] optional,
   testset : [testset size] optional,
   normalize : [true || false] optional
}
  • PUT /network/[key]NR.OBSERVE с объектом JSON, переданным в теле запроса HTTP.
[
   {
      input : [array of input values],
      output : [array of output values],
      dataset : ['TRAIN' || 'TEST'] optional
   },
   ...
]
  • PATCH /network/[key]NR.TRAIN с объектом JSON, переданным в теле запроса HTTP.
{
   maxCycles : [maximum number of cycles] optional,
   maxTime : [maximum time in milliseconds] optional,
   backtrack : [true || false] optional,
   autostop : [true || false] optional
}
  • GET /network/[key]/[input-1]/…/[input-n][?classify=true]Получить результат ключа и входных данных, переданных либо с помощью NR.RUN, либо с помощью NR.CLASS (если задан параметр запроса classify).
  • УДАЛИТЬ /network/[key]Выполнить NR.RESET для данного ключа

На сервере есть определенный шаблон, который я хочу выделить. Одной из особенностей модуля нейронной сети является то, что многие команды имеют переменное количество аргументов. Это не редкость в Redis, но обычно у вас будет очень веская причина для изменения аргументов в коде скриптов. Однако на этой игровой площадке, в зависимости от количества входов/выходов, указанных в NR.INFO, вам нужно иметь возможность обрабатывать n входов и nвыходов. В случае чего-то вроде NR.OBSERVE у вас есть шаблон [key] [input-1]..[input-n] -> [output-1]..[output-n] [обратный вызов]. Для этого я указываю свои аргументы в виде массива:

var myArgs = []

Затем мне нужно добавить входные данные, которые уже находятся в массиве:

myArgs = myArgs.concat(theInputs);

В этот момент вам нужно нажать на стрелку конструкции

myArgs.push('->');

Затем вывод, тоже уже в массиве:

myArgs = myArgs.concat(theOutputs);

Что также удобно, так это то, что вы можете при желании добавить аргументы, поэтому в этом случае я смотрю на условное выражение и, если оно существует, я добавляю другие аргументы:

if (dataset) {
   myArgs.push('DATASET');
}

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

client.nr_observe(myKey,myArgs,function(err,resp) { ... });

В прошлом я делал подобные вещи с шаблоном client.command.apply(client,…), но это всегда казалось запутанным. Это гораздо более четкий способ достижения того же самого.

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

Одна вещь, которую я заметил во время разработки, — это странная обработка ошибок в блоке MULTI/EXEC. При наблюдении за данными я разрешаю одновременное поступление нескольких строк, а затем перебираю их, добавляя к MULTI. Первоначально я просто проверял длину ответа, чтобы увидеть, сколько элементов было добавлено, но я заметил, что независимо от того, что я делал — вставка без параметров, буквенные параметры вместо числовых и т. д. — это никогда не подводило. По крайней мере, из ответа об ошибке на EXEC. При ближайшем рассмотрении я увидел, что о каждом NR.OBSERVE сообщается либо об успехе, либо о провале в многомассовом ответе. Насколько я понимаю, это не ожидаемое поведение, и я подал отчет об ошибке как в neural-redis, так и в redis. Вероятно, это будет исправлено очень скоро, и я обновлю это в будущем.

На стороне клиента/Angular.js это довольно тяжелая одностраничная игровая площадка. Каждая команда вынесена на отдельную панель. Панели имеют интерактивные входы, которые должны (более или менее) выполнять некоторую слабую проверку. На большинстве панелей есть область, которая показывает, как будет выглядеть ваша команда, если вы захотите поместить ее в redis-cli.

Для большинства команд успех или неудача обозначается уведомлением в стиле Growl через модуль Angular тостер.

Как упоминалось выше, ошибки для NR.OBSERVE немного странные, так как он может одновременно успешно выполняться и завершаться ошибкой. Кроме того, NR.RUN/NR.CLASS вернет результаты на самой панели, так как вы, вероятно, захотите задержаться на результатах более чем на несколько секунд, когда уведомление получено.

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

После того, как вы разблокировали панели, приступайте к вводу данных в панель «Ввод (Наблюдение)». Вы можете добавить несколько строк одновременно, нажав «Добавить строку». После того, как вы добавили свои данные, вы можете обучить свою следующую работу на панели «Обучение» и, наконец, вы можете увидеть, как работает ваша нейронная сеть, на панели «Выполнить/классифицировать». Этого должно быть достаточно, чтобы начать экспериментировать с командами Neural Redis.

Я планирую использовать эту платформу для будущих выпусков и в конечном итоге сделать что-то полезное, используя стандартные блоки, представленные в коде сервера. А пока ознакомьтесь с серией статей Даниэля Ши о Neural Redis, чтобы получить более математический взгляд на эту тему. И следите за обновлениями здесь для будущих экспериментов Node/Redis/Neural Network!