Нейронная сеть это система уравнений

От регрессии к нейронным сетям (From regression to neural networks)

Tags: Регрессия модель нейрона функция активации архитектура сети алгоритм обучения оптимизация функции потерь

Введение

Известна функциональная зависимость между температурой по Цельсию (C )и Фаренгейту (F ):

F = 1.8* C + 32.

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

Эту же задачу можно решить с помощью нейронной сети.

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

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

Интерпретируемость важна, например, для модели, которая предсказывает наличие заболевания. Здесь нас интересует не только точный результат, но и «логика», стоящая за ним.

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

Нейронная сеть определяется рядом сущностей, основные из которых:

  • модель нейрона;
  • функция активации;
  • архитектура сети;
  • алгоритм обучения;
  • функция потерь.

Модель нейрона

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

Нейрон имеет n входов xi с весами wi и единичный вход (смещение) с весом b. Выход Y рассчитывается через функцию активации от суммы воздействия все входов.

Смещение b используется для того, чтобы иметь возможность получать выходной результат, путем сдвига графика функции активации вправо или влево относительно 0. Смещение еще называют порогом воздействия (threshold)

Функция активации

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

Без функции активации значение выхода Y может быть в диапазоне -∞ N , Y N ) (где k = 1, 2, …, N) и способ вычисления функции ошибки E, то обучение нейронной сети превращается в задачу многомерной оптимизации, имеющую очень большую размерность. При этом, поскольку функция E может иметь произвольный вид, обучение в общем случае – многоэкстремальная невыпуклая задача оптимизации.

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

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

Градиентный спуск остается самым популярным методом поиска оптимума, хотя существует множество альтернативных подходов.

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

Проблемы, связанные с этим:

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

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

Наборы данных Training/Validation/Test

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

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

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

Нейронные сети с Numpy для абсолютных начинающих. Часть 2. Линейная регрессия

Дата публикации Jan 25, 2019

В предыдущем уроке вы получили очень краткий обзор персептрона.

Нейронные сети с NumPy для абсолютных начинающих: введение

В этом уроке вы получите краткое представление о том, что такое нейронные сети и как они были разработаны. В…

towardsdatascience.com

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

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

Прежде чем мы начнем, помните, что мы использовали функцию активации порога, чтобы имитировать функцию вентилей AND и NOR ?!

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

Давайте выясним чудеса, которые может сделать эта функция активации!

Функция линейной активации

Давайте предположим, что есть только один вход и смещение на персептрон, как показано ниже:

Результирующий линейный результат (т.е. сумма) будет

, Это уравнение прямой линии, как показано на рисунке ниже.

Здесь следует отметить, что когда функция активации не используется, мы можем сказать, что функция активации является линейной.

Этомногомерное (несколько переменных) линейное уравнение.

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

Линейная регрессия

Подгонка линейного уравнения к заданному набору данных вNпространство называетсяЛинейная регрессия, На изображении ниже GIF показан пример линейной регрессии.

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

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

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

Давайте узнаем это на ходу!

Давайте начнем с импорта необходимых пакетов.

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

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

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

Ваша первая задача будет импортировать или генерировать данные. В этом руководстве вы создадите набор данных, используя sklearn «s make_regression функция.

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

Теперь пришло время визуализировать, что приготовил генератор данных!

Давайте проверим форму векторов на согласованность.

Нам нужно сбросить размерYв (200, 1) чтобы мы не получили ошибок при умножении вектора.

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

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

В нашем случае тренировочный набор составляет 80%, а тестовый набор — 20%.

Давайте проверим форму созданных наборов данных Train и Test.

Как видите, 80% данных, то есть 80% из 200 точек данных — это 160, что является правильным.

Итак, чего мы достигли до сих пор?

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

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

Поэтому следующим шагом является случайноегенерировать линию со случайным наклоном и пересечением (смещение), Цель состоит в том, чтобы добиться наилучшего соответствия линии.

Теперь, учитываям&б, мы можем построить сгенерированную линию.

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

Поскольку линия теперь сгенерирована, вам нужно предсказать значения, которые она производит для данного значенияИкс, Из этого значения все, что нужно сделать, — это рассчитать среднеквадратичную ошибку. Зачем?

Как мы можем найти разницу между фактическим и прогнозируемым результатом?

Простейшим способом было бы просто вычесть эти два различия. У нас есть случайная строка, которая дает вывод y_pred для каждогоИксэто дано, но это, конечно, не фактический результат. К счастью, у нас есть фактический результат всехИксслишком! Итак, что мы делаем, вместо того, чтобы брать разницу напрямую (которая технически называется абсолютным расстоянием или расстоянием L1), мы возводим ее в квадрат (называемую евклидовым расстоянием или расстоянием L2) и берем среднее значение для всех заданных точек, и это называетсяСредняя квадратическая ошибка,

