Как создать нейросеть на компьютере

Обновлено: 06.07.2024

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

Как я говорил в прошлый раз , нам понадобятся данные для обработки. Будут они числовые. Сразу их приведём к виду "от нуля до одного". Я взял для общего случая 5 параметров со случайными значениями. В качестве фактического результата я беру простую сумму этих параметров с некоторыми случайно заданными коэффициентами . Выходит примерно так:

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

Расчёты мы будем проводить с помощью макросов. Для этого можно нажать Alt+F11 и в любом доступном месте написать следующий код:

Public Const step = 0.1
Public d As Worksheet
Public o As Worksheet
Sub Calc(T As Boolean)
Set d = IIf(T, ThisWorkbook.Sheets("Data"), ThisWorkbook.Sheets("Test"))
Set o = ThisWorkbook.Sheets("Out")

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

Результат первого расчёта необученной нейронной сети Результат первого расчёта необученной нейронной сети

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

Sub Teach()
Dim i As Integer, j As Integer
Set d = ThisWorkbook.Sheets("Data")
Set o = ThisWorkbook.Sheets("Out")
Function res(r As Integer, j As Integer, de As Double)
res = 0
Dim w(1 To 5) As Double
For i = 1 To 5
w(i) = o.Cells(2, 1 + i) + IIf(j = i, de, 0) * step
res = res + d.Cells(r, 1 + i) * w(i)
Next i

В данном случае функция res выдаёт нам результат функции для входящих данных с номером r при изменении веса номер j на величину de , умноженную на параметр step , который играет роль скорости обучения. Если step будет очень маленьким, то наша нейронная сеть будет обучаться очень медленно , что потребует существенных затрат времени и мощностей компьютера. Если step очень большое, то мы можем столкнуться с ситуацией, что будем бесконечно "перепрыгивать" правильное решение , из-за чего никогда не обучим нашу сеть. Поэтому подбирать этот параметр надо более менее разумно.

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

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

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

Sub run()
For i = 1 To 25
Call Calc(True)
Call Teach
Next i
End Sub

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

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

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

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

В этом суть нейросетей: долгая и кропотливая работа ради эффектной концовки.

Что сделаем

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

Как собрать и настроить такой датасет — расскажем в другой раз.

Что понадобится

Python версии 3.8 и выше, обязательно под архитектуру x64. Если взять 32-разрядную версию, то нужная в проекте библиотека tensorflow работать не будет. Мы использовали версию 3.9.7.

Остальное установим в процессе. Главное — рабочий Python (по ссылке — как его установить).

👉 Все команды, которые есть в проекте, мы будем запускать в командной строке. Чтобы не было ошибок и затыков, лучше всего запустить её от имени администратора (в Windows) или с правами суперпользователя root (в Mac OS и Linux).

Создаём виртуальное окружение

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

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

pip install --upgrade virtualenv

Запускаем нейросеть на домашнем компьютере

Теперь можно устанавливать окружение. Для этого придумаем ему название — мы выбрали tell-me, но вы можете выбрать любое другое:

virtualenv --system-site-packages tell-me

Запускаем нейросеть на домашнем компьютере

source tell-me/bin/activate (если у вас мак или линукс)
tell-me\scripts\activate (если у вас виндоус)

Эта команда создаст папку на компьютере (путь к ней можно посмотреть на предыдущем скриншоте на третьей строке, параметр «dest») и запустит в ней виртуальное окружение:

Запускаем нейросеть на домашнем компьютере

Устанавливаем tensorflow

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

Для установки пишем команду:

pip install tensorflow

Запускаем нейросеть на домашнем компьютере

pip — это программа, которая отвечает в Python за скачивание, установку и обновление библиотек и вспомогательных пакетов. Это как магазин приложений Apple, только для командной строки и для разработчиков.

Чтобы убедиться, что библиотека установилась правильно и работает штатно, проверим её простым тестом.

1. Пишем команду:

