<jupyter_start><jupyter_text>Введение в искусственные нейронные сети Урок 1. Основы обучения нейронных сетей  Содержание методического пособия:    Общие сведения о искусственных нейронных сетях    Место искусственных нейронных сетей в современном мире    Области применения    Строение биологической нейронной сети    История искусственных нейронных сетей    Небольшой пример по обучению простой нейронной сети    Основы обучения нейронных сетей    Инициализация весов. Функции активации    Обратное распространение ошибки и градиентный спуск    Пример построения двухслойной нейронной сети на numpy      Общие сведения об искусственных нейронных сетяхВ даннном разделе мы узнаем, что такое нейронные сети и из каких компонентов они состоят.Искусственнная нейронная сеть - это программная реализация математической модели биологической нейронной сети. Ее цель — извлечь пользу из переданных ей данных. Например, сказать, что на фотографии или сделать какой либо прогноз.Нейронные сети состоят из входных, внутренних и внешних слоев. Внутренние слои отвественны за обучение. Входные слои позволяют ввести данные во внутренние слои, а внешние слои позволяют вывести полезный вывод из данных. Сами слои состоят из отдельных нейронов, которые связаны с другими нейронами и по определенным алгоритмам способны усиливать с ними связь.Через нейронные сети в процессе обучения многократно пропускают какие-либо данные, например изображения и указывают, что на них. За счет работы различных алгоритмов в нейронной сети вырабатывается такая система связей между нейронами, которая позволяет в последствии при переданных ей новых данных получить ответ, что на фотографии.Но распознавание объектов на фотографиях далеко не единственная сфера применения нейронных сетей. Существует множество видов различных нейронных сетей для разных задач. Основные из этих видов мы разберем в данном курсе. Детальное рассмотрение процесса обучения нейронной сети и базовые сведения об ее архитекутуре будут разобраны во второй половине данного методического пособия.  Место искусственных нейронных сетей в современном миреВ данном разделе мы разберем какие новшества принесли искусственные нейронные сети в современный мир. Долгое время создание искусственого интеллекта оставалось частью научной фантастики и несмотря на многие предсказания прошлого времени, что искусственный интеллект вот-вот будет создан,  искусственный интеллект был создан совсем не давно. По крайней мере люди стали говорить, что они пользуются искусственным интеллектом. Как миним можно сказать, что искусственный интеллект создан с маркетинговой точки зрения. Но, не только с маркетинговой.  Отчасти со сторого научной точки зрения он также создан — в 2014 году был пройден Тест Тьюрига.Тем не менее сейчас мы можем говорить только лишь о том, что создан специализированный искусственный интеллект, способный выполнять только определенные задачи, которые раньше было свойственно выполнять только человеку. Т.е. создан так называеймый «узкий искусственный интеллект». Создание общего искусственного ителлекта, т. е. программы, которая могла бы мыслить как человек, по прежнему остается фантастикой.Искусственные нейронные сети, которые являются предметом изучения данного курса являются основой революции в области «узкого искусственного интеллекта» и являются одной из главных надежд для создания общего искусственного интеллекта в будущем. Однако искусственвенный интеллект может строиться не только на базе искусственных нейронных сетей. Более того как мы увидем в разделе посвященным истории, данных подход долгое время считался неперспективным. Так почему же революция совершена именно благодаря им, а не другим подходам? Дело в том, что компьютерные программы давно могут автоматизировать различную работу человека. Им можно дать определенный алгоритм работы и они будут его надежно выполнять. Программы как известно получают на вход данные, обрабатывают их с помощью определенного алгоритма и на выходе опять выдают данные.Но возьмем к примеру завод. На нем есть много труда, который можно было автоматизировать. Не трудно составить алгоритм, например, для промышленного манипулятора, который будет переносить определенные тяжелые предметы. С помощью технологий перевода данных обработанных процессором в движения манипулятора можно заставить его совершить определенную полезное работу. Но как ввести данные для обработки? Как дать понять манипулятору какой предмет ему нужно взять? Здесь на помощь может прийти камера. Долгое время различные алгоритмы компьютерного зрения справлялись с определенными несложными задачами наподобее определения предметов через цвет или его геометрическую форму.Но что если нужно работать с предметами разного цвета и формы, но объеденных другими общими свойствами? Например сортировать яблоки и груши. Они могут быть разной формы и цвета, но тем не менее нужно отделить отдно от другого. Человеку не трудно справиться с подобной задачей но для искусственного интеллекта долгое время это оставалось не посильной задачей. Однако с применением глубоких искусственных нейронных сетей начиная с 2012 года выполнение этой задачи стало реальностью. Но не только в сфере распознования образов важную роль сыграли нейронные сети. Также в сфере распознования речи. И не только в сфере распознования, сейчас есть нейронные сети которые могут создавать изображения и синтезировать голос. Также они применяются в многих других сферах.Подобный переворот случился благодаря тому, что нейронные сети стали повторять собою нейронные сети человеческого мозга, который как раз может легко справляться с подобной задачей. Об этом мы подробнее поговорим в разделе «Строение билогической нейронной сети».Сейчас же давайте перейдем к разделу «Применение нейронных сетей», чтобы понять какие задачи они могут выполнять и к решению каких проблем реального мира нужно быть готовым специалистам по нейронным сетям.  Применение нейронных сетейВ нашем курсе мы будем проходить различные виды нейронных сетей и будем рассматривать в соотвествующих уроках применение каждого вида отдельно. Здесь же в общих чертах нарисуем картину их применения.Нейронные сети применяются в области компьютерного зрения. Начиная с обычных программ распознающих предметы в производстве или распознование личности на телефоне, заканчивая крупными комплексами компьютерного зрения, которые применяются в беспилотных автомобилях.Нейронные сети применяются для распознования голоса — Siri, Google помощник, Alexa, Алиса и прочие голосовые помощники распознают голос человек с помощью нейронных сетей. Их применение не ограничивается только лишь распознованием голоса. В последние годы достигнут сущесветнный прогресс в синтезе голоса. Применяются они также в области машинного перевода. Прогресс в этой области как раз связан с ними. Комбинация из нескольких нейронных сетей позволяет описать содержание фотографии.Нейронные сети применяются в медицине — для анализа снимков и заключений о болезни человека в некоторых областях лучше чем человек.Нейронные сети также применяются для анализа поведения покупателей, а также на бирже. Находят они применения также в прогнозах погоды.Нейронные сети находят также применение для творчества. Уже сейчас есть художники которые продают картины которые рисуют нейронные сети.Есть и другие интересные области применения нейронных сетей. Например они используются для придания цвета старым фотографиям или фильмам. В улучшении качетства изображений. Для чтения по губам. Для генерации фотографий и много другого.  Строение биологической нейронной сетиТермин «Нейрон» был  введен <NAME> в 1891 г. Что из себя предствляет человечекий нейрон? Часто можно услышать его сравнение с транизистором. Однаком более убедительным является точка зрения нейрофизиологов утверждающих, что нейрон это полноценный компьютер. Человеческий нейрон также как и компьютер призван обрабатывать сигналы. То, что человеческое сознание является результатом работы нейронов было впервые отмечено в работах Александра Бейна (1873) и <NAME> (1890). В работах Бейна впервые акт запоминания был сформулирован как акт укрепление нейронных связей.В нейронах мозга есть аксоны, через которые в него попадают сигналы,  у искусственных нейронов также есть подобные входы. У нейронов мозга есть выходной компонент дендрит и искуственных нейронов также есть выходные компоненты. У нейронов человеческого мозга насчитывается порядка 10 000 связей с другими нейронами у искусственных нейронов их также может быть много. И биологической нейронной сети и в искусственной в случае если сумма поступающих сигналов в нейрон превышает определенный порог то сигнал передается следующему нейрону.Искусственные нейронные сети работают подобно биологическим — через них пропускаются данные и те связи которые ведут к нужным результатам укрепляются. Но если в человеческом мозге проихсходит засчет утолщения этих связей, то в программных нейронных сетях это происходит за счет увеличение чисел символизирующих эти связи.  История исскусственных нейронных сетейПредставление о нейронных сетях как главном способе создания искусственного интеллекта сложилось далеко не сразу и в данном разделе мы осветим основные вехи этой технологии.Математическую модель искусственного нейрона была предложена У. Маккалоком и У. Питтсом  в 50-х годах 20 века.В виде компьютерной системы нейронная сеть была впервые реализована  Фрэнком Розенблаттом в 1960 году. Фрэнк Розенблат создал «Марк-1», который являлся программно-аппаратным комлексом и воплащал в себе простую нейронную сеть в один слой. Можно отметить, что современные сети (на момент ноября 2019 г.) включают в себя 50 — 150 слоев, а экспериментальные достигают размера более 1000 слоев.Однако волна энтузиазма 60-х годов в отношении искусственных нейронных сетей сменилась скепсисом в отношении них из-за трудностей в совершении определенных логических операций и невозможности получать практические результаты. Доминирующим подходом для создания искусственного интеллекта стали экспертные системы. Они являлись по сути продвинутой энциклопедией знаний в той или иной сфере.Однако в 2012 г. появились глубокие нейронные сети. Т.е. сети с количеством внутренних слоев больше одного. Сначала кол-во слоев таких нейронных сетях было примерно 3-10. Но в силу эффективности подхода по увеличению кол-ва слоев, быстро появились нейронные сети кол-во слоев которых стало исчисляться десятками.Именно с 2012 г. нейросети стали считаться доминирующим способом в решении многих задач искусственного интеллекта. Стоит задаться вопросом: почему именно в этот период? Прорыв был обеспечен более продвинутой архитектурой, возросшими количеством хранимых компьютерами данных для тренировки нейронных сетей, а также возросшей вычислительной мощностью компьютеров. Также можно отметить появления в эту пору возможности использовать видеокарты для тренировки нейронных сетей, которые оказались лучше приспособленными для решения этих задач чем классические CPU.    Общие сведения о том, как происходит обучение нейронных сетей Давайте возьмем какую-нибудь типичную задачу для нейросети и попробуем разобрать как она могла бы ее решить. В качества примера задачи можем взять типичный пример — определить на фотографии находиться кот или собака. Сначала давайте подумаем как это данная задача решалась бы без нейронной сети. Вспомним, что фотография это набор пикселей. Пиксели в компьютере репрезентуются матрицей чисел. Если бы все коты были синего цвета, а собаки красного мы могли бы просто детектировать числа отвественные за данные цвета в изображении и на основаннии этого делать выводы о том кот или собака расположены на фотографии. В действительности как мы понимаем это не так. У кота очень много отличительных черт как и у собаки. Перечесление этих уникальных свойсвт займет долгое время. Но стоит учесть вот какое обстоятесльво - усики и лапки и шерстка есть у многих животных. И скорее всего все что нам остается делать это описывать размеры этих усиков, лапок и т. д. Изучать их угла наклона и т. д. Попытки делать это вручную были, как раз до эпохи нейронных сетей. Но результаты были не высокие. Как вы понимаете объем признаков слишком большой. Таким образом задача нейронной сети содержать в себе необходимый набор признаков которые позволяет ей отличить один объект от другого. В данном уроке будет разобран пример разработки нейронной сети, которая будет обучаться различать разновидности цветка ириса. Данные виды похожи, они состоят из одинаковых элементов, но эти элементы имеют разные размеры в случаи каждого отдельного вида. На этом простом примере мы сможем понять как работают более сложные нейронные сети.Но встает вопрос как поместить в нейронную сеть необходимый набор признаков? Ответ на этот вопрос будет даваться в течении всего данного курса и всеравно не будет исчерпывающим. Поскольку для это придется обучать нейронную сеть, а ее обучение порой преобретает характер искусства нежели набор предписаний.Тем не менее в процессе обучения всегда нужно пройти ряд этапов, которые будут общими для любого процесса обучения.Нам определиться с архитектурой нейронной сети. От удачно выбранной архитектуры будет зависеть насколько быстро мы сможем обучить нейронную сеть, насколько точной она будет, а также сможем ли мы ее обучить в принципе. Архитектура нейронной сети как вы догадываетесь зависит от задачи. В зависимости от задачи нам нужно будет выбрать:- Количество слоев из которых будет состоять нейронная сеть- Сколько будет нейронов в этих слояхКроме этого нам нужно будет подобрать верным образом компоненты, за счет которых будет обучаться нейронная сеть (подробнее о них можно будет узнать в следующих разделах данного мет. пособия)- Систему инициализации весов нейронов - Функцию активации весов нейронов- Алгоритм корректировки весов нейроновЭто безусловно список самых общих компонентов с которыми нам нужно будет определиться, но на данной стадии обучения надо полагать нам их будет достаточно. Архитектуры нейронных сетей мы будем изучать на отдельных уроках этого курса. В этом же уроке мы разберем следующие неотъемлимые компоненты обучения нейронной сети любой архитектуры — инициализация весов, их корректировка, рассмотрим виды функций активации весов нейронной сети, разберем, что такое градиентный спуск и метод обратного распространения ошибки.  Инициализация весов. Функции активации ![vesa.png](attachment:vesa.png) Мы с вами выяснили, что в процессе обучения нейронной сети в ней должны вырабататься признаки по которым она сможет определять, что на фотографии. Но как эти признаки будут репрезентованы в нейронной сети? Все эти признаки будут состовлять определенную сложную матрицу чисел. Настолько сложную, что человеку не под силу ее проанализировать. Какие это будут признаки? Это тоже неизвестно. Их может быть так много и они могут быть такими специфическими, что и описать их будет трудно. Для этого и нужны нейросети, они берут на себя содержание и описание в себе этих признаков.Однако все эти признаки записываются через систему весов нейронов. Веса нейрона отражают толщину связи этого нейрона с др. нейроном. Из совокупности  этих связей и состоят признаки. Т.е. другими словами обучить нейронную сеть значит найти нужные веса для ее нейронов. Первоначально веса можно задать случайными числами. И в процессе обучения они будут все больше и больше подходить под решения задачи. Программно это можно показать на примере следующего фрагмента кода.<jupyter_code>import numpy as np
import random
import matplotlib.pyplot as plt