Давайте теперь предсказать значения y_pred из параметровм&бучитывая точки данных X_train определив функцию forward_prop ,

Функция Стоимость / Потеря

Как упоминалось ранее, теперь, когда у вас есть оба соответствующих значения для X_train и прогнозируемые значения для y_pred Вы рассчитаете функцию Стоимость / Ошибка / Потеря.

Потеря (средняя квадратическая ошибка)является:

Подводя итогиMпримеры, мы получаемПотеря фн.как показано ниже:

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

Давайте теперь кодифицировать это.

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

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

Вы визуализируете линию, созданную из параметровма такжеб,

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

Градиентный спуск для линейной регрессии

Поскольку,потеряявляется зависимой переменной им&бнезависимые переменные, нам придется обновитьм&бчтобы найти минимумпотеря,

Итак, ближайший вопрос будет .

Как я могу обновить параметрыма такжеб?

Давайте рассмотрим, например, только один параметрпкак показано ниже, и пустьT(Целевой)быть значением, которое должно быть предсказано. Мы видим это какСтоимостьсходится к минимуму, параметрпдостигает определенного значения, называемого оптимальным значением. Скажем, оптимальное значениепявляется ,

Градиентный спуск w.r.tп.[Ссылка на источник]

Вы можете сделать несколько наблюдений из этого графика.

Из графика видно, что какпдвижется в сторону Стоимость уменьшается, и, по мере удаления от нее, стоимость увеличивается.

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

Давайте рассмотримпкривой. Из исчисления мы знаем, чтоскаткривой в точке определяется какdг /dИкс(вотdL /dп гдеL → Потеря). Из рис., Когдапнаходится справа от ,скаточевидно-veи когда это направо,скатбыло быанолита, Но мы видим, что еслипнаходится слева от некоторое значение должно быть добавлено кп, Аналогично, некоторое значение должно быть вычтено, когдапнаходится справа от ,

Это означает, что когдаскат является -ve подразумеваетp = p + (некоторые значения)и когдаскатявляетсяанолита подразумеваетp = p — (некоторые значения)двигаться навстречу ,

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

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

То, что мы наконец-то получим, будет

А на рисунке показана траектория движенияппротивСтоимостьэто кривая Белла.

Этот метод называетсяГрадиентный спуск,

В нашем случае мы используем два параметрама такжеб, Таким образом, кривая Белла будет3-размер, как показано на рисунке ниже.

Как уже упоминалось, вы будете вычислять частную производную функции потерь с помощью параметров.м&б, [Заметка:Обычно ожидается, что вы знаете основные понятия частных производных. Однако, если вы этого не сделаете, вы можете сослаться на это замечательноХан Академия видео]

Пишем свою нейросеть: пошаговое руководство

Отличный гайд про нейросеть от теории к практике. Вы узнаете из каких элементов состоит ИНС, как она работает и как ее создать самому.

Если вы в поисках пособия по искусственным нейронным сетям (ИНС), то, возможно, у вас уже имеются некоторые предположения относительно того, что это такое. Но знали ли вы, что нейронные сети — основа новой и интересной области глубинного обучения? Глубинное обучение — область машинного обучения, в наше время помогло сделать большой прорыв во многих областях, начиная с игры в Го и Покер с живыми игроками, и заканчивая беспилотными автомобилями. Но, прежде всего, глубинное обучение требует знаний о работе нейронных сетей.


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

1 Что такое искусственная нейросеть?

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

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

Неконтролируемое обучение в ИНС пытается «заставить» ИНС «понять» структуру передаваемой входной информации «самостоятельно». Мы не будем рассматривать это в данном посте.

2 Структура ИНС

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

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

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

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

2.2 Узлы

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

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

Что такое «вес»? По весу берутся числа (не бинарные), которые затем умножаются на входе и суммируются в узле. Иными словами, взвешенный вход в узел имеет вид:

где wi— числовые значения веса ( b мы будем обсудим позже). Весы нам нужны, они являются значениями, которые будут меняться в течение процесса обучения. b является весом элемента смещения на 1, включение веса b делает узел гибким. Проще это понять на примере.

2.3 Смещение

Рассмотрим простой узел, в котором есть по одному входу и выходу:

Ввод для активационной функции в этом узле просто x1w1. На что влияет изменение в w1 в этой простой сети?

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

Из графика можно увидеть, что меняя «вес» смещения b, мы можем изменять время запуска узла. Смещение очень важно в случаях, когда нужно имитировать условные отношения.

2.4 Составленная структура

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

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

2.5 Обозначение

