Как-то раз я наткнулся на книгу под названием «Создай свою нейросеть», автор которой -Тарик Рашид и после прочтения остался доволен, в отличие от многих других методичек по нейронным сетям, которые по-своему, несомненно, хороши, в этой книге все подавалось простым языком c достаточным количеством примеров и советов

По этой же книге я и хочу пройтись пошагово, а именно по практической части — написанию кода простой нейронной сети.

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

image

Теории по машинному обучению и нейронным сетям на хабре и так достаточно. Но если кому-то это необходимо, некоторые ссылки я оставлю в конце статьи. А сейчас, приступаем непосредственно к написанию кода, причем писать мы будем на python, будет лучше, если при написании кода вы будете использовать jupyter-notebook

Шаг 1. Инициализация сети


Сначала нам, конечно же, надо инициализировать все действующие компоненты нашей сети

#импортируем numpy — это библиотека языка Python, добавляющая поддержку больших многомерных массивов и матриц
import numpy
# импортируем scipy.special , -scipy содержит модули для оптимизации, интегрирования, специальных функций, обработки изображений и  многих других задач, нам же здесь нужна наша функция активации, имя которой -Сигмоида
import scipy.special
#Вероятно, нам понадобится визуализировать наши данные
import matplotlib.pyplot 

# Определяем наш класс нейронной сети
class neuralNetwork:
    
    # Инициализация нашей  нейронной сети
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate): #В параметрах мы записываем обязательный self, входные данные,  данные  скрытого слоя, выходные данные ,скорость обучения соответственно)
        # устанавливаем количество узлов для входного , скрытого слоя, выходного слоя
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        
        # Тут обозначены веса матрицы, wih -  вес между входным и скрытым слоем , а  так же  who- вес между скрытым и выходным  слоем
        self.wih = numpy.random.rand(self.hnodes, self.inodes)
        self.who = numpy.random.rand(self.onodes, self.hnodes)

        # Скорость обучения -это наш гиперпараметр, то есть, параметр , который мы подбираем ручками, и в зависимости от того, как нам это удобно нам, и , конечно же, нейронной сети
        self.lr = learningrate
        
        # Наша Сигмоида- функция активации
        self.activation_function = lambda x: scipy.special.expit(x)

Немного о том, как выглядит узел в нейронной сети


image

На картинке изображен самый, что ни на есть узел, только представлен он обычно в виде круга, а не прямоугольника. Как мы видим, внутри прямоугольника(ну или круга) — это все абстрактно, находятся 2 функции:

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

2-я Функция принимает в качестве параметра то самое значение, которое насуммировала первая функция, и эта вторая функция называется функцией активации. В нашем случае -Сигмоида

Продолжаем:

Часть 2. Тренировка Нейронной Сети


def train(self, inputs_list, targets_list):
        # Конвертируем наш список в двумерный массив
        inputs = numpy.array(inputs_list, ndmin=2).T # поступающие на вход данные input
        targets = numpy.array(targets_list, ndmin=2).T #целевые значения targets
        
        # Подсчет сигнала в скрытом слое
        hidden_inputs = numpy.dot(self.wih, inputs)
        # Подсчет сигналов, выходящих из скрытого слоя к выходному слою. Тут в нашем узле, куда поступали все данные в переменную hidden_inputs (1я функция), эта переменная подается  как параметр в Сигмоиду - функцию активации (2я функция)
        hidden_outputs = self.activation_function(hidden_inputs)
        
        # Подсчет сигналов в конечном(выходном) слое
        final_inputs = numpy.dot(self.who, hidden_outputs)
        # Подсчет  сигналов, подающихся в функцию активации
        final_outputs = self.activation_function(final_inputs)
        
        # Значение ошибки (Ожидание - Реальность)
        output_errors = targets - final_outputs
        # Ошибка скрытого слоя становится ошибкой ,которую мы получили для <b>ошибки выходного слоя</b>, но уже <b>распределенные по весам между скрытым и выходным слоями</b>(иначе говоря с учетом умножения соответствующих весов) 
        hidden_errors = numpy.dot(self.who.T, output_errors) 
        
        # Обновление весов между скрытым слоем и выходным (Явление того, что люди зовут ошибкой обратного распространения)
        self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))
        
        # Обновление весов между скрытым слоем и входным(Та же ошибка ошибка обратного распространения в действии)
        self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs))
        
        pass