2. Начало командной строки поменялось на >>> — это значит, питон готов к приёму своих команд. Пишем по очереди такое:

Если в ответ питон нам выдал что-то вроде 'Hello, TensorFlow', это значит, что мы всё сделали правильно.

Устанавливаем классификатор

Классификатор в нейросетях — это алгоритм, который смотрит на объекты и пытается понять, к какой категории их отнести. То есть классифицировать.

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

Запускаем нейросеть на домашнем компьютере

3.Копируем содержимое архива в папку tell-me. Если вы выбрали другое название для проекта, замените tell-me на своё название.

Добавляем фото для обучения

Скачиваем уже собранный датасет с цветами, распаковываем его и копируем в папку tell-me → tf_files.

Запускаем нейросеть на домашнем компьютере

Адаптируем скрипты под актуальную версию tensorflow

👉 На момент написания статьи актуальная версия tensorflow — 2.0. Но скрипты и алгоритмы, которые мы используем, заточены под старую версию, поэтому нужно применить немного магии автозамены:

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

Обучаем нейросеть

  1. В командной строке командой cd переходим в папку tell-me (или в другую, если у вас проект называется по-другому).
  2. Запускаем команду:

python scripts/retrain.py
--output_graph=tf_files/retrained_graph.pb
--output_labels=tf_files/retrained_labels.txt
--image_dir=tf_files/flower_photos

Пошёл процесс обучения. В нём 4000 этапов, по времени занимает примерно 20 минут. За это время нейросеть обработает около 250 фото (это очень мало для нейросети) и научится отличать розу от ландышей:

Запускаем нейросеть на домашнем компьютере

Запускаем нейросеть

Чтобы проверить работу нашей нейросети, скачиваем любой файл с розой из интернета, кладём его в папку tell-me (или как у вас она называется) и пишем такую команду:

python scripts/label_image.py --image image.jpg

Нейросеть думает, а потом выдаёт ответ в виде процентов. В нашем случае она на 98% уверена, что это роза:

Запускаем нейросеть на домашнем компьютере

А вот как нейросеть реагирует на фото Цукерберга:

Запускаем нейросеть на домашнем компьютере

50% — что на фото тюльпан, и на 18% — что это одуванчик. А всё потому, что она умеет различать только 5 видов цветов, а не всяких там цукербергов.


Что же такое нейронная сеть?

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

Говоря проще, ИНС можно назвать неким «черным ящиком», превращающим входные данные в выходные данные. Если же посмотреть на это с точки зрения математики, то речь идет о том, чтобы отобразить пространство входных X-признаков в пространство выходных Y-признаков: X → Y. Таким образом, нам надо найти некую F-функцию, которая сможет выполнить данное преобразование. На первом этапе этой информации достаточно в качестве основы.

Какую роль играет искусственный нейрон?

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

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


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


Нейрослой

Один нейрон может превратить в одну точку входной вектор, но по условию мы желаем получить несколько точек, т. к. выходное Y способно иметь произвольную размерность, которая определяется лишь ситуацией (один выход для XOR, десять выходов, чтобы определить принадлежность к одному из десяти классов, и так далее). Каким же образом получить n точек? На деле все просто: для получения n выходных значений, надо задействовать не один нейрон, а n. В результате для каждого элемента выходного Y будет использовано n разных взвешенных сумм от X. В итоге мы придем к следующему соотношению:


Давайте внимательно посмотрим на него. Вышенаписанная формула — это не что иное, как определение умножения матрицы на вектор. И в самом деле, если мы возьмем матрицу W размера n на m и выполним ее умножение на X размерности m, то мы получим другое векторное значение n-размерности, то есть как раз то, что надо.

Таким образом, мы можем записать похожее выражение в более удобной матричной форме:


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

Ниже показан пример нейронной сети, имеющей 2 входа, 5 нейронов и 1 выход:


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


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

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


Каждая из них имеет свои особенности.

Пишем код

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

  1. Вектор.
  2. Матрица (каждый слой включает в себя матрицу весовых коэффициентов).
  3. Нейронная сеть.