# генерации случайных чисел для инициализации весов
np.random.seed(1)
synaptic_weights = 2 * np.random.random((3, 1)) - 1

w1 = 2 * np.random.random((1, 2)) - 1
w2 = 2 * np.random.random((2, 2)) - 1

# можем посмотреть 

print(w1)
print(w2)<jupyter_output>[[-0.39533485 -0.70648822]]
[[-0.81532281 -0.62747958]
 [-0.30887855 -0.20646505]]<jupyter_text>Но мало просто присвоить определенные веса нейронам. Необходимо также определить функцию по которой будет активироваться нейроны. Выбранная функция активация будет одинаковой для всех нейронов. С помощью нее можно определять с какой силой нужно подействовать на нейрон, чтобы он  активировался и передал сигнал дальше. ![formula.png](attachment:formula.png) На картинке выше показан нейрон в виде формулы. Активация его как говорилась зависит от входных данных, его весов и некоего порогого значения.Функций таких существует очень много - сигмоида, линейная, ступенчатая, ReLu, tahn и т. д. Нам скорее сейчас нужно понять их суть изложенную выше нежели чем научиться их выбирать. Отметим лишь следующее. Для учебных целей часто можно встретить сигмоиду. Для реальных задач — ReLu. ![formula2.png](attachment:formula2.png) В данном фрагменте кода мы можем посмотреть как программно реализовать функцию сигмоид -<jupyter_code># вычисление сигмоид функции