И вот мы приближаемся к концу

Часть 3. Опрос нейронной сети


#Создаем функцию , которая будет принимать входные данные
 def query(self, inputs_list):
        # Конвертируем поданный список входных данных в двумерный массив
        inputs = numpy.array(inputs_list, ndmin=2).T
        # Подсчет сигналов в скрытом слое
        hidden_inputs = numpy.dot(self.wih, inputs)
        # Подсчет сигналов, поданных в функцию активации
        hidden_outputs = self.activation_function(hidden_inputs)
        
        #Подсчет сигналов в конечном выходном слое
        final_inputs = numpy.dot(self.who, hidden_outputs)
        #Подсчет сигналов в конечном выходном слое, переданных в функцию активации
        final_outputs = self.activation_function(final_inputs)
        
        return final_outputs

Доводим дело до конца


 
#Подаем конкретное значение для входного , скрытого ,выходного слоев соответственно(указываем количество <b>нод</b>- узлов в ряду входного, скрытого, выходного соответственно
input_nodes = 3
hidden_nodes = 3
output_nodes = 3

# Возьмем коэффициент обучения - скорость обучения равной, например... 0.3!
learning_rate = 0.3

# Создаем нейронную сеть(n это объект класса neuralNetwork , при его создании запустится конструктор __init__  , и дальше все будет включаться по цепочке
n = neuralNetwork(input_nodes,hidden_nodes,output_nodes, learning_rate)

P.S


Выше была представлена простейшая модель нейронной сети, способной на вычисления. Но какого-то конкретного применения показано не было.

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

1.Github
2.Книга «Создай свою нейронную сеть»
3.Теория по машинному обучению 1
4.Теория по машинному обучению 2
5.Теория по машинному обучению 3
6.Теория по машинному обучению 4