Вся математика, приведенная выше, требует очень точной нотации. Нотация, которая используется здесь, используется и в руководстве по глубинному обучению от Стэнфордского Университета. В следующих уравнениях вес соответствующего связи будет обозначаться как w ij (l) , где i — номер узла в слое l+1, а j- номер узла в слое l. Например, вес связи между узлом 1 в слое 1 и узлом 2 в слое 2 будет обозначаться как w 21 (l) . Непонятно, почему индексы 2-1 означают связь 1-2? Такая нотация более понятна, если добавить смещения.

Из графика выше видно, что смещение 1 связано со всеми узлами в соседнем слое. Смещение в Ш1 имеет связь со всеми узлами в Ш2. Так как смещение не является настоящим узлом с активационной функцией, оно не имеет и входов (его входное значение всегда равно константе). Вес связи между смещением и узлом будем обозначать через bi (l) , где i- номер узла в слое l+1, так же, как в w ij (l) . К примеру с w 21 (l) вес между смещением в Ш1 и вторым узлом в Ш2 будет иметь обозначение b2 (1) .

Помните, что эти значения -w ij (l) и bi (l) — будут меняться в течение процесса обучения ИНС.
Обозначение связи с исходным узлом будет выглядеть следующим образом: hj l , где j- номер узла в слое l. Тогда в предыдущем примере, связью с исходным узлом является h1 (2) .
Теперь давайте рассмотрим, как рассчитывать выход сети, когда нам известны вес и вход. Процесс нахождения выхода в нейронной сети называется процессом прямого распространения.

3 Процесс прямого распространения

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

, где f(∙) — активационная функция узла, в нашем случае сигмоидальная функция. В первой строке h1 (2) — выход первого узла во втором слое, его входами соответственно являются w11 (1) x1(1), w12 (1) x2(1),w13 (1) x3(1) и b1 (1) . Эти входы было сложены, а затем переданы в активационную функцию для расчета выхода первого узла. С двумя следующими узлами аналогично.

Последняя строка рассчитывает выход единого узла в последнем третьем слое, он является конечной исходной точкой в нейронной сети. В нем вместо взвешенных входных переменных (x1,x2,x3)берутся взвешенные выходы узлов с другой слоя (h1 (2) ,h2 (2) ,h3 (2) )и смещения. Такая система уравнений также хорошо показывает иерархическую структуру нейронной сети.

3.1 Пример прямого распространения

Приведем простой пример первого вывода нейронной сети языке Python . Обратите внимание, веса w11 (1) ,w12 (1) . между Ш1 и Ш2 идеально могут быть представлены в матрице:

Представим эту матрицу через массивы библиотеки numpy.

Мы просто присвоили некоторые рандомные числовые значения весу каждой связи с Ш1. Аналогично можно сделать и с Ш2:

Мы можем присвоить некоторые значения весу смещения в Ш1 и Ш2:

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

3.2 Первая попытка реализовать процесс прямого распространения

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

Данная функция принимает в качестве входа номер слоя в нейронной сети, х — входной массив / вектор:

Функция сначала проверяет, чем является входной массив для соответствующего слоя с узлами / весами. Если рассматривается первый слой, то входом для второго слоя является входной массив xx, Умноженный на соответствующие веса. Если слой не первый, то входом для последующего будет выход предыдущего.
Вызов функции:

возвращает результат 0.8354. Можно проверить правильность, вставив те же значения в систему уравнений:

3.3 Более эффективная реализация

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

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

3.4 Векторизация в нейронных сетях

Можно более компактно написать предыдущие уравнения, тем самым найти результат эффективнее. Сначала добавим еще одну переменную zi (l) , которая является суммой входа в узел i слоя l, Включая смещение. Тогда для первого узла в Ш2, z будет равна:

, где n- количество узлов в Ш1. Используя это обозначение, систему уравнений можно сократить:

Обратите внимание на W, что означает матричную форму представления весов. Помните, что теперь все элементы в уравнении сверху являются матрицами / векторами. Но на этом упрощение не заканчивается. Данные уравнения можно свести к еще более краткому виду:

Так выглядит общая форма процесса прямого распространения, выход слоя l становится входом в слой l+1. Мы знаем, что h (1) является входным слоем x, а h (nl) (где nl- номер слоя в сети) является исходным слоем. Мы также не стали использовать индексы i и j-за того, что можно просто перемножить матрицы — это даст нам тот же результат. Поэтому данный процесс и называется «векторизацией». Этот метод имеет ряд плюсов. Во-первых, код его реализации выглядит менее запутанным. Во-вторых, используются свойства по линейной алгебре вместо циклов, что делает работу программы быстрее. С numpy можно легко сделать такие подсчеты. В следующей части быстро повторим операции над матрицами, для тех, кто их немного подзабыл.