Начнем с вектора. Создавать его можно:

  • из количества элементов;
  • из перечисления вещественных чисел.

Также мы можем получать и менять значения по индексу i.

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


А вот и сама нейронная сеть:


Как будем обучать?

Некоторые особенности работы этого алгоритма:

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

Вот как выглядит алгоритм обучения:


Переходим к обучению

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

  • x — вход слоя,
  • z — выход,
  • df — производная функции активации.

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


Несколько слов об обратном распространении ошибки

В качестве функции оценки нейросети E(W) мы берем среднее квадратичное отклонение:


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


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


Смотрим реализацию в коде:


Обновление весовых коэффициентов

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



Вот оно, обучение!

Теперь мы имеем все нужные нам методы, поэтому остается лишь всё это вместе соединить, сформировав единый метод обучения.


Наша сеть готова, но мы пока ее еще ничему не научили. Сейчас это исправим.

Тренировка нейронной сети. Функции XOR

Функция XOR интересна тем, что ее нельзя получить одним нейроном:


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


Теперь запускаем обучение с параметрами ниже:

Выполнив обучение, посмотрим итоги, для чего надо будет сделать прямой проход для всех элементов:


В итоге вывод будет следующим:


Результаты

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

  • подготовить 2 векторных обучающих массива векторов X и Y,
  • подобрать параметры,
  • запустить само обучение,
  • наблюдать за процессом.

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

Надеемся, что материал был вам полезен и теперь вы знаете, как сделать нейросеть, и какие нюансы разработки стоит учитывать. Если же интересуют более продвинутые знания, обратите внимание на курсы, которые разработала команда Otus:

Превью к статье о написании нейронной сети прямого распространения

Нейросети кажутся людям чем-то очень сложным и запутанным, однако это вовсе не так. Простую нейросеть можно написать менее чем за час с нуля. В нашей статье мы создадим нейронную сеть прямого распространения (также называемую многослойным перцептроном), используя лишь массивы, циклы и условные операторы, а значит этот код легко можно будет перенести на любой язык программирования, предоставляющий эти возможности. А если язык предоставляет библиотеку для матричных и векторных вычислений (как, например, numpy в языке Python, то написание займёт ещё меньше времени).

Что такое нейросеть?

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

Более простыми словами, это некий чёрный ящик, который превращает входные данные в выходные, или, говоря более математическим языком, является отображением пространства входных признаков X в пространство выходных признаков Y: X → Y. То есть мы хотим найти какую-то функцию F, которая сможет выполнять это преобразование. Для начала этой информации нам будет достаточно. Для более подробного ознакомления рекомендуем ознакомиться с этой статьёй на хабре.

Коротко об искусственном нейроне

Чаще всего в подобных статьях начинают расписывать про устройство биологического нейрона, связь с его искусственной моделью и прочую лирику. Мы же этого делать не будем, а сразу перейдём к сути. Искусственный нейрон — это всего лишь взвешенная сумма значений входного вектора элементов, которая передаётся на нелинейную функцию активации f: z = f(y), где y = w0·x0 + w1·x1 + . + wm - 1·xm - 1 . Здесь w0, . wm - 1 — коэффициенты, веса каждого элемента вектора, x0, . xm - 1 — значения входного вектора X, y — взвешенная сумма элементов X, а z — результат применения функции активации. Мы вернёмся к функции активации немного позднее, а пока давайте придумаем, как вместо одного выходного значения получить n.

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

Нейронный слой

Один нейрон способен входной вектор превратить в одну точку, однако по условию мы хотим получить несколько точек, так как выходной вектор Y может иметь произвольную размерность, определяемую лишь конкретной ситуацией (один выход для XOR, 10 выходов для определения принадлежности к одному из 10 классов и т.д.). Как же нам получить n точек, преобразуя элементы входного вектора X? Оказывается, всё довольно просто: для того, чтобы получить n выходных значений, необходимо использовать не один нейрон, а n. Тогда для каждого из элементов выходного вектора Y будет использовано ровно n различных взвешенных сумм от вектора X. То есть мы получаем, что zi = f(yi) = f(wi0·x0 + wi1·x1 + . + wim - 1·xm - 1)

