КПДВ про нейронные сети


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


Но врожденные любознательность и энтузиазм довели меня до того, что я стал одним из разработчиков Synaptic — проекта фреймворка для построения нейронных сетей на JS с 3к+ звезд на GitHub. Сейчас мы с автором фреймворка занимаемся созданием Synaptic 2.0 с ускорением на GPU и WebWorker-ах и с поддержкой почти всех основных фич любого приличного NN-фреймворка.


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


Для начала — маленькая предыстория.


В конце октября я выступал с докладом на мероприятии #ITsubbotnik в Петербурге и начал тему, которую решил продолжить здесь. Давайте поговорим о том, как написать с нуля нейронную сеть на JavaScript.


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


Что такое нейронная сеть?


Лучшее из определений я услышал от одного умудренного опытом человека на конференции. Он сказал, что нейронные сети — это просто красивое название, которое придумали, потому что на определение "цепочки операций над матрицами" грант получить куда сложнее.


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


Чтобы объяснить, что такое сеть, нужно зайти немного издалека.


С точки зрения Data Scientist-ов (еще есть точка зрения нейробиологов, например) нейронная сеть — это один из инструментов моделирования какого-либо физического процесса. И любой из инструментов моделирования работает следующим образом:


  • мы совершаем значимое количество наблюдений
  • мы собираем ключевую информацию для каждого из этих наблюдений
  • из этой информации мы получаем знание
  • через это знание мы находим решение

Линейная зависимость


В качестве примера можно взять металлургию. Представим, что у нас есть сплав из 2 металлов. Если мы берем 80% чугуна и 20% алюминия (например), балка из такого сплава сломается, если на нее будет давить одна тонна. Если берем 70%+30% — она сломается, если будет давить 2 тонны. 60%+40% — 3 тонны.


Можно предположить, что вариант 50%+50% должен выдержать 4 тонны. В жизни все работает немного иначе, но упрощая — можно представить себе, что оно так работает.


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


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


В общем виде линейная регрессия выглядит следующим образом:


function predict(x1, x2, x3..., xN) {
    return weights.x1 * x1 
         + weights.x2 * x2 
         + ... 
         + weights.xN * xN 
         + weights.bias;
}

Стоит запомнить термин "weight", вес. В дальнейших примерах он тоже будет использоваться. Весом (или значимостью) каждого параметра называется его значимость в нашей предсказательной модели — а и нейронная сеть, и линейная регрессия — всё суть модели для предсказания.


Вес “bias”, или в переводе на русский “сдвиг” — это дополнительный параметр, который характеризует значение в нуле.


Например, в известной (пусть и не очень корректной) формуле "правильный вес должен быть равен росту — 100" — weight = height - 100 — bias равен -100, а весомость роста — единице.


Нелинейная зависимость


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


Одним из хороших примеров для изучения является датасет (набор данных) титаника со статистикой по выжившим людям.


Если поиграться с интерактивной визуализацией, можно увидеть, что в среднем женщин выжило больше. Однако если углубиться в детали, заметим, что среди экипажа и третьего класса — процент выживания был куда меньше. Чтобы построить более точную предсказательную модель, нам нужно каким-то образом записать в ней — "если это женщина и она из 1 класса, то она имела +10% вероятности выжить".


Ребята от науки предложили простую схему — такие параметры назвать дополнительными фичами и использовать их в оригинальной функции. То есть к нашим x1, x2, x3 и так далее добавляется еще один xN+1, который равен 1, если это женщина из первого класса, и 0, если нет. Потом появляются еще и еще параметры, и мы начинаем все это учитывать в наших расчетах.


Как на языке математики можно описать функцию "если условие, то 1, иначе 0"?


Если решать задачу в лоб, сделав аналог тернарного оператора, то у нас будет функция, график для которой выглядит так:


график hlim


Но можно поступить умнее, оставив себе кучу лазеек. Дело в том, что с подобным графиком сложно работать. Из-за разрыва от него нельзя, например, взять производную, или совершить еще десяток интересных трюков. Поэтому вместо такой "ломанной" функции обычно используются непрерывные и непрерывно возрастающие функции, например, сигмоида — 1 / (1 + Math.E ** -x). Выглядит она так:


график сигмоиды


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


Эта функция называется функцией активации.


В итоге мы получаем новый параметр через функцию вида


const activation_sigmoid = x => 1 / (1 + Math.exp(-x));