3.5 Умножение матриц

Распишем z (l+1) =W (l) h (l) +b (l) на выражение из матрицы и векторов входного слоя ( h (l) =x):

Для тех, кто не знает или забыл, как перемножаются матрицы. Когда матрица весов умножается на вектор, каждый элемент в строке матрицы весов умножается на каждый элемент в столбце вектора, после этого все произведения суммируются и создается новый вектор (3х1). После перемножения матрицы на вектор, добавляются элементы из вектора смещения и получается конечный результат.

Каждая строка полученного вектора соответствует аргументу активационной функции в оригинальной НЕ матричной системе уравнений выше. Это означает, что в Python мы можем реализовать все, не используя медленные циклы. К счастью, библиотека numpy дает возможность сделать это достаточно быстро, благодаря функциям-операторам над матрицами. Рассмотрим код простой и быстрой версии функции simple_looped_nn_calc:

Обратите внимание на строку 7, в которой происходит перемножение матрицы и вектора. Если вместо функции умножения a.dot (b) вы используете символ *, то получится нечто похожее на поэлементное умножение вместо настоящего произведения матриц.

Если сравнить время работы этой функции с предыдущей на простой сети с четырьмя слоями, то мы получим результат лишь на 24 микросекунды меньше. Но если увеличить количество узлов в каждом слое до 100-100-50-10, то мы получим гораздо большую разницу. Функция с циклами в этом случае дает результат 41 миллисекунду, когда у функции с векторизацией это занимает лишь 84 микросекунды. Также существуют еще более эффективные реализации операций над матрицами, которые используют пакеты глубинного обучения, такие как TensorFlow и Theano.

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

4 Градиентный спуск и оптимизация

Расчеты значений весов, которые соединяют слои в сети, это как раз то, что мы называем обучением системы. В контролируемом обучении идея заключается в том, чтобы уменьшить погрешность между входом и нужным выходом. Если у нас есть нейросеть с одним выходным слоем и некоторой вход xx и мы хотим, чтобы на выходе было число 2, но сеть выдает 5, то нахождение погрешности выглядит как abs(2-5)=3. Говоря языком математики, мы нашли норму ошибки L 1 (Это будет рассмотрено позже).

Смысл контролируемого обучения в том, что предоставляется много пар вход-выход уже известных данных и нужно менять значения весов, основываясь на этих примерах, чтобы значение ошибки стало минимальным. Эти пары входа-выхода обозначаются как (x (1) ,y (1) ). (x (m) ,y (m) ), где m является количеством экземпляров для обучения. Каждое значение входа или выхода может представлять собой вектор значений, например x (1) не обязательно только одно значение, оно может содержать N-размерный набор значений. Предположим, что мы обучаем нейронную сеть выявлению спам-сообщений — в таком случае x (1) может представлять собой количество соответствующих слов, которые встречаются в сообщении:

y (1) в этом случае может представлять собой единое скалярное значение, например, 1 или 0, обозначающий, было сообщение спамом или нет. В других приложениях это также может быть вектор с K измерениями. Например, мы имеем вход xx, Который является вектором черно-белых пикселей, считанных с фотографии. При этом y может быть вектором с 26 элементами со значениями 1 или 0, обозначающие, какая буква была изображена на фото, например (1,0. 0)для буквы а, (0,1. 0) для буквы б и т. д.

В обучении сети, используя (x,y), целью является улучшение нахождения правильного y при известном x. Это делается через изменение значений весов, чтобы минимизировать погрешность. Как тогда менять их значение? Для этого нам и понадобится градиентный спуск. Рассмотрим следующий график:

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

Сначала находится градиент погрешности на «1» по отношению к w. Градиент является уровнем наклона кривой в соответствующей точке. Он изображен на графике в виде черных стрелок. Градиент также дает некоторую информацию о направлении — если он положителен при увеличении w, то в этом направлении погрешность будет увеличиваться, если отрицательный — уменьшаться (см. График). Как вы уже поняли, мы пытаемся сделать, чтобы погрешность с каждым шагом уменьшалась. Величина градиента показывает, как быстро кривая погрешности или функция меняется в соответствующей точке. Чем больше значение, тем быстрее меняется погрешность в соответствующей точке в зависимости от w.

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

, где wн означает новое значение w, wст— текущее или «старое» значение w, ∇error является градиентом погрешности на wст и α является шагом. Шаг α также будет означать, как быстро ответ приближается к минимальной погрешности. При каждой итерации в таком алгоритме градиент должен уменьшаться. Из графика выше можно заметить, что с каждым шагом градиент «стихает». Как только ответ достигнет минимального значения, мы уходим из итеративного процесса. Выход можно реализовать способом условия «если погрешность меньше некоторого числа». Это число называют точностью.