def sigmoid(x):
     return 1 / (1 + np.exp(-x))
# для картинки
D  = 10

X = np.linspace(0-D,0+D,20)
Y = sigmoid(X)

plt.plot(X ,  Y ,'-g',label = 'сигмоид' )
plt.legend()
plt.xlabel('X')
plt.ylabel('Y')
plt.show()<jupyter_output><empty_output><jupyter_text>А вот, как например на Python, можно реализовать упомянутую выше функцию активации Relu -<jupyter_code># вычисление Relu функции

def ReLU(x):
         return x  *  (x  >  0)

# для картинки
D  = 10

X = np.linspace(0-D,0+D,21)
Y = ReLU(X)

plt.plot(X ,  Y ,'-g',label = 'ReLU' )
plt.legend()
plt.xlabel('X')
plt.ylabel('Y')
plt.show()<jupyter_output><empty_output><jupyter_text>Обратное распространение ошибки и градиентный спуск Давайте прежде чем начнем разбирать обратное распространение и градиентный спуск, скажем несколько слов о тех типах архитектур нейронной сети, которые будут использованы в данном уроке. Это простые нейронные сети. С точки зрения архитектуры их можно отнести к полносвязным нейронным сетям так как все нейроны связаны между собой. С другой стороны их можно отнести к нейронным сетям прямого распространения(feed forward). Сети прямого распространения подразумевает распространения сигналы от вход к выходу, в отличии от реккурентных нейронных сетей где во внутренних слоях сигналы могут ходить по циклу. О плюсах и минусах сетей полносвязных мы поговорим в уроке посвященном Сверточным нейронным сетям, поскольку данные нейронные сети отошли от этого подхода и нам нужно будет выяснить почему.После того как на выходе нейронной сети мы получили определенное цифровое значение нам нужно сравнить его с искомым. Мы можем посчитать насколько в количественом выражение ошиблась нейросеть. Задача обратного распространения ошибки пройтись от выхода ко входу и скорректировать веса нейронов. Это процесс происходит множество раз в процесс обучения.Можно сказать что процесс обучения нейронной сети это попытка оптимизировать веса нейронной сети до тех пор пока не будет достигнута минимальная степень ошибки. Для этого хорошо подходит такой алгоритм как градиентный спуск. Суть данного метода заключается в том, чтобы искать такие числовые параметры при которых значение ошибки достигнет нуля. Градиентным он называется потому что это процесс пошаговый, требующий одно вычисление за другим. Спуском он называется потому что значение ошибки должно быть как можно меньше.Обратите внимание на следующий график из него видно, что есть определенное число по шкале весов которому соотсвествует минимальное значение по шкале Error. Это число и нужно находить в процессе обучения нейронных сетей. ![formula3.png](attachment:formula3.png) Давайте попробуем реализовать программного градиентный спуск, чтобы лучше понять как он работает.<jupyter_code>''' 
Исходный код к уроку 1.
Демонстрация работы градиентного спуска 
'''