function predict(x1, x2, x3..., xN) {
    return activation_sigmoid(
       weights.x1 * x1 
     + weights.x2 * x2 
     + ... 
     + weights.xN * xN 
     + weights.bias);
}

Такая функция называется перцептроном.


Перцептрон — это простейший вид нейронной сети, не имеющий скрытых слоев. В визуальном представлении он выглядит как-то так:


графическое представление перцептрона


Если посмотреть на картинку из статьи "нейронная сеть" на википедии, то можно заметить очень большую схожесть:


граф нейронной сети


И мы подошли к определению нейронной сети с точки зрения реализации.


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


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


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


Нейронная сеть в коде


С нейронными сетями самым популярным примером является реализация XOR, это своеобразный Hello World для изучающих data science.


Особенность XOR заключается как раз в том, что это простейшая нелинейная функция — реализация в качестве линейной регрессии для нее невозможна (читай, нельзя провести линию через все значения).


Датасет для нее выглядит таким образом:


var data = [
    {input: [0, 0], output: 0},
    {input: [1, 0], output: 1},
    {input: [0, 1], output: 1},
    {input: [1, 1], output: 0},
];

Итак, нам нужно как-то реализовать XOR исключительно с помощью сложений и нелинейной функции, которое на вход принимает одно число.


Вот как выглядит такая реализация (пока без нейронной сети):


var activation = x => x >= .5 ? 1 : 0;
function xor(x1, x2) {
    var h1 = activation(-x1 + x2);
    var h2 = activation(+x1 - x2);
    return activation(h1 + h2);
}

Где h1 и h2 — это скрытые параметры.


Или, если попытаться добавить веса, то получается:


var activation = x => x >= .5 ? 1 : 0;
var weights = {
    x1_h1: -1,
    x1_h2: 1,
    x2_h1: 1,
    x2_h2: -1,
    bias_h1: 0,
    bias_h2: 0
}

function xor(x1, x2) {
    var h1 = activation(
    weights.x1_h1 * x1
    + weights.x2_h1 * x2
    + weights.bias_h1);

    var h2 = activation(
    weights.x1_h2 * x1
    + weights.x2_h2 * x2
    + weights.bias_h2);

    return activation(h1 + h2);
}

Как выглядит наша функция нейронной сети? Ну, мы заменяем веса на случайные значения.


var rand = Math.random;

var weights = {
    i1_h1: rand(),
    i2_h1: rand(),
    bias_h1: rand(),
    i1_h2: rand(),
    i2_h2: rand(),
    bias_h2: rand(),
    h1_o1: rand(),
    h2_o1: rand(),
    bias_o1: rand(),
};

При попытке запустить сеть мы получаем кашу.


Теперь перед нами встает задача "найти наиболее правильные веса". Зачем мы это сделали?


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


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


Наиболее популярны для этих задач две функции: метод наименьших квадратов, когда берется среднее квадратов ошибок (она удобна для задач регрессии, когда на выходе у нас значение, например, между 0 и 1, или 10 значений от -100 до 1250 — главное, что они могут находиться в данном диапазоне) и т.н. LogLoss или перекрестная энтропия, логарифмическая оценка потерь, которая эффективна для задач классификации, когда мы пытаемся, например, определить, какую из цифр или букв видит наша нейронная сеть.


Для XOR мы будем использовать среднее квадратов ошибок.


const _ = require('lodash');

var calculateError = () =>
    _.mean(data.map(({input: [i1, i2], output: y}) => 
        (nn(i1, i2) - y) ** 2));

Ученье — свет


Настало время учить нашу сеть.


Немного отступая назад, нам стоит разобраться, как "обучается" линейная регрессия. Работает она следующим образом — у нас есть тот же самый MSE (mean squared error), и мы пытаемся его уменьшить. Если вспомнить курс математики, то график квадрата от X выглядит так:


просто парабола


И наша задача — скатиться к самому минимуму этой параболы.


Чтобы скатиться к этому минимуму — нам нужно посмотреть по сторонам и понять, где у нас ошибка больше, а где меньше. А потом двинуться в сторону уменьшения. Это можно сделать числовыми способами (посмотреть значение для +1 и для -1 и посчитать, куда надо двигаться), а можно математически — взяв производную, которая характеризует скорость изменения функции. Говоря иначе, если при увеличении какого-либо параметра ошибка увеличивается, то производная ошибки будет положительной (мы в правой части параболы), и наоборот. Мы добавляем умноженную на наши веса производную к нашим же весам, и пошагово приближаемся к ответу с наименьшей ошибкой, пока не надоест или пока не достигнем локального минимума. Говоря еще проще — если мы берем производную и она положительная, то для этого конкретного значения при увеличении значения на входе ошибка будет расти, а при уменьшении — уменьшаться.