4.1 Простой пример на коде

Рассмотрим пример простой имплементации градиентного спуска для нахождения минимума функции f(x)=x 4 -3x 3 +2 на языке Python . Градиент этой функции можно найти аналитически через производную f»(x)=4x 3 -9x 2 . Это означает, что для любого xx мы можем найти градиент по этой простой формуле. Мы можем найти минимум через производную — x=2.25.

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

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

4.2 Функция оценки

Существует более общий способ изобразить выражения, которые дают нам возможность уменьшить погрешность. Такое общее представление называется функция оценки. Например, функция оценки для пары вход-выход (x z , y z ) в нейронной сети будет выглядеть следующим образом:

Выражение является функцией оценки учебного экземпляра zth, где h (nl) является выходом последнего слоя, то есть выход нейронной сети. h (nl) можно представить как yпyп, Что означает полученный результат, когда нам известен вход xz. Две вертикальные линии означают норму L 2 погрешности или сумму квадратов ошибок. Сумма квадратов погрешностей является довольно распространенным способом представления погрешностей в системе машинного обучения. Вместо того, чтобы брать абсолютную погрешность abs(ypred(x z )-y z ), мы берем квадрат погрешности. Мы не будем обсуждать причину этого в данной статье. 1/2 в начале просто константой, которая нормализует ответ после того, как мы продифференцируем функцию оценки во время обратного распространения.

Обратите внимание, что приведенная ранее функция оценки работает только с одной парой (x,y). Мы хотим минимизировать функцию оценки со всеми mm парами вход-выход:

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

4.3 Градиентный спуск в нейронных сетях

Градиентный спуск для каждого веса w(ij) (l) и смещение bi(l) в нейронной сети выглядит следующим образом:

Выражение выше фактически аналогично представлению градиентного спуска:
wnew=wold-α*∇error. Нет лишь некоторых обозначений, но достаточно понимать, что слева расположены новые значения, а справа — старые. Опять же задействован итерационный метод для расчета весов на каждой итерации, но на этот раз основываясь на функции оценки J(w,b).

Значения ∂/∂wij (l) и ∂/∂bi (l) являются частными производными функции оценки, основываясь на значениях веса. Что это значит? Вспомните простой пример градиентного спуска ранее, каждый шаг зависит от наклона погрешности / оценки по отношению к весу. Производная также имеет значение наклона / градиента. Конечно, производная обозначается как d/dx. x в нашем случае является вектором, а это значит, что наша производная тоже будет вектором, который является градиент каждого измерения x.

4.4 Пример двумерного градиентного спуска

Рассмотрим пример стандартного двумерного градиентного спуска. Ниже представлены диаграмму работы двух итеративных двумерных градиентных спусков:

Синим обозначены контуры функции оценки, они обозначают области, в которых значение погрешности примерно одинаковы. Каждый шаг (p1→p2→p3) В градиентном спуске используют градиент или производную, которые обозначаются стрелкой / вектором. Этот вектор проходит через два пространства [x1, x2][x1,x2]и показывает направление, в котором находится минимум. Например, производная, исчисленная в p1 может быть d/dx=[2.1,0.7], Где производная является вектором с двумя значениями. Частичная производная ∂/∂x1 в этом случае равна скаляру →[2.1]- иными словами, это значение градиента только в одном измерении поискового пространства (x1).

В нейронных сетях не существует простой полной функции оценки, с которой можно легко посчитать градиент, похожей на функцию, которую мы ранее рассматривали f(x)=x 4 -3x 3 +2). Мы можем сравнить выход нейронной сети с нашим ожидаемым значением y (z) , После чего функция оценки будет меняться из-за изменения в значениях веса, но как мы это сделаем со всеми скрытыми слоями в сети?

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

4.5 Углубляемся в обратное распространение

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

Сначала, давайте вспомним базовые уравнения для нейронной сети с тремя слоями из предыдущих разделов:

Выход этой нейронной сети находится по формуле:

Мы можем упростить это уравнение к h1(3)=f(z1 (2) ), добавив новое значение z1 (2) , которое означает:

Предположим, что мы хотим узнать, как влияет изменение в весе w12 (2) на функцию оценки. Это означает, что нам нужно вычислить ∂J/∂w12 (2) . Чтобы сделать это, нужно использовать правило дифференцирования сложной функции:

Если присмотреться, то правая часть полностью сокращается (по принципу 2552=22=1). ∂J∂w12(2) были разбиты на три множителя, два из которых можно прекрасно заменить. Начнем с ∂z1 (2) /∂w12 (2) :