Если внимательно посмотреть, то оказывается, что написанная выше формула является определением умножения матрицы на вектор. И действительно, если взять матрицу W размера n на m и умножить её на вектор X размерности m, то получится другой вектор размерности n, то есть ровно то, что нам и нужно. Таким образом, получение выходного вектора по входному для n нейронов можно записать в более удобной матричной форме: Y = W·X , где W — матрица весовых коэффициентов, X — входной вектор и Y — выходной вектор. Однако полученный вектор является неактивированным состоянием (промежуточным, невыходным) всех нейронов, а чтобы получить выходное значение,, необходимо каждое неактивированное значение подать на вход функции активации. Результат её применения и будет выходным значением слоя.

Пример сети

Пример нейронной сети с двумя входами, пятью нейронами в скрытом слое и одним выходом

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

Пример многослойной сети

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

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

  • Сигмоида: f(x) = 1 / (1 + e -x )
  • Гиперболический тангенс: f(x) = tanh(x)
  • ReLU: f(x) = max(x,0)

У каждой из них есть свои особенности, но об этом лучше почитать в другой статье.

Хватит бла бла, давайте писать код

  1. Вектор (входные, выходные);
  2. Матрица (каждый слой содержит матрицу весовых коэффициентов);
  3. Нейросеть.

1. Вектор:

  • Вектор можно создавать из количества элементов (длины);
  • Вектор можно создавать из перечисления вещественных чисел;
  • Можно получать значения по индексу i.
  • Можно изменять значения по индексу i.

2. Матрица:

  • Матрицу можно создавать из числа строк, столбцов и генератора случайных чисел для заполнения случайными значениями;
  • Можно получать значения по индексам i и j;
  • Можно изменять значения по индексам i и j;

3. Сама нейросеть:

Сеть есть, но её ответы случайны. Как обучать?

На данный момент мы имеем случайную (необученную) нейронную сеть, которая может по входному вектору input выдать случайный ответ, однако нам требуется ответы, удовлетворяющие конкретной задаче. Чтобы добиться этого нашу сеть необходимо обучить. Для этого нам необходима база тренировочных примеров, то есть множество пар векторов X - Y, на которых будет обучаться сеть. Обучать нейросеть мы будем с помощью алгоритма обратного распространения ошибки. Если кратко, то он работает следующим образом:

  • Подать на вход сети обучающий пример (один входной вектор)
  • Распространить сигнал по сети вперёд (получить выход сети)
  • Вычислить ошибку (разница получившегося и ожидаемого векторов)
  • Распространить ошибку на предыдущие слои
  • Обновить весовые коэффициенты для уменьшения ошибки

Сам же алгоритм обучения выглядит так:

Обучаем нейронную сеть

Для обратного распространения ошибки нам потребуется знать значения входов, выходов и значения производных функции активации сети на каждом из слоёв, поэтому создадим структуру LayerT, в которой будет 3 вектора: x — вход слоя, z — выход слоя, df — производная функции активации. Также для каждого слоя потребуются векторы дельт, поэтому добавим в наш класс ещё и их. С учётом вышесказанного наш класс станет выглядеть так:

Обратное распространение ошибки

Перейдём к обратному распространению ошибки. В качестве функции оценки сети E(W) возьмём среднее квадратичное отклонение: E = 0.5 · Σ(y1i - y2i) 2 . Чтобы найти значение ошибки E, нам нужно найти сумму квадратов разности значений вектора, который выдала сеть в качестве ответа, и вектора, который мы ожидаем увидеть при обучении. Также нам потребуется найти дельту для каждого слоя, причём для последнего слоя она будет равна вектору разности полученного и ожидаемого векторов, умноженному (покомпонентно) на вектор значений производных последнего слоя: δlast = (zlast - d)·f'last , где zlast — выход последнего слоя сети, d — ожидаемый вектор сети, f'last — вектор значений производной функции активации последнего слоя.