Если представить себе это визуально, выглядеть это будет как-то так:



В комментариях пишут, что изначально эта картинка из курса Andrew Ng


Если мы представим нашу функцию ошибки как (f(x) — y) * 2, то производная от нее будет равна 2 (f(x) — y) f'(x). Пруф)


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


Вживую


Наверное, пора просто показать код с объяснением происходящего.


Вставлять такой объем кода в хабр — довольно жестокое занятие, поэтому я выложил код с большим количеством комментариев на RunKit:


https://runkit.com/jabher/neural-network-from-scratch-in-js


и на русском:


https://runkit.com/jabher/neural-network-from-scratch-in-js---ru


и на всякий случай — дубль кода в gist.github.com


Заключение


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


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

Поделиться с друзьями
-->

Комментарии (40)


  1. MadridianFox
    07.12.2016 13:30
    -2

    Чугун, в отличие от аллюминия уже не является чистым металлом — это «сплав» железа с углеродом.


    1. Jabher
      07.12.2016 13:42
      +2

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

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


    1. Zenitchik
      07.12.2016 14:14
      +3

      Почему в кавычках? На самом деле — сплав. А ещё точнее — многофазная система из твёрдого раствора углерода в железе, цементита и графита.


  1. nckma
    07.12.2016 13:59

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


    1. Jabher
      07.12.2016 14:10
      +3

      Ну, это довольно корректное утверждение. Была история, как в 70-е (кажется), на волне увлечения перцептронами, какой-то НИИ объявил, что сможет распознать на фото танки, отличить их от всего остального. Это сейчас мы понимаем, что на тех мощностях это невозможно, но каким-то образом все в это верили.
      Но почему-то у них это заработало. Все успешно сдали, но вдруг в реальных условиях она перестает работать. Все в паники, начинают разбираться.

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

      Поэтому большая часть работы — это корректная подготовка примеров для обучения, перетасовка, кроссвалидация, вот это все.


    1. agentf
      07.12.2016 14:26
      +1

      С детьми примерно так же…


  1. vidyacat
    07.12.2016 14:10

    тема бэкпропагэйшен не раскрыта


    1. Jabher
      07.12.2016 14:15
      +3

      нейронные сети != deep learning. В сети с одним скрытым слоем не нужен механизм back propagation, при этом понимание того, как данные «текут» через узлы и почему нам нужны отдельно линейные преобразования и отдельно активации — куда более важно для понимания того, как сети работают. К тому же количество материала и так огромное получилось (если считать вместе с кодом, выложенным на runkit).

      Планируется еще одна статья про сверточные сети (там уже backprop необходим) и, возможно, про LSTM/RNN.


      1. Tatikoma
        07.12.2016 18:08
        +2

        Эта статья отличная. Буду очень ждать статью про сверточные сети.


      1. apelsyn
        08.12.2016 11:36

        Я недавно писал статью про машинное обучение на JS, там как раз очень нехватало такой теоретической части как у Вас. Я добавил линк на вашу статью. Очень хорошо и доступно получилось.


        1. Jabher
          08.12.2016 15:36
          -1

          Я очень рад, что могу где-то восполнить пробелы :)

          В этом и была проблема, почему написал такую статью — есть куча «ну вот мы делаем на фреймворке», и есть очень крутые с точки зрения тех, кто уже понимает, как это работает, и они разжевывают вот так, например — https://geektimes.ru/post/277088/. Статья крутая, но она работает так же, как паттерны разработки — ты читаешь, киваешь головой, вроде все понимаешь, но реальное понимание приходит только потом, после опыта.

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

          И да. Переходите с brain, он мертв, в смысле deprecated :) Подключайтесь к Synaptic, я надеюсь, релиз в январе будет.


  1. mephistopheies
    07.12.2016 17:21
    -3

    Давайте поговорим о том, как написать с нуля нейронную сеть на JavaScript.

    вопрос только один — зачем?


    1. Jabher
      07.12.2016 18:16
      +4

      как обычно, сделать плагин для PostCSS.

      А если серьезно — то ответ на вопрос «зачем написать», я не видел ни одной статьи, где не было бы реально внятного объяснения без матричных операций.
      А если вопрос «зачем на JS», то в реальности нет никакой разницы, с чем ты работаешь. R и Python с их супер-библиотеками на поверку оказываются тупо биндингами к C-шным библиотекам (окей, иногда с абстракциями), и чтобы начать работать с TensorFlow или XGBoost достаточно написать эти самые биндинги для Nodejs. Вся сила в экосистеме, и сделать эту экосистему не так сложно, как кажется — с учетом количества JS-разработчиков, желающих делать что-то большее чем формочки на сайтах


      1. mephistopheies
        07.12.2016 18:40
        -3

        А если вопрос «зачем на JS», то в реальности нет никакой разницы, с чем ты работаешь. R и Python


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

        а вот если вы занимаетесеь машинным обучением, по настоящему, а не сеточку на жс накидать, то на жс вы не то что бы страдать будите, вы просто не сможете им заниматься, вообще никак

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

        R и Python с их супер-библиотеками на поверку оказываются тупо биндингами к C-шным библиотекам (окей, иногда с абстракциями), и чтобы начать работать с TensorFlow или XGBoost достаточно написать эти самые биндинги для Nodejs

        Theano например полноценный фреймворк для глубокого обучения и написан почти полностью на питоне, и дифференцирование вычислительного графа и всякая другая наркомания, далее он генерит немного куда-си кода и компилит его, что бы все еще и н гпу работало

        А если серьезно — то ответ на вопрос «зачем написать», я не видел ни одной статьи, где не было бы реально внятного объяснения без матричных операций.

        думаю вы плохо гуглите, таких статей море, и видюшек на ютубе и курсов на курсере

        а то что сети используют матричные операции, это не просто так наверное, да?

        Вся сила в экосистеме, и сделать эту экосистему не так сложно, как кажется — с учетом количества JS-разработчиков, желающих делать что-то большее чем формочки на сайтах

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

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


        1. Jabher
          07.12.2016 19:00
          +2

          а вот если вы занимаетесеь машинным обучением, по настоящему, а не сеточку на жс накидать, то на жс вы не то что бы страдать будите, вы просто не сможете им заниматься, вообще никак


          Для реальных (ну насколько реальные вещи можно делать без аренды сервера в 100gb RAM) вещей я использую в основном XGBoost и Keras, плюс кучка вещей поменьше. Synaptic — это работа на перспективу в надежде, что получится принести ML в JS.


          думаю вы плохо гуглите, таких статей море, и видюшек на ютубе и курсов на курсере

          а то что сети используют матричные операции, это не просто так наверное, да?


          в том-то и дело, что нет. Уровень «разжеванности» в статье выше, чем где-либо кроме увиденного, где была бы реальная реализация, а не абстрактный разговор, и несколько ребят из OpenDataScience это подтвердили.

          Theano например полноценный фреймворк для глубокого обучения и написан почти полностью на питоне, и дифференцирование вычислительного графа и всякая другая наркомания, далее он генерит немного куда-си кода и компилит его, что бы все еще и н гпу работало


          На один Theano, который почти без си, у нас есть TensorFlow, AutoML, XGBoost, Caffe, MXNet, которые работают как биндинги к сям. И это то что я вспомнил сходу, в реальности кроме theano и либ поверх него ничего не работает без нативного слоя.

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


          Я удивлю, но большое количество JS-разработчиков довольно хорошо шарят в матане. Алгоритмы и структуры данных — они везде нужны.

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


          Это у вас не возникло желания ;) Поверх Postgres, Neo4j, ну и вишенкой на торте — поверх Redis


          1. mephistopheies
            07.12.2016 19:09

            плюс кучка вещей поменьше. Synaptic — это работа на перспективу в надежде, что получится принести ML в JS

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

            Я удивлю, но большое количество JS-разработчиков довольно хорошо шарят в матане. Алгоритмы и структуры данных — они везде нужны.

            это оценочное суждение, пруфов то нет, по моим знакомым 1 из 10 шарит, опять же смещенная выборка


            1. Jabher
              07.12.2016 19:24
              +1

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

              я и не отрицаю, что в данный момент не годен, я расчитываю на него в перспективе. причем не через месяц или два.

              это оценочное суждение, пруфов то нет, по моим знакомым 1 из 10 шарит, опять же смещенная выборка

              угу, а почему тогда в Питере Papers We Love сделали два фронтэндщика и позвали выступать третьего про CRDT, а датасатанисты бегали и удивлялись, как это не они сами сделали? :)

              А вообще — да, верно, 10% JS-еров шарят. Это те ребята, которые делают транспиляторы, синхронизацию между клиентами, статический анализ, фреймворки для webGL и так далее. Но фронтэндщиков в целом дохрена, и эти 10% в численном представлении не то чтобы сильно меньше, кмк, чем специалистов с аналогичным уровнем знаний, которые умеют в R модельки крутить, например.


  1. jasonOk
    07.12.2016 18:08

    Где можно прочитать больше похожей информации (помимо вашей wiki на github)?


    1. Jabher
      07.12.2016 18:09

      Пожалуй, я бы посоветовал начать с курса machine learning от Andrew Ng на Coursera, если нет проблем с английским


      1. Jabher
        07.12.2016 18:19

        Или вопрос конкретно про Synaptic?


      1. ooptimum
        08.12.2016 15:37

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

        По существу хочется дать небольшой совет: попробуйте в качестве функции активации использовать не сигмоидную функцию, а функцию, называемую rectified linear activation, имеющую очень простой вид: rect(x) = max(0, x), поэтому очень дешевую с точки зрения вычислительных ресурсов, что немаловажно для JS.


        1. Jabher
          08.12.2016 15:44

          Я, к сожалению, не знал, что она из этого курса — я ее нашел на каком-то непонятном портале три месяца назад, и банально не смог восстановить авторство :( Непонятно было, откуда она. И да, это одна гифка, не две.
          То, что она была у Эндрю в курсе — попросту забыл, увы. Спасибо большое за ваше замечание.

          Говоря по быстродействию:
          1. по сравнению с операциями перемножения матриц для больших размерностей скорость работы функции активации ничтожна — несмотря на то, что разовое перемножение дешевле, O(n,m) в итоге на полносвязном слое куда дороже чем O(n) на слое активации.
          2. Для маленьких сетей есть более полезные для обучения сеточек реализации — ломанная сигмоида и т.н. Leaky ReLU. Стоимость расчета примерно аналогичная, а обучение работает в большинстве случаев эффективнее (если не считать выходного слоя для задач классификации, там ReLU очень хорошо ложится).
          3. Бенчмарки утверждают, что скорость V8 и Chakra в задачах математики на JIT-оптимизированных функциях (которые исполнились несколько сотен или тысяч раз) довольно сильно приближается к скорости аналогичного кода на сях
          4. развивая мысль. Такие задачи все-таки обычно по возможности решаются на GPGPU — WebCL и OpenCL/CUDA в ноде.


          1. Kaiser
            08.12.2016 17:13

            ReLU хорош не сколько тем, что он быстрый, а тем что градиент в области >0 не затухает [Leaky ReLU чтобы совсем везде был]. У сигмоида он нормальный только в окрестности 0, поэтому случается vanishing gradient problem.

            Раз затронули быстродействие, то еще наброшу.

            Для обучения кажется не так перспективно, потому что сети учатся долго. Не так сложно сделать фреймворк, нужно чтобы он быстро учил сеть. Мне кажется сомнительным, что этот проект окажется быстрее существующих решений. Если я не прав, дайте ссылку на benchmark когда оно станет сравнимым с tensorflow/theano.

            Но вещь крайне полезная в плане применения обученной сети на девайсах, где есть js. Аналогично такой штуке: tf на android device
            Где-то обучили сеть, закинули веса в protobuf, затем загрузили обученную сеть в tensorflow на мобильном девайсе и (1) не грузим сервер (2) не нужно быть online. Одна из killer features TF.
            Правда нужно предварительно загрузить тяжелые веса. Но тут от автора Keras есть приличного качества сеть всего в 100Mb: https://arxiv.org/abs/1610.02357 (речь идет о распознавании картинок)

            Но, в силу своей необразованности, я не знаю девайсов js-only. Данный проект был бы крайне полезен именно для них. Придумал только один пример. Prediction на стороне клиента прямо в браузере. Загружаем веса обученной в keras/tensorflow сети в браузер, берем вход клиента, делаем предсказание (а вероятно много предсказаний), не тратя ресурсы сервера. Но есть сомнения, что это реально нужно.

            Jabher напиши пожалуйста видишь ли ты применение этого js фреймворка в устройствах? В каких?


            1. Jabher
              08.12.2016 19:26

              Касательно быстродействия — Synaptic планируется обновить, чтобы он стал похожим на Keras. Т.е. вся вычислительная мощность будет находиться в одном из бэкэндов, например, в том же TensorFlow. Я думаю, дальше объяснять не надо :)

              Для задачи «загружаем веса» уже есть keras.js.

              Вообще предполагаемое применение — очень простое. Есть JS-сообщество, и многим в нем очень хочется поиграться с machine learning. На уровне «распознавать котиков, чтобы включать брызгалку». Если им дать доступ к чему-то не очень сложному для них, то они начнут с этим играться. Если не дать — вряд ли они для этого будут изучать питон. Что будет дальше — неизвестно, но когда этим людям дали ноду, случился какой-то чудовищный буст в развитии и фронтэнда, и бэкэнда. Я очень надеюсь, что тут случится такая же история.


              1. Kaiser
                09.12.2016 13:19

                > Для задачи «загружаем веса» уже есть keras.js.

                Я так понял, что Synaptic были первее. Цитата со страницы keras.js. Но да, получается что сейчас keras.js для такой задачи удобнее.
                > Inspiration is drawn from a number of deep learning / neural network libraries for JavaScript and the browser, including Tensorflow Playground, ConvNetJS, synaptic, brain, CaffeJS, MXNetJS. However, the focus of this library is on inference only.

                Ок, если TF будет бэкэндом, то да, получится js wrapper, который кому-то может быть удобен.


                1. Jabher
                  09.12.2016 13:23

                  synaptic никоим образом не параллелится с keras.js, ребята, которые его сделали — реально большие молодцы, но они решают вот абсолютно другую задачу, не связанную с синаптиком.


    1. timiskhakov
      08.12.2016 14:40

      Еще на Coursera есть классный курс по основам анализа данных и машинного обучения от Яндекса и МФТИ: https://www.coursera.org/learn/mathematics-and-python/


  1. n0ne
    07.12.2016 18:10

    А можно подборку книг, блогов, сайтиков каких-то на данную тематику?
    Понимаю, конечно, что надо бы и самому поискать, но может всё же да? (-:


    1. Jabher
      07.12.2016 18:29

      я бы порекомендовал помимо курса выше разве что http://statweb.stanford.edu/~tibs/ElemStatLearn/. Но это очень тяжелая книга, хотя и одна из самых крутых. Первые 50 страниц я осиливал где-то две недели до полного понимания, например.


  1. QtRoS
    07.12.2016 21:43

    Господа, вопрос в лоб — почему сигмоиду всегда рисуют сжатой по оси X?
    Она ведь намного более пологая (производная у нее довольно невелика). По этому типичному графику с диспропорциями осей можно делать другие выводы…


    1. domix32
      07.12.2016 22:43

      Думаю потому что значения обычно выбирают в пределах [-1,1], а в этом промежутке сигмоид похоже все еще слишком крутой


  1. domix32
    07.12.2016 22:42

    Запрашиваю перевод hacker's guide и таки внос кода в статью с комментариями.


    1. Jabher
      08.12.2016 01:11

      Если вы под hacker's guide подразумеваете то, что выложено на RunKit — то оно изначально делалось как на русском, так и на английском же, и ссылки обе есть. Или вы о чем?


      1. domix32
        08.12.2016 01:34

        Hacker's guide to neural networks — более понятно и обширно рассказывает содержимое вашей статьи. А вот как смотреть код на runkit мне непонятно.

        Слишком легко можно сделать что-то не так.


      1. domix32
        08.12.2016 01:44

        Конечно же ждем продолжения с рассказом как из вот такого простого кирпича сделать что-то более полезное, чем предсказатель xor


        1. apelsyn
          08.12.2016 11:27

          Можете посмотреть еще мою статью:
          Нейронные сети на Javascript


  1. inoyakaigor
    08.12.2016 15:02
    +1

    Наконец-то хоть кто-то человеческим языком без лишнего матана объяснил как нейронные сети работают.


  1. iDennis
    10.12.2016 21:47

    tensorflow nodejs


  1. Large
    14.12.2016 02:39

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


  1. sashaz
    14.12.2016 12:00

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