Также можно ознакомиться с этим курсом.

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


  1. DartAlex
    13.02.2019 16:37
    +1

    Объясню в чём суть, для тех кто не разбирается в программировании. Вот смотрите, есть библиотека которая делает нейронную сеть. У этой библиотеки есть следующие функции: принять_начальные_данные, запустить_нейронную_сеть, показать_результаты. И что мы делаем? Как думаете?

    Библиотека.принять_начальные_данные
    Библиотека.запустить_нейронную_сеть
    Библиотека.показать_результаты


  1. Vinchi
    13.02.2019 17:02

    на sklearn подобная задача делается в несколько строчек кода да еще и быстрее.
    scikit-learn.org/stable/modules/neural_networks_supervised.html


    1. 0xUL Автор
      13.02.2019 17:11

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


      1. Sychuan
        13.02.2019 21:37
        +1

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


  1. tuxi
    13.02.2019 18:46

    Листал Хабр, бац! вижу «Знакомство с простейшей нейронной сетью и ее пошаговая реализация» «Машинное обучение», «Программирование»

    Ура! Наконец то появилась статья не про то как подключать библиотеки на питоне, а про внутреннюю реализацию таких сетей!

    У меня же висит технический долг перед самим собой, создать сеть распознающую определенную категорию геометрических объектов! Так чего же ждем? Вперед! К новым вершинам знаний!

    Потираю руки, наливаю кофе, откидываюсь в кресле, тынц!

    1. берем питон
    2. делаем импорт либ
    3. вызываем из либы сеть
    4. профит!

    <тут картинка с недопитым кофем и пустым вращающимся креслом >

    Чем дальше, тем сильнее ощущение, что тут питонистам индульгенция выдана по теме «нейронные сети и машинное обучение: расскажите всем какие замечательные у вас есть библиотеки» :)


    1. Alyoshka1976
      13.02.2019 20:08

      Если технический английский — не слишком большая проблема, то вот — www.analog.com/media/en/technical-documentation/dsp-book/dsp_book_Ch26.pdf — от ребят из Analog Devices, весьма подробно, формулы только по делу, картинки опять же, хотя и черно-белые. И без лишних комплексов — примеры приведены на Бейсике ;-)


    1. Sychuan
      13.02.2019 21:38

      3. вызываем из либы сеть

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


      1. tuxi
        13.02.2019 22:09
        +1

        «Функция активации, имя которой -Сигмоида» это разве не оно?

        На самом деле, автору надо было всего лишь указать 3 хаба в списке хабов,
        — «Питон»
        — «Машинное обучение»
        — «Программирование»

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


        По этой же книге я и хочу пройтись пошагово, а именно по практической части — написанию кода простой нейронной сети.

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


        1. Sychuan
          13.02.2019 22:33

          Функция активации, имя которой -Сигмоида» это разве не оно

          Ну это просто функция 1/(1+e^-x)
          Ее можно было и в ручную написать. И было бы у автора не
          import scipy.special
          
          self.activation_function = lambda x: scipy.special.expit(x)
          

          а что-нибудь типа:
          self.activation_function = lambda x: 1/(1+e**-x) 
          

          На самом деле, автору надо было всего лишь указать 3 хаба в списке хабов,
          — «Питон»
          — «Машинное обучение»
          — «Программирование»

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


          1. tuxi
            13.02.2019 22:53
            +1

            И объяснить хотя бы бегло в 1 абзаце, что такое сигмоида и почему выбрана она. В сети куча картинок на тему «сигмоида» vs «линейная» vs «ступенчатая» функции активации нейрона. Вот первая попавшаяся ссылка например.
            Я не сомневаюсь, что этой статьей автор делает хорошее дело, но все же имеет смысл дать возможность людям понять, что такое «сигмоида» и почему она лучше для задач классификации/распознавания.


  1. webviktor
    13.02.2019 20:31
    +1

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


    1. Sychuan
      13.02.2019 21:41

      Самая простая статья на понимание нейросетей была у этого товарища на мой взгляд neuralnetworksanddeeplearning.com

      И существует ли вообще вариант для технаря (аля поставить галочки в какой-то софтине/сервисе/онлайн, чтоб создать и обучить чему-то сеть)?

      Библиотеки scikit-learn.org/stable/index.html или Keras в принципе позволяют за несколько строчек все сделать. Вариант отдельной программы не знаю


      1. webviktor
        13.02.2019 22:19

        Спасибо, познавательно.


    1. raamid
      14.02.2019 16:15

      Похоже в соседней теме то что вам нужно:
      Пример простой нейросети на C++
      Там конечно не так няшно и аккуратно как здесь, зато там есть торты и можно ими кидаться :)



  1. YuryB
    13.02.2019 20:59
    +1

    шо, опять, вводная по нейросетям?


  1. ZNY
    14.02.2019 16:16

    есть стандарты типа

    import numpy as np

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


  1. raamid
    15.02.2019 03:41

    Попробовал сделать пример по вашей статье, столкнулся со следующими проблемами:
    1. В строках (в первом блоке)
    self.wih = numpy.random.rand(self.hnodes, self.inodes))
    self.who = numpy.random.rand(self.onodes, self.hnodes))
    лишняя закрывающая скобка
    2. При копировании кусков программы из разных мест статьи иногда ломаются отступы, а для Python'а это критично. Возможно лучше было бы весь код поместить в один блок.
    3. Все-таки новичку не совсем понятно, что делать дальше с полученной в самом конце перменной n. Правильно ли я понимаю, что теперь нужно будет загрузить где-то данные для тренировки и затем вызвать:

    n.train(mydata) // тренировать нейросеть
    n.query(myquery) // посмотреть как она работает


    1. 0xUL Автор
      15.02.2019 19:03

      Вот, глянь на файл. Вот тут указано, как дальше быть
      А именно глянь с 4 ячейки — тут (In:[4]), там код увеличится у тебя в размерах :)