Частичная производная z1 (2) по w12 (2) зависит только от одного произведения в скобках, w12(1)h2(2), Так как все элементы в скобках, кроме w12 (2) , не изменяются. Производная от константы всегда равна 1, а ∂/∂w12 (2) )сокращается до просто h2 (2) , Что является обычным выходом второго узла из слоя 2.

Следующая частичная производная сложной функции ∂h1 (3) /∂z1 (2) является частичной производной активационной функции выходного узла h1(3). Так что нам нужно брать производные активационной функции, следует условие ее включения в нейронные сети — функция должна быть дифференцированной. Для сигмоидальной активационной функции производная будет выглядеть так:

, где f(z)является самой активационной функцией. Теперь нам нужно разобраться, что делать с ∂J∂h1 (3) . Вспомните, что J(w,b,x,y) есть функция квадрата погрешности, выглядит так:

здесь y1 является ожидаемым выходом для выходного узла. Опять используем правило дифференцирования сложной функции:

Мы выяснили, как находить ∂J/∂w12 (2) по крайней мере для весов связей с исходным слоем. Перед тем, как перейти к одному из скрытых слоев, введем некоторые новые значения δ, чтобы немного сократить наши выражения:

, где i является номером узла в выходном слое. В нашем примере есть только один узел, поэтому i=1. Напишем полный вид производной функции оценки:

, где выходной слой, в нашем случае, l=2, а i соответствует номеру узла.

4.6 Распространение в скрытых слоях

Что делать с весами в скрытых слоях (в нашем случае в слое 2)? Для весов, которые соединены с выходным слоем, производная ∂J/∂h=-(yi-hi (nl) )имела смысл, т.к. функция оценки может быть сразу найдена через сравнение выходного слоя с существующими данными. Но выходы скрытых узлов не имеют подобных уже существующих данных для проверки, они связаны с функцией оценки только через другие слои узлов. Как мы можем найти изменения в функции оценки из-за изменений весов, которые находятся глубоко в нейронной сети? Как уже было сказано, мы используем метод обратного распространения.

Мы уже сделали тяжелую работу по правилу дифференцирования сложных функций, теперь рассмотрим все более графически. Значение, которое будет обратно распространяться, — δi (nl) , т.к. оно в ближайшей связи с функцией оценки. А что с узлом j во втором слое (скрытом слое)? Как он влияет на δi (nl) в нашей сети? Он меняет другие значения из-за веса wij (2) (см. диаграмму ниже, где j=1 i=1).

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

, где j номер узла в слое l. Но что будет, если в исходном слое находится много выходных узлов? В этом случае δj (l) находится по взвешенной сумме всех связанных между собой погрешностей, как показано на диаграмме ниже:

На рисунке показано, что каждое значение δ из исходного слоя суммируется для нахождения δ1 (2) , Но каждый выход δ должен быть взвешенным соответствующими значению wi1 (2) . Другими словами, узел 1 в слое 2 способствует изменениям погрешностей в трех выходных узлах, при этом полученная погрешность (или значение функции оценки) в каждом из этих узлов должна быть «передана назад» значению δ этого узла. Сформируем общее выражение значение δ для узлов в скрытом слое:
, где j является номером узла в слое l, i- номер узла в слое l+1(что аналогично обозначениям, которое мы использовали ранее). s(l+1)— это количество узлов в слое l+1.
Теперь мы знаем, как находить:
Но что делать с весами смещения? Принцип работы с ними аналогичный обычным весам, используя правила дифференцирования сложных функций:

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

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

4.7 Векторизация обратного распространения

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

Что представляет собой h (l) ? Все просто, вектор (sl×1), где sl является количеством узлов в слое l. Как тогда выглядит произведение h (l) δ (l+1) ? Мы знаем, что α×∂J/∂W (l) должно быть того же размера, что и матрица весов W(l), Мы также знаем, что результат h(l)δ (l+1) должен быть того же размера, что и матрица весов для слоя l. Иными словами, произведение должно быть размера (sl + 1× sl).

Мы знаем, что δ(l+1) имеет размер (sl+1×1), а h (l) — размер (sl×1). По правилу умножения матриц, если матрицу (n×m)умножить на матрицу (o×p), То мы получим матрицу размера (n×p). Если мы просто перемножим h(l) на δ(l+1), то количество столбцов в первом векторе (один столбец) не будет равно количеству строк во втором векторе (3 строки). Поэтому, для того, чтобы можно было умножить эти матрицы и получить результат размера (sl+1× sl), Нужно сделать трансформирование. Оно меняет в матрице столбцы на строки и наоборот (например матрицу вида (sl×1)на (1×sl)). Трансформирование обозначается как буква T над матрицей. Мы можем сделать следующее:

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

