Читая Хабр на предмет материалов по нейросетям и вообще по теме искусственного интеллекта я нашел пост о однослойном перцептроне и решил из любопытства начать изучение нейросетей с него, а потом и расширить опыт до многослойного перцептрона. О чем и повествую.
Многослойный перцептрон неплохо описан в Вики, но там описана лишь структура, мы же попробуем его на деле, вместе с алгоритмом обучения. Кстати, он тоже описан в Вики, хотя, сравнивая его с несколькими другими источниками (книги и aiportal.ru), я нашел несколько проблемных мест и там и там.
Итак, многослойный перцептрон — нейронная сеть, состоящая из слоев, каждый из которых состоит из элементов — нейронов (точнее их моделей). Эти элементы бывают трех типов: сенсорные (входные, S), ассоциативные (обучаемые «скрытые» слои, A) и реагирующие (выходные, R). Многослойным этот тип перцептронов называется не потому, что состоит из нескольких слоев, ведь входной и выходной слои можно вообще не оформлять в коде, а потому, что содержит несколько (обычно, не более двух — трех) обучаемых (A) слоев.
Модель нейрона (будем называть его просто нейрон) — это элемент сети, который имеет несколько входов, каждый из которых имеет вес. Нейрон, получая сигнал, помножает сигналы на веса и суммирует получившиеся величины, после чего передает результат к другому нейрону или на выход сети. Здесь тоже многослойный перцептрон имеет отличия. Его функция — сигмоид, она выдает значения на промежутке от 0 до 1. К сигмоидам относится несколько функций, мы будем иметь ввиду логистическую функцию. В ходе рассмотрения метода Вы поймете, почему это так хорошо.
Несколько слоев, которые могут обучаться (точнее, подстраиваться) позволяют аппроксимировать очень сложные нелинейные функции, то есть их область применения шире, нежели однослойных.
Сразу будем перекладывать теорию на практику, дабы запомнилось лучше и все могли попробовать.
Рекомендую прочитать вышеуказанный пост, если вы не ас в нейросетях, конечно.
Итак, возьмем простую задачу — распознать цифры без поворота и искажений. В такой задаче многослойного перцептрона будет достаточно, более того, он менее чувствителен к шумам.
Сеть будет иметь два скрытых слоя, размером в 5 раз меньше, чем входной. То есть, если у нас 20 входов, то на скрытых слоях будет по 4 нейрона. В случае данной задачи я позволю себе смелость подобрать количество слоев и нейронов эмпирически. Слоев возьмем 2, при увеличении количества слоев результат не улучшается.
Обучение нейросетей выбранного типа проводится по алгоритму обратного распространения ошибки. То есть если при ответе у нас слои передают сигнал в выходу сети, то мы будем сравнивать ответ нейросети с верным и вычислять ошибку, которая потом пойдет «вверх» по сети — от выходов к входам.
Оценивать ошибку сети мы будем как половину суммы квадратов разниц сигналов на выходах. Попроще: делим пополам сумму по i таких вот выражений: (ti — oi)^2, где ti — значение i-го сигнала в правильном ответе, а oi — значение i-го выхода нейросети. То есть мы суммируем квадраты ошибок по входам и делим все пополам. Если эта ошибка (в коде примера это $d) достаточно велика (не укладывается в нужную нам точность), правим веса нейронов.
Формулы поправок весов целиком выложены в Вики, репостить не буду. Хочу только заметить, что старался буквально повторить формулы, чтобы было понятно, как оно на практике. Здесь всплывает преимущество выбора функции активации — у нее простая производная ( σ'(x)=σ(x)*(1-σ(x)) ), и она и применяется для коррекции весов. Веса каждого слоя корректируются отдельно. То есть слой за слоем от последнего к первому. И вот здесь я допустил ошибку, сначала я корректировал веса на каждом примере в отдельности, и нейросеть училась решать только один «примерчик». Правильно же в таком алгоритме давать на входы по очереди все примеры обучающей выборки, это называется эпохой. И только с концом эпохи считать ошибку (суммарную по всем примерам выборки) и корректировать веса.
Во время обучения возможны скачки в погрешностях, но такое бывает. Подбор коэффициентов α (определяет влияние весов на обучение) и η (определяет влияние величины поправки δ) очень важен — от него зависят скорость сходимости и попадания в локальные экстремумы. Наиболее универсальным я считаю α=0.7 и η=0.001, хотя попробуйте ими поиграть: увеличение α и η ускоряет обучение, но мы можем пролететь минимум.
Далее выкладываю пример на PHP. Код далек от идеала, но свои задачи выполняет.
Теория
Многослойный перцептрон неплохо описан в Вики, но там описана лишь структура, мы же попробуем его на деле, вместе с алгоритмом обучения. Кстати, он тоже описан в Вики, хотя, сравнивая его с несколькими другими источниками (книги и aiportal.ru), я нашел несколько проблемных мест и там и там.
Итак, многослойный перцептрон — нейронная сеть, состоящая из слоев, каждый из которых состоит из элементов — нейронов (точнее их моделей). Эти элементы бывают трех типов: сенсорные (входные, S), ассоциативные (обучаемые «скрытые» слои, A) и реагирующие (выходные, R). Многослойным этот тип перцептронов называется не потому, что состоит из нескольких слоев, ведь входной и выходной слои можно вообще не оформлять в коде, а потому, что содержит несколько (обычно, не более двух — трех) обучаемых (A) слоев.
Модель нейрона (будем называть его просто нейрон) — это элемент сети, который имеет несколько входов, каждый из которых имеет вес. Нейрон, получая сигнал, помножает сигналы на веса и суммирует получившиеся величины, после чего передает результат к другому нейрону или на выход сети. Здесь тоже многослойный перцептрон имеет отличия. Его функция — сигмоид, она выдает значения на промежутке от 0 до 1. К сигмоидам относится несколько функций, мы будем иметь ввиду логистическую функцию. В ходе рассмотрения метода Вы поймете, почему это так хорошо.
Несколько слоев, которые могут обучаться (точнее, подстраиваться) позволяют аппроксимировать очень сложные нелинейные функции, то есть их область применения шире, нежели однослойных.
Пробуем на практике
Сразу будем перекладывать теорию на практику, дабы запомнилось лучше и все могли попробовать.
Рекомендую прочитать вышеуказанный пост, если вы не ас в нейросетях, конечно.
Итак, возьмем простую задачу — распознать цифры без поворота и искажений. В такой задаче многослойного перцептрона будет достаточно, более того, он менее чувствителен к шумам.
Структура сети
Сеть будет иметь два скрытых слоя, размером в 5 раз меньше, чем входной. То есть, если у нас 20 входов, то на скрытых слоях будет по 4 нейрона. В случае данной задачи я позволю себе смелость подобрать количество слоев и нейронов эмпирически. Слоев возьмем 2, при увеличении количества слоев результат не улучшается.
Алгоритм обучения
Обучение нейросетей выбранного типа проводится по алгоритму обратного распространения ошибки. То есть если при ответе у нас слои передают сигнал в выходу сети, то мы будем сравнивать ответ нейросети с верным и вычислять ошибку, которая потом пойдет «вверх» по сети — от выходов к входам.
Оценивать ошибку сети мы будем как половину суммы квадратов разниц сигналов на выходах. Попроще: делим пополам сумму по i таких вот выражений: (ti — oi)^2, где ti — значение i-го сигнала в правильном ответе, а oi — значение i-го выхода нейросети. То есть мы суммируем квадраты ошибок по входам и делим все пополам. Если эта ошибка (в коде примера это $d) достаточно велика (не укладывается в нужную нам точность), правим веса нейронов.
Формулы поправок весов целиком выложены в Вики, репостить не буду. Хочу только заметить, что старался буквально повторить формулы, чтобы было понятно, как оно на практике. Здесь всплывает преимущество выбора функции активации — у нее простая производная ( σ'(x)=σ(x)*(1-σ(x)) ), и она и применяется для коррекции весов. Веса каждого слоя корректируются отдельно. То есть слой за слоем от последнего к первому. И вот здесь я допустил ошибку, сначала я корректировал веса на каждом примере в отдельности, и нейросеть училась решать только один «примерчик». Правильно же в таком алгоритме давать на входы по очереди все примеры обучающей выборки, это называется эпохой. И только с концом эпохи считать ошибку (суммарную по всем примерам выборки) и корректировать веса.
Во время обучения возможны скачки в погрешностях, но такое бывает. Подбор коэффициентов α (определяет влияние весов на обучение) и η (определяет влияние величины поправки δ) очень важен — от него зависят скорость сходимости и попадания в локальные экстремумы. Наиболее универсальным я считаю α=0.7 и η=0.001, хотя попробуйте ими поиграть: увеличение α и η ускоряет обучение, но мы можем пролететь минимум.
Далее выкладываю пример на PHP. Код далек от идеала, но свои задачи выполняет.