(Первоначально опубликовано здесь: https://codability.in/understanding-neural-networks/)

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

Введение

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

Биологический нейрон

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

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

То, как мы, люди, учимся, до конца не известно, однако мы можем извлечь некоторые подсказки из Правила Хебба. Дональд Хебб, канадский психолог, в своей книге The Organization of Behavior заявил: Когда аксон клетки A находится достаточно близко к клетке B и неоднократно или настойчиво участвует в ее возбуждении, какой-то процесс роста или метаболические изменения происходят в одной или обеих клетках, так что эффективность А, как одной из клеток, запускающих В, увеличивается. Его часто перефразируют так: Нейроны, которые активируются вместе, соединяются вместе.

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

Искусственный нейрон

Первая формальная модель нейрона была предложена Уорреном МакКаллохом и Уолтером Питтсом в 1943 году. Она была очень похожа на логические элементы, из которых сделаны компьютеры. Этот нейрон МакКаллоха-Питтса включается, когда количество его активных входов превышает некоторый порог. Он может моделировать как ворота И, так и ворота ИЛИ, т. Е. Если порог равен единице, нейрон действует как ворота ИЛИ; если порог равен количеству входов, как вентиль И. Таким образом, то, что делает компьютер, может быть достигнуто с помощью сети этих нейронов. Но чего он не делает, так это учится. Это стало возможным благодаря обеспечению переменных весов между нейронами, что и сделал Фрэнк Розенблатт, физик из Корнельского университета. Это привело к изобретению персептрона в 1957 году.

Сегодня военно-морской флот показал эмбрион электронного компьютера, который, как ожидается, сможет ходить, говорить, видеть, писать, воспроизводить себя и осознавать свое существование.
— The New York Times, 1958

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

Персептрон — это простой линейный классификатор; математически это функция, которая сопоставляет свои входные данные x (вектор с действительным знаком) с выходным значением f(x) (одиночное двоичное значение). Это можно представить как:

где w представляет вектор действительных весов, b – смещение, а w⋅x – скалярное произведение, представленное следующим образом.

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

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

Нейронные сети!

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

Причина, по которой я говорю скрытые «слои», а не «слой», заключается в том, что количество этих слоев может быть больше одного, а в большинстве сложных задач больше одного. Каждый слой в некотором роде представляет собой нелинейную функцию, которая распознает определенные функции. Во-первых, есть форма линейного преобразования, за которой следует некоторое сжатие, которое обычно нелинейно. Это примерно то, что происходит внутри слоя. Мы скоро углубимся в подробности этого.

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

Структурная функциональность

Давайте на самом деле построим нейронную сеть, которая может распознавать рукописные цифры, мы собираемся использовать базу данных MNIST, которая является популярным набором данных рукописных цифр, она имеет обучающий набор из 60 000 изображений и тестовый набор из 10 000 изображений. Каждое изображение имеет размер 28 на 28 пикселей и содержит уровни серого.

Таким образом, сеть, которую мы собираемся построить, будет принимать значения пикселей, что означает, что наш входной слой будет иметь 28 × 28 = 784 нейрона, каждый нейрон соответствует пикселю на изображении. Теперь давайте переопределим наш нейрон на данный момент, давайте предположим, что нейрон — это не что иное, как узел в сети, который содержит число. Итак, говоря о 784 нейронах, каждый нейрон содержит значение серого для каждого пикселя. Теперь перейдем к последнему слою. Вероятно, в нем должно быть 10 нейронов, каждый из которых соответствует цифре от 0 до 9. (У нас может быть и меньшее количество нейронов, но для простоты давайте остановимся на одном для каждого числа).

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

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

Теперь у нас есть сеть, которая выглядит примерно так: 784 ➔ 16 ➔ 10.
Давайте попробуем понять, как наша сеть учится распознавать, возьмем в качестве примера цифру 8. Как упоминалось ранее, каждому нейрону во входном слое соответствует к каждому пикселю изображения, его просто представить как каждый нейрон, содержащий число, это число является значением яркости пикселя (0 для черных пикселей и 1 для белых пикселей), это число является его активацией. Значение 1 означает, что нейрон «активирован». Так что на данный момент можно с уверенностью считать нейрон узлом, содержащим число от 0 до 1.

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

Итак, давайте возьмем распознавание на человеческий уровень, как мы узнаем, что определенная цифра 8, 9 или 2? Мы разлагаем изображение на «функции» или «компоненты». Восьмерка — это две петли, наложенные друг на друга, так же как и четверка — это три линии, две вертикальные и одна горизонтальная. Наше лучшее предположение о том, что делают эти скрытые слои, — это попытаться смоделировать эти функции для некоторого определенного набора нейронов. Точно так же, как каждый нейрон во входном слое соответствует пикселю, в скрытом будут некоторые определенные нейроны, соответствующие этим компонентам или функциям. Это означает, что всякий раз, когда мы подаем в сеть число 9, в скрытом слое будет определенный набор нейронов, активация которых будет близка к единице. Это могут быть нейроны, которые активируются любым зацикленным паттерном, и нейроны, которые активируются вертикальными линиями, таким образом обобщая конкретную форму входного изображения до паттерна, который он находит на изображении. Таким образом, результат можно узнать, просто зная, какая функция или компонент были найдены на изображении.

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

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

Ранее мы обсуждали, что каждый нейрон получает входные данные от каждого нейрона предыдущего слоя. Каждый нейрон получает взвешенную сумму всех активаций предыдущего слоя.
Рассмотрим этот пример нейронной сети с конфигурацией 5 ➔ 8 ➔ 2.

Нейрон b1 получает входные данные от a1, a2, a3, a4 и a4. сильный>a5. С каждым соединением связан вес, например w1, w2, w3, w4, w5. . Таким образом, нейрон b1 получает взвешенную сумму всех активаций из первого слоя. Итак, b1 сейчас выглядит так:

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

По сути, это S-образная кривая, которая падает к 0, когда значения приближаются к более высокому отрицательному числу, и постепенно поднимается к 1, когда мы приближаемся к более высоким положительным значениям. Математически сигмоида выражается как:

Сигмоид прост и прямолинеен, но имеет несколько проблем, таких как проблема исчезающего градиента, вывод не центрируется на нуле и т. д. Многие другие функции активации, такие как tanh, ReLU, ELU и т. д., заменили сигмоид в более новых моделях. Но пока остановимся на сигмовидной. После применения этой функции активации мы получили новое значение для b1.

Теперь предположим, что по каким-то причинам мы хотим сдвинуть нашу функцию активации влево или вправо. Но почему кто-то хочет сделать это правильно? Что ж, ответ заключается в том, чтобы лучше соответствовать нашим данным. Давайте разберемся на примере линии, представленной как y=mx, линия проходит через начало координат. Не всегда может быть так, что эта линия будет правильно соответствовать нашим данным, поэтому мы добавляем к ней пересечение по оси y, уравнение становится y=mx+b. По аналогичным причинам мы также хотели бы изменить нашу функцию активации, возможно, когда мы хотим, чтобы наш нейрон активировался, когда взвешенная сумма больше 20, а не ноль, мы можем добавить -20 к взвешенной сумме. Это число является нашей предвзятостью (прочитайте этот пост на форуме, чтобы понять, почему нам нужны предвзятости в контексте функций активации). Таким образом, веса говорят о том, какой паттерн нас должен интересовать, а смещение говорит о том, насколько высокой должна быть взвешенная сумма весов, чтобы нейрон стал значимо активным.
Добавляя это смещение в нашу функцию, мы получаем

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

Для общего сценария мы можем записать это как

Здесь a⁰ представляет нейроны из первого слоя, а затем представляет нейрон из следующего слоя с n и k количество нейронов в этом слое соответственно. представляет смещения, связанные с каждым нейроном в первом слое. Прямой переход от слоя N-1 к следующему слою N может быть представлен как одно изящное выражение

Благодаря этому становится невероятно легко писать код для нейронных сетей, все, что нам нужно, — это достаточно хорошая математическая библиотека, которая может выполнять матричное умножение. Но как насчет обучения? У нас должен быть какой-то механизм для регулировки весов, в нашем примере 784 нейрона во входном слое, 16 в последующем слое и 10 в выходном слое, это 12 730 параметров для игры. В следующем разделе рассказывается, как мы настраиваем эти 12 730 параметров или, скорее, как сеть настраивает эти параметры.

Процесс изучения

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

Выходной слой имеет 10 нейронов, поэтому на выходе мы получим вектор для 10 чисел со значениями в диапазоне от 0 до 1. Для цифры 4 ожидаемый результат: [0.0, 0.0, 0.0, 0.0, 1,0, 0,0, 0,0, 0,0, 0,0, 0,0]

Теперь давайте предположим, что мы начали со случайных весов и смещений, когда пытаемся передать изображение для идентификации нашей сети. Ну, мы получим очень плохой результат, который явно не будет похож на то, что мы ожидали. например: [0,53, 0,72, 0,52, 0,73, 0,56, 0,23, 0,14, 0,01, 0,43, 0,35]

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

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

Из-за этих модных словечек и всей шумихи они звучат сложно, но как только вы попытаетесь понять их лучше, вы поймете, что все сводится к исчислению.

Исчисление средней школы, если вы хотите минимизировать функцию, просто приравняйте ее производную первого порядка к нулю, найдите значения ваших переменных функции, и все готово, эти значения являются значениями параметров, которые имеют минимальную ошибку или потерю. Единственная проблема заключается в том, что мы имеем дело не с простой линейной функцией, а с функцией с более чем 12 000 параметров. Так что подход, приравнивающий производную первого порядка к нулю, не сработает. Вместо этого мы делаем то, что называется Градиентный спуск. Я настоятельно рекомендую этот блог Понимание градиентного спуска для всестороннего объяснения этого. По сути, мы начинаем с точки в пространстве весов (поверхностный график зависимости весов от потерь) и находим производную функции по каждому из весов сети, а затем корректируем веса в направлении отрицательного наклона. Что мы на самом деле делаем, так это пытаемся выяснить, в каком направлении функция потерь больше всего наклоняется вниз (относительно изменения параметров), и делаем небольшой шаг в этом направлении. Градиент функции дает нам направление наибольшего увеличения, т. е. то, в каком направлении функция увеличивается, вполне естественно, что движение в противоположном направлении, вероятно, даст нам направление, в котором функция уменьшается. Поэтому мы делаем шаг, который приводит к наибольшему изменению функции потерь. Изменение весов и смещений путем выполнения каждого шага в направлении минимумов, т. е. установка их на значение параметров в конечной точке шага, начинает подгонять наш вывод к примерам. Но для миллионов примеров делать это для каждого примера не имеет смысла, кроме переобучения как основного побочного эффекта, это слишком затратно в вычислительном отношении. Итак, мы делаем это на нескольких обучающих примерах. Это известно как Пакетный градиентный спуск. Чрезвычайно сложно понять это для поверхности размером 12 730, поэтому способ увидеть это непространственным способом состоит в том, чтобы увидеть эти веса как вектор W, и аналогичным образом можно представить отрицательный градиент этих весов. как −∇(W) Теперь каждое значение этого вектора представляет, насколько мы должны подтолкнуть наши веса, отрицательное значение подразумевает уменьшение значения, а положительный вес подразумевает увеличение значений. Кроме того, относительные величины этих компонентов говорят нам, какие изменения окажут большее влияние на функцию затрат.

Маленькие признания

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

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

У меня есть еще один грязный маленький секрет. Технически это не нейронная сеть, а ее разновидность. Это многоуровневый персептрон, представляющий собой довольно примитивную, рудиментарную архитектуру, но являющуюся фундаментальной частью предметной области. Нейронная сеть — это скорее общий термин для объединения всех фокусов под одним названием.

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

Вернемся к обучению

Процесс обучения — продолжение

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

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

Выходной нейрон — a(L), нейрон перед ним — a(L−1) и так далее. Здесь желаемый выход (скажем, y) нейрона равен 1. Однако мы получили выход как 0,66, поэтому стоимость может быть указана следующим образом

Небольшое изменение веса W(L) задается как ∂w(L). Теперь нам нужно выяснить, насколько сильно это изменение повлияет на стоимость C0. т. е. нам нужно найти производную C0 по W(L). Для этого давайте посмотрим, как функции влияют друг на друга.
Если мы попытаемся наметить параметры, какие параметры на что влияют, для этого случая мы можем построить какое-то дерево.

Мы видим, что небольшое изменение W(L) вызывает небольшое изменение z(L), что, в свою очередь, вызывает небольшое изменение a(L) и, наконец, стоимость C0. Итак, мы разделяем вещи следующим образом:

Применение цепного правила и умножение этих отношений дает нам чувствительность C0 к небольшим изменениям W(L).

Теперь давайте вычислим каждую производную

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

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

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

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

Здесь функция стоимости будет суммой ошибок обоих выходных нейронов.

Обобщая это, получаем

и

Это сумма по всему слою L, это сделано потому, что нейрон i слоя L-1 влияет на оба нейрона в Л. Итак, мы добавляем их. Как только мы узнаем, насколько чувствительна функция стоимости к активациям на предпоследнем слое, мы можем просто повторить процесс для всех весов и смещений, поступающих в этот слой. Таким образом, производная каждой компоненты ∇C помогает нам спускаться по градиенту и находить минимумы, многократно спускаясь с холма.

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

А это, друзья мои, нейронная сеть.
На создание этой статьи меня вдохновила Серия глубокого обучения 3Blue1Brown.

использованная литература

Нейронные сети и глубокое обучение
Пошаговый пример обратного распространения ошибки
CS 224D: Глубокое обучение для NLP1, преподаватель курса: Ричард Сочер
Глоссарий глубокого обучения
> Книга: Главный алгоритм
cs.stanford.edu

Подробнее Чтение

Как выбрать количество скрытых слоев
Как обучаются нейронные сети
Получение обратного распространения ошибки
Понятие градиентного спуска