Еще одно трансформирование нужно сделать с суммой погрешностей в обратном распространении:

символ (∙) в предыдущем выражении означает поэлементное умножение (произведение Адамара), не является умножением матриц. Обратите внимание, что произведение матриц (((W (l) ) T δ(l+1))требует еще одного сложения весов и значений δ.

4.8 Реализация этапа градиентного спуска

Как тогда интегрировать векторизацию в этапы градиентного спуска нашего алгоритма? Во-первых, вспомним полный вид нашей функции оценки, который нам нужно сократить:

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

Это означает, что по прохождению через экземпляры обучения нам нужно иметь отдельную переменную, которая равна сумме частных производных функции оценки каждого экземпляра. Такая переменная соберет в себе все значения для «глобального» подсчета. Назовем такую «суммированную» переменную ΔW (l) . Соответствующая переменная для смещения будет обозначаться как Δb (l) . Следовательно, при каждой итерации в процессе обучения сети нам нужно будет сделать следующие шаги:

Выполняя эти операции на каждой итерации, мы подсчитываем упомянутую ранее сумму Σ m z= 1∂/∂W (l) J( w , b , x (z) , y (z) )(и аналогичная формула для b). После того, как будут проитерированы все экземпляры и получены все значения δ, мы обновляем значения параметров веса:

4.9 Конечный алгоритм градиентного спуска

И, наконец, мы пришли к определению метода обратного распространения через градиентный спуск для обучения наших нейронных сетей. Финальный алгоритм обратного распространения выглядит следующим образом:
Рандомная инициализация веса для каждого слоя W (l) . Когда итерация (l) б. Найдите значение δ ( nl) выходного слоя. Обновите ΔW (l) и Δb ( l ) для каждого слоя.
03. Запустите процесс градиентного спуска, используя:

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

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

5 Имплементация нейросети языке Python

В предыдущем разделе мы рассмотрели теорию по обучению нейронной сети через градиентный спуск и метод обратного распространения. В этом разделе мы используем полученные знания на практике — напишем код, который прогнозирует, основываясь на данных MNIST. База данных MNIST — это набор примеров в нейронных сетях и глубинном обучении. Она включает в себя изображения цифр, написанных от руки, с соответствующими ярлыками, которые объясняют, что это за число. Каждое изображение размером 8х8 пикселей. В этом примере мы используем сети данных MNIST для библиотеки машинного обучения scikit learn в языке программирования Python . Пример такого изображения можно увидеть под кодом:

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

01. Масштабировать данные.
02. Разделить данные на тесты и учебные тесты.

5.1 Масштабирование данных

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

Заметили ли вы, что входные данные меняются в интервале от 0 до 15? Достаточно распространенной практикой является масштабирование входных данных так, чтобы они были только в интервале от [0, 1], или [1, 1]. Это делается для более легкого сравнения различных типов данных в нейронной сети. Масштабирование данных можно легко сделать через библиотеку машинного обучения scikit learn:

Стандартный инструмент масштабирования в scikit learn нормализует данные через вычитание и деление. Вы можете видеть, что теперь все данные находятся в интервале от -2 до 2. По же на счет выходных данных yy, то обычно нет необходимости их масштабировать.

5.2 Создание тестов и учебных наборов данных

В машинном обучении появляется такой феномен, который называется «переобучением». Это происходит, когда модели, во время учебы, становятся слишком запутанными — они достаточно хорошо обучены, но когда им передаются новые данные, которые они никогда на «видели», то результат, который они выдают, становится плохим. Иными словами, модели генерируются не очень хорошо. Чтобы убедиться, что мы не создаем слишком сложные модели, обычно набор данных разбивают на учебные наборы и тестовые наборы. Учебный набором данных, на которых модель будет учиться, а тестовый набор — это данные, на которых модель будет тестироваться после завершения обучения. Количество учебных данных должно быть всегда больше тестовых данных. Обычно они занимают 60-80% от набора данных.

Опять же, scikit learn легко разбивает данные на учебные и тестовые наборы:

В этом случае мы выделили 40% данных на тестовые наборы и 60% соответственно на обучение. Функция train_test_split в scikit learn добавляет данные рандомно в различные базы данных — то есть, функция не берет первые 60% строк для учебного набора, а то, что осталось, использует как тестовый.

5.3 Настройка выходного слоя

Для того, чтобы получать результат — числа от 0 до 9, нам нужен выходной слой. Более-менее точная нейросеть, как правило, имеет выходной слой с 10 узлами, каждый из которых выдает число от 0 до 9. Мы хотим научить сеть так, чтобы, например, при цифре 5 на изображении, узел с цифрой 5 в исходном слое имел наибольшее значение. В идеале, мы бы хотели иметь следующий вывод: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]. Но на самом деле мы можем получить что-то похожее на это: [0.01, 0.1, 0.2, 0.05, 0.3, 0.8, 0.4, 0.03, 0.25, 0.02]. В таком случае мы можем взять крупнейших индекс в исходном массиве и считать это нашим полученным числом.