# первоначальное точка
start_point = 1

# размер шага(learning rate)
learn_r = 0.01

# установка первоначальной точности
precision = 0.0001

# функция градиента для y = X**4 - 3 * X**3 
gr_func = lambda x: 4 * x**3 - 9 * x**2

# для картинки
D  = 1

X = np.linspace(2.2-D,2.2+D,20)
Y = X**4 - 3 * X**3 

# начальная точка
next_point = start_point

iter = 0 

x = []
x.append(next_point)

plt.figure(figsize=(16,2))
plt.plot(X,  Y ,'r',label = 'Y(X)' )

# количество итерация 
n = 150
for i in range(n):
    current_point = next_point

    # движение в негативную сторону вычисляемого градиента
    next_point = current_point - learn_r*gr_func(current_point)
    x.append(next_point)
    # print(next_point)                                     

    iter += 1

    # остановка когда достигнута необходимая степень точности
    print(f"Итерация: {iter}")
    print(f"Текущая точка {current_point}| След-я точка {next_point}")
    print(f"Дистан-я между текущей точк. и след. {abs(current_point - next_point)}")
    print("--------------------------------------------------------")
    
    
    if(abs(current_point - next_point) <= precision):
        break

print(f"минимум {next_point}, количество затраченных итераций: {iter}") 
X_grad = np.array(x)
plt.plot(X_grad ,  (X_grad **4 - 3 * X_grad **3) ,'-*g',label = 'GD' )
plt.legend()
plt.xlabel('X')
plt.ylabel('Y')
plt.show()<jupyter_output>Итерация: 19
Текущая точка 2.046459608850202| След-я точка 2.0805566701861915
Дистан-я между текущей точк. и след. 0.034097061335989665
--------------------------------------------------------
Итерация: 20
Текущая точка 2.0805566701861915| След-я точка 2.109895552692658
Дистан-я между текущей точк. и след. 0.029338882506466657
--------------------------------------------------------
Итерация: 21
Текущая точка 2.109895552692658| След-я точка 2.1348434430078296
Дистан-я между текущей точк. и след. 0.024947890315171417
--------------------------------------------------------
Итерация: 22
Текущая точка 2.1348434430078296| След-я точка 2.155836743721791
Дистан-я между текущей точк. и след. 0.02099330071396155
--------------------------------------------------------
Итерация: 23
Текущая точка 2.155836743721791| След-я точка 2.173342190492916
Дистан-я между текущей точк. и след. 0.017505446771124866
--------------------------------------------------------
Итерация: 24
Текущая точка 2.17334219[...]<jupyter_text>Метод обратного распрасранения (backpropogation) самый популярный способ обучения нейронных сетей, однако у него есть несколько альтернатив - Метод упругого распространения (Resilient propagation или Rprop)  и генетический алгоритм (Genetic Algorithm). Rprop для корректировки весов и смещений использует знак градиента, а не его значение, а генетический алгоритм для задач оптимизации и моделирования использует случайный подбор.  Небольшой пример по обучению простой нейронной сетиРассмотрим создание простой нейронной сети. Данная нейронная сеть будет обучаться предсказывать 4 число на основании первых трех. Для этого мы ей передадим обучающую набор данных из трех последовательностей чисел. В каждой последовательности чисел будет три первых числа в качетсве входных данных и 4 число которое следуюет за этими данными.После обучения нейронной сети мы просим пользователя ввести 3 числа и программа выдаст 4 число в качестве предсказания.В данном примере будут использоваться следующие обучающие последовательсноти:Данные 1|    0	    0	1	 0Данные 2|    1	    1	1	 1Данные 3|    1	    0	1	 1Данные 4|    0	    1	1	 0Здесь можно заметить, что четвертое число всегда соотвествует первому. Эту закономерность и должна благодаря коду ниже научиться обнаруживать нейронная сеть, а затем на произвольных данных введенных пользователем выдать правильное предсказание.Этот пример позволяет создать и запустить простейшую нейронную сеть, но уже  в этом примере присутсвуют все необходимые атрибуты создания нейронных сетей: подготовка данных для обучения модели, конфигурация модели, запуск модели.Дальше мы разберем, что такое функция активации, обратное распространение, внутренние слои нейронной сети и прочие аспекты создания нейронных сетей.Обратите внимание, что здесь используется библиотека numpy для получения дополнительных команд при работе с массивами. Более подробно данную библиотеку мы рассмотрим позже.<jupyter_code>'''
Исходный код к уроку 1.
Пример простой нейронной сети на numpy
'''

