Нейросеть с нуля своими руками. Часть 1. Теория

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

  1. Берем Tensorflow

  2. Создаем нейросеть

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

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

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

Нейросеть с нуля своими руками. Часть 2. Реализация

Нейросеть с нуля своими руками. Часть 3. Sad Or Happy?

Для чего нужны нейросети

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

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

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

Устройство нейрона

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

Нейрон условно можно разделить на три части: тело нейрона, и его отростки - дендриты и аксон.

Дендриты нейрона создают дендритное дерево, размер которого зависит от числа контактов с другими нейронами. Это своего рода входные каналы нервной клетки. Именно с их помощью нейрон получает сигналы от других нейронов.

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

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

Нейросети в IT

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

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

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

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

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

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

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

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

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

Добавим немного математики.

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

f(x) = 1 / (1 + e^-x)

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

Итак, давайте рассмотрим пример с топологией сети рассмотренной выше. У нас есть три входных нейрона со значениями ИСТИНА, ЛОЖЬ и ИСТИНА соответственно, два нейрона в среднем слое нейросети (эти слои также называют скрытыми), и один выходной нейрон, который сообщит нам о решении, принятом нейросетью. Так как наша сеть еще не обучена, поэтому значения весов на входах нейронов мы возьмем случайными в диапазоне от -0,5 до 0,5. 

Таким образом сумма входных значений первого нейрона скрытого слоя будет равна 

1 * 0,43 + 0 * 0,18 + 1 * -0,21 = 0,22

Передав это значение в активационную функцию, мы получим значение, которое наш нейрон передаст далее по сети в следующий слой.

sigmoid(0,22) = 1 / (1 + e^-0,22) = 0,55

Аналогичные операции произведём для второго нейрона скрытого слоя и получим значение 0,60.

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

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

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

error = 0.60 - 0 = 0.60

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

sigmoid(x)dx = sigmoid(x)(1 - sigmoid(x))

Таким образом наша дельта весов будет равна

delta = 0.60 * (1 - 0.60) = 0.24

Новый вес для входа нейрона рассчитывается по формуле

weight = weight - output * delta * learning rate

Где weight - текущий вес, output - значение на выходе предыдущего нейрона, delta - дельта весов, которую мы рассчитали ранее и learning rate - значение, подбираемое экспериментально, от которого зависит скорость обучения нейросети. Если оно будет слишком маленьким - нейросеть будет более чувствительна к деталям, но будет обучаться слишком медленно и наоборот. Для примера возьмем learning rate равным 0,3. Итак новый вес для первого входа выходного нейрона будет равен:

w = 0,22 - 0,55 * 0,24 * 0,3 = 0,18

Аналогичным образом рассчитаем новый вес для второго входа выходного нейрона:

w = 0.47 - 0.60 * 0.24 * 0.3 = 0.43

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

error = 0.18 * 0.24 = 0.04

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

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

Комментарии 10

    +1
    Это всё уже тысячу раз писалось и такую сеть сделает кто угодно, ибо материала в инете полно.
    Гораздо более интересна свёрточная нейросеть с нуля со всеми нюансами. Там пошаговая инструкция создания и обучения мне известна ровно одна и то финальная сборка заброшена; в остальных просто брошена кипа формул, типа, сами догадаетесь, как там из них обучать свёртки.
      0
      Надеюсь тег javascript в следующей статье оправдает себя
        +1
        Да, он был добавлен именно ради нее. Последующие части уже опубликованы, а ссылки на них добавлены в статью. Надеюсь, они вас не разочаруют.
        +2
        К сожалению, автор не имеет никакого понятия о том, как работает настоящая нейронная сеть мозга.Всё это — устаревшие представления, которые никак не связаны с реальными возможностями нервной ткани. А представление о том, что нейрон, как персональный компьютер, может обрабатывать сигналы и принимать решения — вообще дикость и несуразица.
          0

          расскажите в двух словах: как работает настоящая нейронная сеть мозга? и в чём (кроме терминологии) автор не прав?


          PS: я в ML ни бум-бум. прочитал статью с интересом

            +1
            Ну Вы даёте! В двух словах Вы хотите, чтобы я изложил одну из важнейших научных тайн.
            Самое главное, что в этой публикации бросается в глаза, нейроны воспринимаются как вычислительные центры, которые, как логические модули в компьютере могут оценивать информацию и решать, какие действия на эту информацию должен предпринять мозг. Этого нет и быть не может. Нейрон не обладает никакими возможностями — ни быстродействием, ни памятью. Его функция — «выстрелить» потенциалом действия в нужный момент — и всё.Совокупность выстреливших нейронов (нейронный ансамбль по Анохину) и представляет собой семантический код входящей информации. Эта комбинация возбуждённых нейронов строго соответствует какому-то объекту внешней среды (вроде QR-кода). Поскольку нейронов — миллионы, то и код может быть бесконечно сложной детализации. А вот создание этой комбинации ещё остроумнее, и называется афферентным синтезом(Анохин), правда, тогда он до этого не додумался. В мозгу существует механизм создания колебания электрической структуры мозга со строго определённой частотой — это выявляется на ЭЭГ в виде альфа-ритма. Свойства его очень строго контролируются — не ниже 9.5 Гц и частота +-0.25 Гц. Это несущая частота, как при голографии. На неё накладываются колебания, возникающие от воздействия на итерационно-рекурсную структуру нейронно-глиальной сети, и возникает фрактальная интерференционная неравномерность электрического состояния ткани вокруг нейронов; при этом вокруг одних нейронов порог потенциала действия снижается, и нейрон «выстреливает», вокруг других — повышается. Эта сложнейшая фрактальная структура строго соответствует воспринятой ранее, если сохраняется стабильность работы мозговой ткани и технические характеристики её остаются неизменными. На этом основан новый метод дисперсии альфа-ритма для диагностики психопатологии.
              0
              Ну Вы даёте! В двух словах Вы хотите, чтобы я изложил одну из важнейших научных тайн.

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


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

              эм, биты в компьютере тоже в каком-то смысле "принимают решение". Разглядывая биты трудно увидеть программу, но чтобы понимать программирование — биты тоже приходится разглядывать. Не?


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

              а коэффициент при нейроне кто помнит?

                0
                возникает фрактальная интерференционная неравномерность электрического состояния ткани вокруг нейронов; при этом вокруг одних нейронов порог потенциала действия снижается, и нейрон «выстреливает», вокруг других — повышается. Эта сложнейшая фрактальная структура строго соответствует воспринятой ранее, если сохраняется стабильность работы мозговой ткани и технические характеристики её остаются неизменными.

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

                Сама же ритмика легко может возникать по совершенно другой причине. Нейроны, не нашедшие корреляции в данных, организуются в структуру малого мира (ту самую, которую повсеместно стали недавно находить в мозге), и возникает генераторная активность, идущая от хабов, очень стабильная. По крайней мере, в моём эксперименте с искусственным органоидом (живой прототип которого в сенсорной депривации также запускает ритм) получилось именно так.
              +1
              Как я заметил в тексте статьи, я не являюсь специалистом в области биологии и вполне допускаю, что мои представления могут быть достаточно далеки от реальности. Все что тут изложено, лишь компиляция общедоступной информации на тему нейронных сетей.
                +1
                То есть это фантастический рассказ? Так и надо было написать, чтобы мы время не тратили.

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

            Самое читаемое