В данных MNIST нужны результаты от изображений записаны как отдельное число. Нам нужно конвертировать это единственное число в вектор, чтобы его можно было сравнивать с исходным слоем с 10 узлами. Иными словами, если результат в MNIST обозначается как «1», то нам нужно его конвертировать в вектор: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]. Такую конвертацию осуществляет следующий код:

Этот код конвертирует «1» в вектор [0, 1, 0, 0, 0, 0, 0, 0, 0, 0].

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

Следующим шагом является создание структуры нейронной сети. Для входного слоя, мы знаем, что нам нужно 64 узла, чтобы покрыть 64 пикселей изображения. Как было сказано ранее, нам нужен выходной слой с 10 узлами. Нам также потребуется скрытый слой в нашей сети. Обычно, количество узлов в скрытых слоях не менее и не больше количества узлов во входном и выходном слоях. Объявим простой список на языке Python , который определяет структуру нашей сети:

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

Сейчас мы не имеем никакого представления, как выглядит наша нейросеть. Как мы будем ее учить? Вспомним наш алгоритм из предыдущих разделов:
Рандомно инициализируем веса для каждого слоя W (l) Когда итерация (l) б. Найдите значение δ ( nl) выходного слоя. Обновите ΔW (l) и Δb ( l ) для каждого слоя.
03. Запустите процесс градиентного спуска, используя:

Значит первым этапом является инициализация весов для каждого слоя. Для этого мы используем словари в языке программирования Python (обозначается через <>). Рандомные значения предоставляются весам для того, чтобы убедиться, что нейросеть будет работать правильно во время обучения. Для рандомизации мы используем random_sample из библиотеки numpy. Код выглядит следующим образом:

Следующим шагом является присвоение двум переменным ΔW и Δb нулевых начальных значений (они должны иметь такой же размер, что и матрицы весов и смещений)

Далее запустим процесс прямого распространения через нейронную сеть:

И наконец, найдем выходной слой δ (nl) и значение δ (l) в скрытых слоях для запуска обратного распространения:

Теперь мы можем соединить все этапы в одну функцию:

Функция сверху должна быть немного объяснена. Во-первых, мы не задаем лимит работы градиентного спуска, основываясь на изменениях или точности функции оценки. Вместо этого, мы просто запускаем её с фиксированным числом итераций (3000 в нашем случае), а затем наблюдаем, как меняется общая функция оценки с прогрессом в обучении. В каждой итерации градиентного спуска, мы перебираем каждый учебный экземпляр (range (len (y)) и запускаем процесс прямого распространения, а после него и обратное распространение. Этап обратного распространения является итерацией через слои, начиная с выходного слоя к началу — range (len (nn_structure), 0, 1). Мы находим среднюю оценку на исходном слое (l == len (nn_structure)). Мы также обновляем значение ΔW и Δb с пометкой tri_W и tri_b, для каждого слоя, кроме исходного (исходный слой не имеет никакого связи, который связывает его со следующим слоем).

И наконец, после того, как мы прошлись по всем учебным экземплярам, накапливая значение tri_W и tri_b, мы запускаем градиентный спуск и меняем значения весов и смещений:

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

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

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

5.5 Оценка точности модели

Теперь, после того, как мы научили нашу нейросеть MNIST, мы хотим увидеть, как хорошо она работает на тестах. Дан входной тест (64 пикселя), нам нужно получить вывод нейронной сети — это делается через запуск процесса прямого распространения через сеть, используя наши полученные значения веса и смещения. Как было сказано ранее, мы выбираем результат выходного слоя через выбор узла с максимальным выводом. Для этого можно использовать функцию numpy.argmax, она возвращает индекс элемента массива с наибольшим значением:

Теперь, наконец, мы можем оценить точность результата (процент раз, когда сеть выдала правильный результат), используя функцию accuracy_score из библиотеки scikit learn:

Мы получили результат 86% точности. Звучит довольно неплохо? На самом деле, нет, это довольно низкая точностью. В наше время точность алгоритмов глубинного обучения достигает 99.7%, мы немного отстали.


источники:

http://www.machinelearningmastery.ru/neural-networks-with-numpy-for-absolute-beginners-part-2-linear-regression-e53c0c7dea3a/

http://proglib.io/p/neural-nets-guide