import numpy as np

# генерации случайных чисел для инициализации весов
np.random.seed(1)
synaptic_weights = 2 * np.random.random((3, 1)) - 1


# вычисление сигмоид функции
def sigmoid(x):
     return 1 / (1 + np.exp(-x))

# вычисление производной от сигмоид функции
def sigm_deriv(x):
        return x * (1 - x)
    
# вычисление Relu функции

def ReLU(x):
         return x  *  (x  >  0)
# вычисление производной от Relu функции
def relu_deriv(x):
    return x>0
# для картинки
D  = 10

X = np.linspace(0-D,0+D,210)
Y = sigmoid(X)
dY = sigm_deriv(sigmoid(X))

plt.figure(figsize =(16,4))
plt.subplot(1,2,1)
plt.plot(X ,  Y ,'-g',label = 'сигмоид' )
plt.plot(X ,  dY ,'-r',label = 'd сигмоид' )
plt.grid('On')
plt.legend()
plt.xlabel('X')
plt.ylabel('Y')

Yr = ReLU(X)
dYr = relu_deriv(ReLU(X))
plt.subplot(1,2,2)
plt.plot(X ,  Yr ,'-g',label = 'ReLU' )
plt.plot(X ,  dYr ,'-r',label = 'd ReLU' )
plt.legend()
plt.grid('On')
plt.xlabel('X')
plt.ylabel('Yr')
plt.show()
# тренировка нейронной сети
def train_nn(training_inputs, training_outputs, training_iterations):
    global synaptic_weights
    for iteration in range(training_iterations):
        # перекачивание данных через нейрон
        output = run_nn(training_inputs)

        # вычисление ошибки через обратное распространение back-propagation
        error = training_outputs - output
            
        # выполнение корректировки весов
        adjustments = np.dot(training_inputs.T, error * sigm_deriv(output))

        synaptic_weights += adjustments


# пропускание входных данных через нейрон и получение предсказания
# конвертация значений во floats
def run_nn(inputs):
    global synaptic_weights
    inputs = inputs.astype(float)
    output = sigmoid(np.dot(inputs, synaptic_weights))
    return output
# создание данных для обучения
training_inputs = np.array([[0,0,1], [1,1,1], [1,0,1], [0,1,1]])
training_outputs = np.array([[0,1,1,0]]).T

# запуск тренировки нейронной сети 
train_nn(training_inputs, training_outputs, 10000)
print("веса после завершения обучения: ")
print(synaptic_weights)

# получение трех чисел от пользователя
user_inp1 = str(input("Первое число(0 или 1): "))
user_inp2 = str(input("Второе число(0 или 1): "))
user_inp3 = str(input("Третье число(0 или 1): "))

print(f"Проверка на новых данных: {user_inp1} {user_inp2} {user_inp3}")
print("Предсказание нейронной сети: ")
print(run_nn(np.array([user_inp1, user_inp2, user_inp3])))

print(run_nn(np.array([0,0,0])))<jupyter_output>[0.5]<jupyter_text>Пример построения  двухслойной нейронной сети на numpy<jupyter_code>'''
Исходный код к уроку 1.
Построение двухслойной нейронный сети для классификации цветков ириса
'''

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

# sklearn здесь только, чтобы разделить выборку на тренировочную и тестовую
from sklearn.model_selection import train_test_split
### Шаг 1. Определение функций, которые понадобяться для обучения
# преобразование массива в бинарный вид результатов
def to_one_hot(Y):
    n_col = np.amax(Y) + 1
    binarized = np.zeros((len(Y), n_col))
    for i in range(len(Y)):
        binarized[i, Y[i]] = 1.
    return binarized

# преобразование массива в необходимый вид
def from_one_hot(Y):
    arr = np.zeros((len(Y), 1))

    for i in range(len(Y)):
        l = Y[i]
        for j in range(len(l)):
            if(l[j] == 1):
                arr[i] = j+1
    return arr

# сигмоида и ее производная
def sigmoid(x):
    return 1/(1+np.exp(-x))

def sigmoid_deriv(x):
    return (x)*(1 - (x))

# нормализация массива
def normalize(X, axis=-1, order=2):
    l2 = np.atleast_1d(np.linalg.norm(X, order, axis))
    l2[l2 == 0] = 1
    return X / np.expand_dims(l2, axis)

### Шаг 2. Подготовка тренировочных данных
# получения данных из csv файла. укажите здесь путь к файлу Iris.csv
iris_data = pd.read_csv("Iris.csv")
# print(iris_data.head()) # расскоментируйте, чтобы посмотреть структуру данных

# репрезентация данных в виде графиков
g = sns.pairplot(iris_data.drop("Id", axis=1), hue="Species")
plt.show() # расскоментируйте, чтобы посмотреть

# замена текстовых значений на цифровые
iris_data['Species'].replace(['Iris-setosa', 'Iris-virginica', 'Iris-versicolor'], [0, 1, 2], inplace=True)

# формирование входных данных
columns = ['SepalLengthCm', 'SepalWidthCm', 'PetalLengthCm', 'PetalWidthCm']
x = pd.DataFrame(iris_data, columns=columns)
#x = normalize(x.as_matrix())
x = normalize(x.values)

# формирование выходных данных(результатов)
columns = ['Species']
y = pd.DataFrame(iris_data, columns=columns)
#y = y.as_matrix()
y = y.values
y = y.flatten()
y = to_one_hot(y)

# Разделение данных на тренировочные и тестовые
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.33)
y
def neural_network(neuron_numb, l_r, epoches):
    
    w0 = 2*np.random.random((4, neuron_numb)) - 1 # для входного слоя   - 4 входа, 3 выхода
    w1 = 2*np.random.random((neuron_numb, 3)) - 1 # для внутреннего слоя - 5 входов, 3 выхода
    
    errors = []
    
    for i in range(epoches):

        # прямое распространение(feed forward)
        layer0 = X_train
        layer1 = sigmoid(np.dot(layer0, w0))
        layer2 = sigmoid(np.dot(layer1, w1))
    
        # обратное распространение(back propagation) с использованием градиентного спуска
        layer2_error = y_train - layer2 # производная функции потерь = производная квадратичных потерь 
        layer2_delta = layer2_error * sigmoid_deriv(layer2)
        
        layer1_error = layer2_delta.dot(w1.T)
        layer1_delta = layer1_error * sigmoid_deriv(layer1)
        
        w1 += layer1.T.dot(layer2_delta) * l_r
        w0 += layer0.T.dot(layer1_delta) * l_r
        
        # метрика модели
        error = np.mean(np.abs(layer2_error))
        errors.append(error)
        accuracy = (1 - error) * 100
        
#     plt.figure(figsize = (16,5))
#     plt.plot(errors)
#     plt.xlabel('Обучение')
#     plt.ylabel('Ошибка')
#     plt.show() # расскоментируйте, чтобы посмотреть 
    
#     N = 50
#     plt.figure(figsize = (16,5))
#     plt.plot(layer2[:N,1], 'r',label = 'Y new')
#     plt.plot(y_train[:N,1],'g', label = 'Y train')
#     plt.xlabel('№ примера')
#     plt.ylabel('выход сети и целевой')
#     plt.legend( )
#     plt.show() # расскоментируйте, чтобы посмотреть 
            
    print("Аккуратность нейронной сети " + str(round(accuracy,2)) + "%")
    
#     return round(accuracy,2)

    # прямое распространение(feed forward)
    layer0_t = X_test
    layer1_t = sigmoid(np.dot(layer0_t, w0))
    layer2_t = sigmoid(np.dot(layer1_t, w1))
    layer2_error_t = y_test - layer2_t
        
        
#     N = 50
#     plt.figure(figsize = (16,5))
#     plt.plot(layer2_t[:N,1], 'r',label = 'Y new')
#     plt.plot(y_test[:N,1],'g', label = 'Y train')
#     plt.xlabel('№ примера')
#     plt.ylabel('выход сети и целевой')
#     plt.legend( )
#     plt.show() # расскоментируйте, чтобы посмотреть
    
    # метрика модели
    error_t = np.mean(np.abs(layer2_error_t))
    accuracy_t = (1 - error_t) * 100
    print("Аккуратность нейронной сети на тесте " + str(round(accuracy_t,2)) + "%")
neural_network(neuron_numb=5, l_r=0.1, epoches=3000)
neural_network(neuron_numb=10, l_r=0.1, epoches=3000)
neural_network(neuron_numb=50, l_r=0.1, epoches=3000)
neural_network(neuron_numb=7, l_r=0.4, epoches=3000)
neural_network(neuron_numb=5, l_r=1, epoches=3000)
neural_network(neuron_numb=5, l_r=5, epoches=3000)
neural_network(neuron_numb=5, l_r=1, epoches=300)
neural_network(neuron_numb=5, l_r=1, epoches=30000)<jupyter_output>Аккуратность нейронной сети 96.28%
Аккуратность нейронной сети на тесте 99.19%<jupyter_text>Вывод:Для улучшения точности нейронной сети нужно выбирать адекватные параметры, для этой модели значения в 5 скрытых нейронов, шаг обучения 1 и самое большое количество эпох (30000) показало лучший результат.Количество скрытых нейронов должно лежать в промежутке от 3 до 7. При увеличении числа нейронов точность не сильно изменяется, однако, параметров становится на порядки больше, что ведет к увеличению затрат мощности.Скорость обучения показала хорошие результаты в рамках [0.1; 1]. При увеличении скорости обучения алгоритм не сходится в самой нижней точке на поверхности ошибок, что не позволяет дойти до минимальной ошибки.С каждой эпохой величина весов усиливается, что дает более точное предсказание. При очень больших количествах эпох сеть может переобучится, выкручивая в максимум и минимум веса, что неоднозначно скажется на сложных задачах.<jupyter_code>### Шаг 3. Обученние нейронной сети

# определим число нейронов скрытого слоя
neuron_numb = 5


# присваевание случайных весов



w0 = 2*np.random.random((4, neuron_numb)) - 1 # для входного слоя   - 4 входа, 3 выхода
w1 = 2*np.random.random((neuron_numb, 3)) - 1 # для внутреннего слоя - 5 входов, 3 выхода

# скорость обучения (learning rate)
n = 0.1

# массив для ошибок, чтобы потом построить график
errors = []

# процесс обучения
for i in range(3000):

    # прямое распространение(feed forward)
    layer0 = X_train
    layer1 = sigmoid(np.dot(layer0, w0))
    layer2 = sigmoid(np.dot(layer1, w1))

    # обратное распространение(back propagation) с использованием градиентного спуска
    layer2_error = y_train - layer2 # производная функции потерь = производная квадратичных потерь 
    layer2_delta = layer2_error * sigmoid_deriv(layer2)
    
    layer1_error = layer2_delta.dot(w1.T)
    layer1_delta = layer1_error * sigmoid_deriv(layer1)
    
    w1 += layer1.T.dot(layer2_delta) * n
    w0 += layer0.T.dot(layer1_delta) * n
    # метрика модели
    error = np.mean(np.abs(layer2_error))
    errors.append(error)
    accuracy = (1 - error) * 100


### Шаг 4. Демонстрация полученных результатов
# черчение диаграммы точности в зависимости от обучения
plt.figure(figsize = (16,5))
plt.plot(errors)
plt.xlabel('Обучение')
plt.ylabel('Ошибка')
plt.show() # расскоментируйте, чтобы посмотреть 

N = 50
plt.figure(figsize = (16,5))
plt.plot(layer2[:N,1], 'r',label = 'Y new')
plt.plot(y_train[:N,1],'g', label = 'Y train')
plt.xlabel('№ примера')
plt.ylabel('выход сети и целевой')
plt.legend( )
plt.show() # расскоментируйте, чтобы посмотреть 
        
print("Аккуратность нейронной сети " + str(round(accuracy,2)) + "%")


# прямое распространение(feed forward)
layer0_t = X_test
layer1_t = sigmoid(np.dot(layer0_t, w0))
layer2_t = sigmoid(np.dot(layer1_t, w1))
layer2_error_t = y_test - layer2_t
    
    
N = 50
plt.figure(figsize = (16,5))
plt.plot(layer2_t[:N,1], 'r',label = 'Y new')
plt.plot(y_test[:N,1],'g', label = 'Y train')
plt.xlabel('№ примера')
plt.ylabel('выход сети и целевой')
plt.legend( )
plt.show() # расскоментируйте, чтобы посмотреть

# метрика модели
error_t = np.mean(np.abs(layer2_error_t))
accuracy_t = (1 - error_t) * 100
print("Аккуратность нейронной сети на тесте " + str(round(accuracy_t,2)) + "%")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0738239670367403, 'fraction_numerical': 0.023549273205905916, 'mean_word_length': 4.596317053642914, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '8804', 'license': 'MIT', 'n_tokens_mistral': 14250, 'n_tokens_neox': 16437, 'n_words': 4039, 'path': 'neural_networks/nn_1_lesson.ipynb', 'repo_name': 'QuackingKnox/geekbrains', 'size': '34968'}