Теперь, зная дельту последнего слоя, мы можем найти дельты всех предыдущих слоёв. Для этого нужно умножить транспонированную матрицы текущего слоя на дельту текущего слоя и затем умножить полученный вектор на вектор производных функции активации предыдущего слоя: δk-1 = W T k·δk·f'k .

Что ж, давайте реализуем это в коде:

Изменение весов

Для того, чтобы уменьшить ошибку сети нужно изменить весовые коэффициенты каждого слоя. Как же именно нужно менять весовые коэффициенты матриц на каждом слое? Оказывается, всё довольно просто. Для этого используется метод градиентного спуска, а значит нам необходимо вычислить градиент по весам и сделать шаг в отрицательную сторону от этого градиента. На этапе прямого распространения мы зачем-то запоминали входные сигналы, а при обратном распространении ошибки мы вычисляли дельты в каждом слое. Именно их мы и будем сейчас использовать для нахождения градиента! Градиент по весам равен перемножению входного вектора и вектора дельт (не покомпонентно). Поэтому, чтобы обновить весовые коэффициенты и уменьшить тем самым ошибку сети нужно всего лишь вычесть из матрицы весов результат перемножения дельт и входных векторов, умноженный на скорость обучения. Это можно записать в таком виде: Wt+1 = Wt - η·δ·X , где Wt+1 — новая матрица весов, Wt — текущая матрица весов, X — входное значение слоя, δ — дельта этого слоя. Почему именно так с математической точки зрения хорошо описано в этой статье.

Обучение сети

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

Сеть готова. Давайте же её чему-нибудь научим!

Тренируем нейросеть на функции XOR

Почему функция XOR так интересна? Просто потому, что её невозможно получить одним нейроном: 0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0. Однако она легко получается увеличением числа нейронов. Мы же попробуем выполнить обучение сети с 3 нейронами в скрытом слое и 1 выходным (так как выход у нас всего один). Для этого нам необходимо создать массив векторов X и Y с обучающими данными и саму нейросеть:

После чего запустим обучение со следующими параметрами: скорость обучения - 0.5, число эпох - 100000, величина ошибки - 1e-7:

После обучения посмотрим на результаты выполнив прямой проход для всех элементов:

В результате вывод может быть таким:

Проверять результаты на тренировочной же выборке довольно скучно, ведь как никак на ней мы сеть обучали, но, увы, для XOR проблемы ничего другого не остаётся. В качестве более серьёзного примера рекомендуем выполнить задачу распознавания картинок с рукописными цифрами MNIST. Это база содержит 60000 картинок написанных от руки цифр размером 28 на 28 пикселей и используется как один из основных датасетов для начала изучения машинного обучения. Не смотря на простоту нашей сети, при грамотном выборе параметров (число нейронов, число слоёв, скорость обучения, число эпох. ) можно получить точность распознавания до 98%! Проверить свою сеть вы можете, поучаствовав в соревновании на сайте Kaggle. Нашей команде удалось достичь точности в 98.171%! А вы сможете больше? :)

В заключение

Мы написали с вами нейронную сеть прямого распространения и даже обучили её функции XOR. При этом мы позаботились об универсальности, благодаря чему нейросеть может быть обучена на любых данных, главное только подготовить два массива обучающих векторов X и Y, подобрать параметры обучения и запустить само обучение, после чего наблюдать за процессом. Важно помнить, что при использовании сигмоидальной функции активации, выходные значения сети не будут превышать 1, а значит, для обучения данным, которые значительно больше 1 необходимо отнормировать их, то есть привести к отрезку [0, 1].

Фото Перминова Андрея и Кудиновой Светланы, авторов этой статьи

Выпускник МГУ им. М.В. Ломоносова

Выпускница МГТУ им. Н.Э. Баумана

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

Читайте также: