Как найти дельта случайное

72,1% бесплатных материалов

968 руб. средняя цена курсовой работы

353 руб. средняя цена домашнего задания

116 руб. средняя цена решённой задачи

162 руб. средняя цена лабораторной работы

174 руб. средняя цена реферата

177 руб. средняя цена доклада

1626 руб. средняя цена ВКР

665 руб. средняя цена диссертации

596 руб. средняя цена НИР

360 руб. средняя цена отчёта по практике

276 руб. средняя цена ответов (шпаргалок)

202 руб. средняя цена лекций

232 руб. средняя цена семинаров

280 руб. средняя цена рабочей тетради

187 руб. средняя цена презентации

67 руб. средняя цена перевода

143 руб. средняя цена изложения

150 руб. средняя цена сочинения

308 руб. средняя цена статьи

Гарантия возврата средств

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

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

История персептрона

Понятия искусственного нейрона и искусственной нейронной сети появились достаточно давно, еще в 1943 году. Эта была чуть ли не первая статья, в которой предпринимались попытки смоделировать работу мозга. Ее автором был Уоррен Мак-Каллок.

Эти идеи продолжил нейрофизиолог Фрэнк Розенблатт. Он предложил схему устройства, моделирующего процесс человеческого восприятия, и назвал его «персептроном» (от латинского perceptio – восприятие). В 1960 году Розенблатт представил первый нейрокомпьютер – «Марк-1», который был способен распознавать некоторые буквы английского алфавита.

Таким образом персептрон является одной из первых моделей нейросетей, а «Марк-1» – первым в мире нейрокомпьютером.

Персептроны стали очень активно исследовать. На них возлагали большие надежды. Однако, как оказалось, они имели серьезные ограничения. Был такой ученый Минский, который был сокурсником Розенблатта. Видимо, ему не очень понравилось, как все вокруг боготворили персептроны, и он написал целую книгу (1971 год), в которой провел детальнейший их анализ, попутно показав, что они не так уж много и умеют, да и вообще сильно ограничены.

Розенблатт не успел написать ответ Минскому, так как погиб в свой 43 день рождения при крушении лодки.

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

Теперь подробно разберем, что из себя представляет персептрон.

Персептрон

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

Рассмотрим принцип работы персептрона.

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

Далее сигналы от S-элементов передаются A-элементам по так называемым S-A связям. Эти связи могут иметь веса, равные только -1, 0 или 1.

Затем сигналы от сенсорных элементов, прошедших по S-A связям попадают в A-элементы, которые еще называют ассоциативными элементами. Стоит заметить, что одному A-элементу может соответствовать несколько S-элементов. Если сигналы, поступившие на A-элемент, в совокупности превышают некоторый его порог ​( theta )​, то этот A-элемент возбуждается и выдает сигнал, равный 1. В противном случае (сигнал от S-элементов не превысил порога A-элемента), генерируется нулевой сигнал.

Почему A-элементы назвали ассоциативными? Дело в том, что A-элементы являются агрегаторами сигналов от сенсорных элементов. Например, у нас есть группа сенсоров, каждый из которых распознает кусок буквы «Д» на исследуемой картинке. Однако только их совокупность (то есть когда несколько сенсоров выдали сигнал, равный 1) может возбудить A-элемент целиком. На другие буквы А-элемент не реагирует, только на букву «Д». То есть он ассоциируется с буквой «Д». Отсюда и такое название.

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

Далее сигналы, которые произвели возбужденные A-элементы, направляются к сумматору (R-элемент), действие которого вам уже известно. Однако, чтобы добраться до R-элемента, они проходят по A-R связям, у которых тоже есть веса. Однако здесь они уже могут принимать любые значения (в отличие от S-A связей).

Финальный аккорд. R-элемент складывает друг с другом взвешенные сигналы от A-элементов и, если превышен определенный порог, генерирует выходной сигнал, равный 1. Это означает, что в общем потоке информации от глаз мы распознали лицо человека.

Если порог не превышен, то выход персептрона равен -1. То есть мы не выделили лицо из общего потока информации.

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

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

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

Выше я рассказал о персептроне в самом общем виде. Есть несколько подвидов персептронов, на некоторых из которых мы и сконцентрируем все внимание.

Классификация персептронов

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

Персептрон с одним скрытым слоем

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

Почему слой именно скрытый? Потому что слой А-элементов расположен между слоями S-элементов и R-элементов.

Персептрон с одним скрытым слоем — персептрон, у которого имеется только по одному слою S, A и R элементов.

Обе картинки выше изображают именно персептрон с одним скрытым слоем.

Однослойный персептрон

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

Его ключевая особенность состоит в том, что каждый S-элемент однозначно соответствует одному A-элементу, все S-A связи имеют вес, равный +1, а порог A элементов равен 1.

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

Изначально персептрон в общем смысле выглядит так:

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

Обязательно убедитесь, что поняли фразу «каждый S-элемент однозначно соответствует одному A-элементу». Это означает, что каждый сенсор может передавать сигнал только одному А-элементу. Однако это утверждение вовсе не запрещает ситуации, когда несколько сенсоров передают сигнал на один А-элемент, что и продемонстрировано на картинке выше (1, 2 и 3 А-элементы).

Далее, S-A связи всегда имеют вес, равный единице, а порог А-элементов всегда равен +1. С другой стороны нам известно, что сенсоры могут подавать сигнал равный только 0 или 1.

Рассмотрим первый S-элемент на последней картинке. Пусть он генерирует сигнал, равный единице. Сигнал проходит по S-A связи и не изменяется, так как любое число, умноженное на 1 равно самому себе. Порог любого А-элемента равен 1. Так как сенсор произвел сигнал, равный 1, то А-элемент однозначно возбудился. Это означает, что он выдал сигнал, равный 1 (так как он тоже может генерировать только 1 или 0 на своем выходе). Далее этот единичный сигнал умножается на произвольный вес A-R связи и попадает в соответствующий R-элемент, который суммирует все поступившие на него взвешенные сигналы, и если они превышают его порог, выдает +1. В противном случае выход данного R-элемента равен -1.

Ничего на напоминает? Правильно, не считая сенсорных элементов и S-A связей, мы только что описали схему работы искусственного нейрона. И это неслучайно. Однослойный персептрон действительно представляет собой искусственный нейрон с небольшим отличием. В отличие от искусственного нейрона, у однослойного персептрона входные сигналы могут принимать фиксированные значения: 0 или 1. У искусственного нейрона на вход можно подавать любые значения.

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

Нетрудно догадаться, что такое поведение легко задается функцией активации под названием функция единичного скачка, которую мы уже рассматривали во 2 главе. Отличие заключается в том, что функция единичного скачка выдает 0, если порог не превышен, а здесь выдает -1, но это не существенно.

Таким образом становится ясно, что часть однослойного персептрона (выделена черным прямоугольником на картинке выше) можно представить в виде искусственного нейрона, но ни в коем случае не путайте два этих понятия. Во-первых, никто не отменял S-элементы, которых в искусственном нейроне просто нет. Во-вторых, в однослойном персептроне S-элементы и A-элементы могут принимать только фиксированные значения 0 и 1, тогда как в искусственном нейроне таких ограничений нет.

Однослойный персептрон — персептрон, каждый S-элемент которого однозначно соответствует одному А-элементу, S-A связи всегда равны 1, а порог любого А-элемента равен 1.

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

Однослойный персептрон может быть и элементарным персептроном, у которого только по одному слою S,A,R-элементов.

Многослойный персептрон

Под многослойным персептроном понимают два разных вида: многослойный персептрон по Розенблатту и многослойный персептрон по Румельхарту.

Многослойный персептрон по Розенблатту содержит более 1 слоя А-элементов.

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

  1. S-A связи могут иметь произвольные веса и обучаться наравне с A-R связями.
  2. Обучение производится по специальному алгоритму, который называется обучением по методу обратного распространения ошибки.

Этот метод является краеугольным камнем обучения всех многослойных  ИНС. Во многом благодаря ему возобновился интерес к нейронным сетям. Но обсуждать мы его будем в других главах.

Многослойный персептрон по Розеблатту — персептрон, у которого имеется более 1 слоя А-элементов.

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

Опорная схема

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

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

Говоря про персептрон я буду иметь ввиду именно его.

Какие задачи решает персептрон?

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

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

«Очень хорошо» – понятие растяжимое. Насколько хорошо? Розенблатт доказал несколько теорем, суть которых я попытаюсь донести максимально понятным образом.

1. Если имеется поле сенсоров (матрица) и какая-то классификация, зависящая от него, то множество элементарных персептронов, проводящих успешную классификацию не является пустым.

Объясняю на пальцах. Под полем сенсоров понимается множество всех S-элементов. Под классификацией – придуманные нами классы (те же кошки и собаки). Под «непустым множеством элементарных персептронов, проводящих успешную классификацию» понимается, что найдется хотя бы один перспетрон, справившийся с классификацией объектов.

Рассмотрим на примере.

Я хочу, чтобы персептрон научился различать кошек и собак. Это задача довольно трудная, но мы ее существенно упростим.

У нас будет 3 сенсора: длина лап, окрас и форма морды. Так как S-элементы могут принимать значения 0 или 1, то условимся, что значения 1 будут соответствовать коротким лапам, смешанному окрасу и округлая морда соответственно. Значения 0 будут означать признак собаки на данном S-элементе (длинные лапы, однотонный окрас и вытянутая морда). Вот мы и получили сенсорное поле. Если хотите, его можно представить в виде множества возможных значений 0 и 1 у каждого S-элемента. Например, абсолютная кошка должна вызвать срабатывание всех S-элементов ​( {1,1,1} )​.

Идеальной же собаке соответствует следующий набор выходов S-элементов: ​( {0,0,0} )​.

Сами по себе сенсоры не играют роли. Но добавив к набору выходов сенсоров смысл: кошка или собака, мы тем самым задали некоторую классификацию. Математически это означает, что мы задали некоторую функцию, которая принимает набор выходов S-элементов, а ее значением является 0 или 1 (кошка или собака).

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

Но ведь можно выбрать любой набор S-элементов и любую классификацию. И множество «решений» все равно не будет пустым!

Это означает, что теоретически персептроны способны решать любую задачу на классификацию.

Важное замечание!
1. Речь идет об элементарных персептронах.
2. Объекты классификации должны обладать свойством линейной разделимости (подробнее о ней ниже).

Но есть и вторая теорема, доказанная Розенблаттом:

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

Под произвольным исходным состоянием тут понимается персептрон с произвольными S-A и A-R весами связей. Под решением в теореме понимается персептрон с определенными весами, успешно решающий нашу задачу на классификацию.

Эта теорема не оставляет задачам на классификацию никаких шансов. Теперь нам известно, что мы всегда сможем решить нашу задачу за конечный промежуток времени. Единственный нюанс заключается в том, что никто не говорит о длительности «конечного промежутка времени». Секунда, минута, час, год, 1 000 лет?

Обе теоремы имеют доказательства, но здесь мы их рассматривать не будем.

Метод коррекции ошибок – один из алгоритмов изменения весов. В этой главе мы его также разберем.

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

Линейная разделимость

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

В этом случае потребуется всего два S-элемента, определяющие размер и прирученность. Так как размер животного и степень его прирученности могут быть разными и иметь промежуточные значения, то давайте немного отойдем от принятого определения S-элемента. Представим, что он может выдавать не только 0 (маленький размер, совсем дикий) или 1 (большой, полностью ручной), но и другие значения.

Так как у нас есть два сенсорных элемента, то их значения можно расположить вдоль двух координатных осей. На получившейся координатной плоскости можно размещать точки, каждая из которых характеризует какой-то вид кошки или собаки. Как вы знаете, у каждой точки есть координаты. В нашем случае их две: размер и прирученность. Так вот, задачей персептрона в данном случае (основанного на двух S-элементах) – провести некоторую прямую, которая максимально точно разделит два множества точек (кошек и собак). На рисунке ниже видно 4 этапа обучения сети на все более большой обучающей выборке.

Естественно, что у нас может быть больше признаков, а значит, и больше сенсорных элементов. В случае трех признаков будет три S-элемента, то есть имеем уже трехмерное пространство. В таком случае, между точками, каждая из которых соответствует определенным значениям всех трех S-элементов, проводилась бы плоскость. И так далее. В общем случае для ​( n )​ S-элементов в ​( n )​-мерном пространстве строится так называемая гиперплоскость с размерностью ​( n-1 )​.

Вы заметили, что в картинках выше в качестве разделителя используется прямая? А ведь мы могли бы в качестве разделителя использовать и любую другую кривую. Но прямая – проще. Именно поэтому в этой главе я рассматриваю задачи на классификацию линейно (от слова линия) разделимых объектов. Именно такие задачи способны решать элементарные персептроны.

Конечно, бывают и такие множества объектов, которые невозможно разделить линией (плоскостью/гиперплоскостью). В общем случае, такие множества называют линейно неразделимыми и в этой главе мы не будем их рассматривать. Теоремы о сходимости элементарных пересептронов на них не распространяются.

Задача на классификацию

Можно ли классифицировать логические функции? Да, и к тому же эта задача отлично проиллюстрирует такую классификацию.

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

Логические функции очень красиво иллюстрируют идею классификации. Любая такая функция принимает на вход два аргумента. По счастливой случайности точки на плоскости задаются двумя числами (x и y)! Но логические функции могут принимать только дискретные аргументы (0 или 1). В итоге получаем, что для изображения любой логической функции на плоскости достаточно 4 точки (с координатами ​( (0,0) )​ ​( (1,0) )​ ​( (0,1) )​ ​( (1,1) )​). Вот так это выглядит:

Рассмотрим логическую функцию И. Она равна нулю для любого набора входных аргументов, кроме набора ​( (1,1) )​.

X1 X2 Логическое И
0 0 0
1 0 0
0 1 0
1 1 1

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

В случае с логическим И эту прямую, например, можно провести так, как показано на рисунке ниже. Все точки, находящиеся под этой прямой, приводят к 0 значению этой функции. Единственная точка над этой прямой приводит к значению логического И, равному 1.

Похожим образом ведет себя логическое ИЛИ, имеющее следующую таблицу истинности:

X1 X2 Логическое ИЛИ
0 0 0
1 0 1
0 1 1
1 1 1

Для такой функции графическое представление будет выглядеть так:

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

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

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

Обучение персептронов

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

Упрощаем до предела

Начнем обучение наших нейронных сетей с самого простого случая. Для этого мы сильно упростим и без того простой однослойный персептрон с одним скрытым слоем:
1. Будем считать, что его A-R связи могут принимать только целые значения (…, -2, -1, 0, 1, 2, …).
2. Более того, у каждого А-элемента может быть только один S-элемент.
3. И у нас будет только 1 R-элемент.

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

Изначально мы имеем следующий персептрон.

Мы должны упростить его. Теперь A-элементы могут быть соединены только с одним S-элементом. Убираем все лишние связи.

На картинке выше 3 R-элемента. Оставляем только один.

S-A веса и пороги A элементов у нас теперь равны +1. Отмечаем это на рисунке.

В итоге получаем следующую картину.

Однако получается, что у нас слой A-элементов не выполняет никакие функции. Он эквивалентен S-слою. Поэтому мы проводим следующее упрощение. Выбрасываем слой сенсоров. Теперь роль сенсоров у нас будут выполнять ассоциативные элементы (или наоборот, без разницы).

Итак, мы только что ну очень упростили однослойный персептрон с одним скрытым слоем.

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

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

Практика: распознавание цифр

Картинка, как всем известно, состоит из пикселей. Так вот, очень удобно на каждый пиксель выделять один сенсор (для его распознавания).

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

  1. Будем распознавать только черно-белые цифры от 0 до 9.
  2. Цифры будут состоять из черных квадратиков в табличке 3×5 квадратов.
  3. Нейросеть будет распознавать только одну цифру.

Вот как выглядят  наши цифры.

В нашей сети будет по 1 S-элементу (он же А-элемент) на каждый квадратик из таблички. Поэтому для распознавания цифры нам потребуется 15 сенсоров. Черный цвет квадрата соответствует возбуждению S-элемента (значение передаваемого сигнала равно 1). Белый цвет – выход соответствующего S-элемента равен 0.

Цифры в строковом формате

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

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

Для записи каждой цифры у нас используется по 5 строк с 3 символами в каждой. Теперь уберем все переносы строк, чтобы получить для каждой цифры от 0 до 9 одну длинную строку длиной в 15 символов.

1 – 001001001001001

………………………………….

9 – 111101111001111

0 – 111101101101111

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

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

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

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

Постановка задачи

Мы хотим создать программу, которая из всех 10 цифр будет распознавать нужную нам цифру. Например, пусть это будет цифра 5 (можно и любую другую). Только и всего.

Нашей обучающей выборкой будут все цифры от 0 до 9. Когда нейросеть обучится безошибочно распознавать нужную нам цифру (5), тогда мы проверим ее «интеллект» уже на тестовой выборке. Она будет уже похитрее: на вход будут подаваться уже искаженные изображения пятерки.

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

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

Алгоритм обучения

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

Мы знаем, что важность тем или иным входам (в нашем случае – S-элементам) придают веса, связывающие их с R-элементом. Таким образом, чем сильнее повлиял какой-то вес связи на результат, тем сильнее надо его изменить.

Следовательно, мы должны учесть следующие важные моменты:

  • Если наша нейросеть правильно распознала/отвергла цифру 5, то мы ничего не предпринимаем (все ведь замечательно!).
  • Если нейросеть ошиблась и распознала неверную цифру как 5, то мы должны ее наказать – мы уменьшаем веса тех связей, через которые прошел сигнал. Другими словами веса, связанные с возбудившимися входами, уменьшаются.
  • Если нейросеть ошиблась и не распознала цифру 5, то мы должны увеличить все веса, через которые прошел сигнал. Таким образом мы как бы говорим сети, что такие связи, а значит и связанные с ними входы – правильные.

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

  1. Подать на входы нейросети цифру в строковом формате.
  2. Если цифра распознана/отвергнута верно, то перейти к шагу 1.
  3. Если сеть ошиблась и распознала неверную цифру как 5, то вычесть из всех связей, связанных с возбудившимися S-элементами единицу.
  4. Если сеть ошиблась и отвергла цифру 5, то добавить единицу ко всем связям, связанным с возбудившимися S-элементами.

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

Программа

Если вы не знакомы с языком программирования Python и/или не знаете, как приступить к работе, то рекомендую ознакомиться (можно бегло) с самым популярным самоучителем по Python.

Совершенно не обязательно писать нейросети на Python! Искусственные нейросети – математическая модель, и их можно запрограммировать с помощью любого языка. Так что если вам больше по душе Java, C, C#… то можете реализовывать сети на них. Никаких принципиальных различий нет. Алгоритм один и тот же. Реализация на разных языках разная.

Для начала импортируем модуль для работы со случайными числами.

import random

Теперь давайте запишем все цифры от 0 до 9. Просто записывайте цифру в 5 строк по 3 символа, а затем удаляйте переносы строк (выше мы это уже рассматривали).

# Цифры (Обучающая выборка)
num0 = list('111101101101111')
num1 = list('001001001001001')
num2 = list('111001111100111')
num3 = list('111001111001111')
num4 = list('101101111001001')
num5 = list('111100111001111')
num6 = list('111100111101111')
num7 = list('111001001001001')
num8 = list('111101111101111')
num9 = list('111101111001111')

Функция list(*) позволяет нам создать список (массив), состоящий из отдельных символов, на которые разбивается длинная строка.

Далее, для простоты добавим все эти 10 цифр в список (для быстрого доступа к ним).

# Список всех вышеуказанных цифр
nums = [num0, num1, num2, num3, num4, num5, num6, num7, num8, num9]

Теперь запишем 6 видов искаженной пятерки в строковом формате.

# Виды цифры 5 (Тестовая выборка)
num51 = list('111100111000111')
num52 = list('111100010001111')
num53 = list('111100011001111')
num54 = list('110100111001111')
num55 = list('110100111001011')
num56 = list('111100101001111')

Теперь нам необходимо создать список весов. Помните теоремы об обучении персептронов? Там сказано, что сеть из любого состояния может обучиться правильно отвечать. Так что, чтобы не загадывать, пусть все веса вначале у нас будут равны 0. Так как у нас 15 входов и все они сразу соединены с одним R-элементом, то нам потребуется 15 связей.

# Инициализация весов сети
weights = [0 for i in range(15)]

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

# Инициализация весов сети
weights = []
for i in range(15):
    weights.append(0)

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

# Порог функции активации
bias = 7

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

# Является ли данное число 5
def proceed(number):
    # Рассчитываем взвешенную сумму
    net = 0
    for i in range(15):
        net += int(number[i])*weights[i]
 
    # Превышен ли порог? (Да - сеть думает, что это 5. Нет - сеть думает, что это другая цифра)
    return net >= bias

Результатом работы выражения return net >= bias этой функции может быть True (Правда/Да), что означает 1 или False (Ложь/Нет), что означает 0.

Теперь определим еще две вспомогательные функции.

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

# Уменьшение значений весов, если сеть ошиблась и выдала 1
def decrease(number):
    for i in range(15):
        # Возбужденный ли вход
        if int(number[i]) == 1:
            # Уменьшаем связанный с ним вес на единицу
            weights[i] -= 1

Я использовал функцию int(number[i]) для преобразования символа  '1' в цифру 1. Если бы я этого не сделал, то возникла бы ошибка здесь if int(number[i]) == 1:, так как Python не умеет сравнивать символы (текст) с цифрами.

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

# Увеличение значений весов, если сеть ошиблась и выдала 0
def increase(number):
    for i in range(15):
        # Возбужденный ли вход
        if int(number[i]) == 1:
            # Увеличиваем связанный с ним вес на единицу
            weights[i] += 1

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

# Тренировка сети
for i in range(10000):
    # Генерируем случайное число от 0 до 9
    option = random.randint(0, 9)
 
    # Если получилось НЕ число 5
    if option != 5:
        # Если сеть выдала True/Да/1, то наказываем ее
        if proceed(nums[option]):
            decrease(nums[option])
    # Если получилось число 5
    else:
        # Если сеть выдала False/Нет/0, то показываем, что эта цифра - то, что нам нужно
        if not proceed(num5):
            increase(num5)

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

# Вывод значений весов
print(weights)
 
# Прогон по обучающей выборке
print("0 это 5? ", proceed(num0))
print("1 это 5? ", proceed(num1))
print("2 это 5? ", proceed(num2))
print("3 это 5? ", proceed(num3))
print("4 это 5? ", proceed(num4))
print("6 это 5? ", proceed(num6))
print("7 это 5? ", proceed(num7))
print("8 это 5? ", proceed(num8))
print("9 это 5? ", proceed(num9), 'n')
 
# Прогон по тестовой выборке
print("Узнал 5? ", proceed(num5))
print("Узнал 5 - 1? ", proceed(num51))
print("Узнал 5 - 2? ", proceed(num52))
print("Узнал 5 - 3? ", proceed(num53))
print("Узнал 5 - 4? ", proceed(num54))
print("Узнал 5 - 5? ", proceed(num55))
print("Узнал 5 - 6? ", proceed(num56))

Если у вас отображаются какие-то ошибки или вы просто запутались в структуре, то вот полный код программы:

import random
 
# Цифры (Обучающая выборка)
num0 = list('111101101101111')
num1 = list('001001001001001')
num2 = list('111001111100111')
num3 = list('111001111001111')
num4 = list('101101111001001')
num5 = list('111100111001111')
num6 = list('111100111101111')
num7 = list('111001001001001')
num8 = list('111101111101111')
num9 = list('111101111001111')
 
# Список всех вышеуказанных цифр
nums = [num0, num1, num2, num3, num4, num5, num6, num7, num8, num9]
 
# Виды цифры 5 (Тестовая выборка)
num51 = list('111100111000111')
num52 = list('111100010001111')
num53 = list('111100011001111')
num54 = list('110100111001111')
num55 = list('110100111001011')
num56 = list('111100101001111')
 
# Инициализация весов сети
weights = []
for i in range(15):
    weights.append(0)
 
# Порог функции активации
bias = 7
 
# Является ли данное число 5
def proceed(number):
    # Рассчитываем взвешенную сумму
    net = 0
    for i in range(15):
        net += int(number[i])*weights[i]
 
    # Превышен ли порог? (Да - сеть думает, что это 5. Нет - сеть думает, что это другая цифра)
    return net >= bias
 
# Уменьшение значений весов, если сеть ошиблась и выдала 1
def decrease(number):
    for i in range(15):
        # Возбужденный ли вход
        if int(number[i]) == 1:
            # Уменьшаем связанный с ним вес на единицу
            weights[i] -= 1
 
# Увеличение значений весов, если сеть ошиблась и выдала 0
def increase(number):
    for i in range(15):
        # Возбужденный ли вход
        if int(number[i]) == 1:
            # Увеличиваем связанный с ним вес на единицу
            weights[i] += 1
 
# Тренировка сети
for i in range(10000):
    # Генерируем случайное число от 0 до 9
    option = random.randint(0, 9)
 
    # Если получилось НЕ число 5
    if option != 5:
        # Если сеть выдала True/Да/1, то наказываем ее
        if proceed(nums[option]):
            decrease(nums[option])
    # Если получилось число 5
    else:
        # Если сеть выдала False/Нет/0, то показываем, что эта цифра - то, что нам нужно
        if not proceed(num5):
            increase(num5)
 
# Вывод значений весов
print(weights)
 
# Прогон по обучающей выборке
print("0 это 5? ", proceed(num0))
print("1 это 5? ", proceed(num1))
print("2 это 5? ", proceed(num2))
print("3 это 5? ", proceed(num3))
print("4 это 5? ", proceed(num4))
print("6 это 5? ", proceed(num6))
print("7 это 5? ", proceed(num7))
print("8 это 5? ", proceed(num8))
print("9 это 5? ", proceed(num9), 'n')
 
# Прогон по тестовой выборке
print("Узнал 5? ", proceed(num5))
print("Узнал 5 - 1? ", proceed(num51))
print("Узнал 5 - 2? ", proceed(num52))
print("Узнал 5 - 3? ", proceed(num53))
print("Узнал 5 - 4? ", proceed(num54))
print("Узнал 5 - 5? ", proceed(num55))
print("Узнал 5 - 6? ", proceed(num56))

Готовы? Запускайте! Пробуйте! Должно сработать.

Мои результаты — сеть стала распознавать пятерку во все случаях только
с 3 попытки. Картинка кликабельна.

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

Но почему сеть не обучается с первого раза?

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

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

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

  1. Добиться равномерности показа всех обучающих цифр.
  2. Увеличить общее количество шагов обучения (50 тысяч или 100 тысяч).

Давайте поближе рассмотрим созданного нами монстра!
Что еще за Скайнет?

Рассмотрим мой результат. Начнем с первой строчки (это веса сети):

[1, 1, 1, 3, 0, -8, 1, 2, 1, -8, 0, 1, 1, 1, 1]

Теперь расположим эти цифры в виде «цифры».

1 1 1
3 0 -8
1 2 1
-8 0 1
1 1 1

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

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

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

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

Но это еще не все. Дальше идет 9 строк обучающей выборки.

0 это 5? False
1 это 5? False
2 это 5? False
3 это 5? False
4 это 5? False
6 это 5? False
7 это 5? False
8 это 5? False
9 это 5? False

Все предложенные цифры наша нейросеть смело отвергает, клеймя их False.

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

Узнал 5? True
Узнал 5 — 1? True
Узнал 5 — 2? True
Узнал 5 — 3? True
Узнал 5 — 4? True
Узнал 5 — 5? True
Узнал 5 — 6? True

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

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

Правила Хебба (Hebb’s rule, Hebbian learning rule) — два правила, составляющие алгоритм обучения персептронов для решения простейших задач классификации, когда входы могут быть равны только 0 или 1:

1 Правило. Если сигнал персептрона неверен и равен 0, то необходимо увеличить веса тех входов, на которые была подана единица.

2 Правило. Если сигнал персептрона неверен и равен 1, то необходимо уменьшить веса тех входов, на которые была подана единица.

Правила Хебба часто встречается в литературе, как обучение с коррекцией ошибки.

Дельта-правило

Теперь попробуем обобщить правила Хебба на произвольные (не только 0/1) входы и связи (не только целые числа).

Пусть мы заранее знаем правильный выход нашей сети. Обозначим его за ​( d )​. Однако в процессе обучения сеть ошибается. Обозначим ответ сети за ​( y )​. Тогда мы можем получить ошибку/погрешность сети как разницу правильного и реального ответов:

[ delta = d-y ]

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

Дельта-правило (Delta rule) — алгоритм обучения персептронов. При обучении сети новое значение ​( i )​-го веса связи на ​( t+1 )​ шаге рассчитывается по следующей формуле:

[ w_i(t+1)=w_i(t)+delta x_i eta ]

Разберем эту формулу. Сначала в общих чертах.

Для отображения количества шагов обучения сети я использую переменную ​( t )​. Очевидно, что наша цель – получить из старого значения веса связи ​( w_i(t) )​ новое значение ​( w_i(t+1) )​.

Для этого мы, как и в правилах Хебба, должны прибавить какое-то число к весу связи. Как раз эта добавка вычисляется по формуле ​( delta x_i eta )​. Разберем эту формулу.

Переменная ​( delta )​ есть ошибка нейросети. Заметим сходство с правилами Хебба. Если нейросеть ответила правильно, то ожидаемый и реальный результаты равны и ​( delta = 0 )​, а значит и вся добавка к весу связи равна ​( 0 )​. Вес не изменился.

В случае, если ​( delta > 0 )​, а значит ​( d > y )​, то значение добавки к весу будет положительное. Вес связи увеличится (1 правило Хебба). Это соответствует случаю, когда сеть получила на вход 5, но не узнала ее.

В случае, если ​( delta < 0 )​, а значит ​( d < y )​, то значение добавки к весу будет отрицательное. Вес связи уменьшится (2 правило Хебба). Это соответствует случаю, когда сеть неверно посчитала данное число за 5.

С ​( delta )​ разобрались. Теперь перейдем к ​( x_i )​. Это значение, которое пришло на ​( i )​-ый вход сети. Опять же, чем более сильный сигнал поступил на вход, тем сильнее изменится вес, с этим входом связанный. Это логично. Ведь если на вход вообще не поступило сигнала (​( x_i = 0 )​), то и соответствующий вес не должен изменится (добавка будет равна нулю).

А теперь самое интересное: Скорость обучения ​( eta )​.

Скорость обучения

Если с первыми двумя членами формулы добавки к весу ​( delta x_i eta )​ все просто и понятно, то постоянный коэффициент ​( eta )​, называемый коэффициентом скорости обучения, вызывает вопросы. Зачем он нужен?

Результат сети зависит от ее весов. Но раз зависит результат сети, то от весов зависит и ее ошибка (как разность постоянного «правильного» значения и результата сети). Представим, что погрешность нашей сети ​( delta )​ зависит от какого-то веса сети ​( w_i )​ следующим образом.

Положение кенгуру означает конкретный вес связи. Так в чем же фишка? А фишка в том, что кенгуру надо попасть в низину, так как именно в ней ошибка сети будет минимальна. Однако наш кенгуру может только прыгать. И именно за «длину прыжка» и отвечает коэффициент ​( eta )​ в формуле добавки к весу.

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

Ок, сделаем коэффициент ​( eta )​ большим. Ведь большими прыжками мы быстрее доскачем до низины? Нет. При большом значении скорости обучения есть опасность так и не доскакать до самого низа из-за того, что вы постоянно будете прыгать вправо-влево оставаясь на одной и той же высоте.

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

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

Практика: линейная аппроксимация

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

Пусть у нас имеется какой-то набор точек на плоскости.

X Y
1 2
2 4.2
2.5 5
3.8 7.9
4 9
6 10.2
6.6 13
7.2 15.3
8 17.1
8.5 19.5

Теперь давайте представим эти данные в виде картинки:

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

Например, у нас нет данных о Y координате точки с X = 3. Задача аппроксимации – с наибольшей вероятностью предсказать, какой будет Y для этой точки.

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

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

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

[ y=kx+c ]

Коэффициент ​( k )​ отвечает за крутизну наклона прямой, а ​( c )​ указывает точку на оси Y, через которую проходит эта прямая.

Мы ищем уравнение прямой, аппроксимирующее наши данные. Значит ​( y )​ у нас будет выходом сети. Теперь определимся со входами. Совершенно точно, что одним входом будет являться переменная ​( x )​. Однако в уравнении прямой фигурирует еще одно слагаемое: ​( c )​. О нем тоже нельзя забывать. ​( c )​ – постоянная величина. Поэтому мы добавим в нашу сеть второй вход, на который всегда будет подаваться единица. Таким образом произведение этого входа на вес всегда будет равно только этому весу, вне зависимости от входа (он всегда равен 1).

Функции активации у нас не будет. Взвешенная сумма и будет являться выходом нашей сети.

Вот так графически выглядит наш персептрон:

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

[ out=left(sumlimits^n_{i=1}w_i x_iright) ]

Это была запись в общем виде. А для нашего случая имеем:

[ out = w_2x+w_1 ]

Ничего эта запись вам не напоминает? Да это же уравнение прямой линии, где ​( out=y, w_2=k, w_1=c )​! Мы построили персептрон так, что в процессе обучения его весовые коэффициенты станут коэффициентами прямой, которую мы ищем!

Программа

Для начала вновь импортируем модуль для работы со случайными числами.

import random

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

# Коэффициент при x
k = random.uniform(-5, 5)

Функция uniform(from, to) генерирует случайное вещественное число от from и до to включительно. Заметьте, что мы уже не работаем с целыми числами.

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

# Свободный член уравнения прямой
c = random.uniform(-5, 5)

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

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

Далее мы должны вывести в консоль созданные переменные.

# Вывод данных начальной прямой
print('Начальная прямая: ', k, '* X + ', c)

Теперь задаем данные о наборе точек.

# Набор точек X:Y
data = {
    1: 2,
    2: 4.2,
    2.5: 5,
    3.8: 7.9,
    4: 9,
    6: 10.2,
    6.6: 13,
    7.2: 15.3,
    8: 17.1,
    8.5: 19.5
}

Здесь я использую не список, а словарь. Это практически тоже самое, что и список, только используются не числовые индексы, а собственноручно заданные имена – ключи. В данном словаре ключи это X, а соответствующие им значения это Y.

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

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

# Скорость обучения
rate = 0.0001

Теперь нужно создать функцию, высчитывающую ответ нашего персептрона:

# Высчитать y
def proceed(x):
    return x*k+c

Как видите, никакой функции активации тут нет. Ответ нашей сети есть взвешенная сумма, где ​( k )​ – вес связи при x, а ​( c )​ – вес связи при входе, всегда равном единице.

Теперь надо обучить нашу сеть. Чем больше шагов заложим, тем лучше. Возьмем, например, 100 000 шагов обучения. Хотя, возможно, для получения результата подошло бы и меньшее количество шагов. Тут есть простор для экспериментирования.

# Тренировка сети
for i in range(100000):
    # Получить случайную X координату точки
    x = random.choice(list(data.keys()))
 
    # Получить соответствующую Y координату точки
    true_result = data[x]
 
    # Получить ответ сети
    out = proceed(x)
 
    # Считаем ошибку сети
    delta = true_result - out
 
    # Меняем вес при x в соответствии с дельта-правилом
    k += delta*rate*x
 
    # Меняем вес при постоянном входе в соответствии с дельта-правилом
    c += delta*rate

На протяжении 100 000 раз мы выбираем из нашего словаря случайный ключ (так как ключ у меня это и есть значение X).

После этого создается переменная true_result, в которой хранится правильный Y для соответствующего X.

Далее, в переменную out мы помещаем ответ нашей сети для данного X.

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

Осталось только вывести данные о новой прямой.

# Вывод данных готовой прямой
print('Готовая прямая: ', k, '* X + ', c)

Привожу полный код программы:

import random
 
# Коэффициент при x
k = random.uniform(-5, 5)
 
# Свободный член уравнения прямой
c = random.uniform(-5, 5)
 
# Вывод данных начальной прямой
print('Начальная прямая: ', k, '* X + ', c)
 
# Набор точек X:Y
data = {
    1: 2,
    2: 4.2,
    2.5: 5,
    3.8: 7.9,
    4: 9,
    6: 10.2,
    6.6: 13,
    7.2: 15.3,
    8: 17.1,
    8.5: 19.5
}
 
# Скорость обучения
rate = 0.0001
 
# Высчитать y
def proceed(x):
    return x*k+c
 
# Тренировка сети
for i in range(100000):
    # Получить случайную X координату точки
    x = random.choice(list(data.keys()))
 
    # Получить соответствующую Y координату точки
    true_result = data[x]
 
    # Получить ответ сети
    out = proceed(x)
 
    # Считаем ошибку сети
    delta = true_result - out
 
    # Меняем вес при x в соответствии с дельта-правилом
    k += delta*rate*x
 
    # Меняем вес при постоянном входе в соответствии с дельта-правилом
    c += delta*rate
 
# Вывод данных готовой прямой
print('Готовая прямая: ', k, '* X + ', c)

Можно запускать! Корректные результаты должны получиться уже с первого раза. Как понять, что ваши результаты корректные? Значение ​( k )​ у вас должно быть в районе 2, а значение ​( c )​ — чем ближе к 0, тем лучше.

Вот, что получилось у меня.

а самом деле я намеренно задал уравнение вида ​( y=2x )​ и, как видно по результатам, сеть практически полностью повторила его.

Цифры это конечно хорошо, но на графиках выглядит еще лучше.

Здесь синие точки – это набор исходных данных. Коричневая прямая – это случайно полученная в начале программы прямая. Она может проходить как угодно и где угодно.

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

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

И ваша, и моя прямые, в свою очередь, практически идеально совпадают с первоначальной прямой (​( y=2x )​), из которой я намеренным искажением получил точки набора.

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

Выводы

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

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

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

Затем вы ознакомились с правилами Хебба и создали первую искусственную нейросеть, которая не только умеет отличать цифру 5 от всех остальных, но еще и умудряется распознавать ее в искаженных картинках. Это был пример использования персептрона как классификатора.

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

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

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

Другие языки программирования

Здесь приведены исходные коды программ для различных языков программирования. (Код для C# и PHP предоставлен пользователем под ником Geograph, код для Java написал poslannik).

C#

PHP

Java

Вопросы и задачи

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

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

Какие существуют виды персептронов? Что чаще всего имеют ввиду, когда говорят о персептроне?

Как надо понимать теоремы о сходимости персептронов?

X1 X2 Логическое НЕ И
0 0 1
1 0 1
0 1 1
1 1 0

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

Сформулируйте правила Хебба.

Сформулируйте дельта-правило.

В чем смысл коэффициента скорости обучения? Чем опасны его слишком большие/маленькие значения?

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

Одним из конкретных видов
стационарного СП. является так называемый
«стационарный белый
шум»
. Кратко
остановимся на это очень важное явление.

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


для
.

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

(39)

где
дельта
функция Дирака. Кратко рассмотрим
основные сведения об этой функции.

Представление
(39) выводится на основании теории
интегралов Фурье.

Дельта функция Дирака.

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

(40)

.

Правую часть равенства (40) можно
представить в виде предела: для любого

(41)
.

где


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

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

Рис. 67.

Равенство (39) также пишут в виде

.

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

Замечание. В
приложениях равенство (39) часто применяют
в форме

которое
выводится аналогично как выше (следует
все рассуждения в окрестности точки
).

Далее продолжим изучение «стационарного
белого шума». Вычислим корреляционную
функцию белого шума. На основании второй
формулы (35) с последующим использованием

формулы
(39) получим

.

То есть

(42)

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

Пример 14. Спектральная плотность
стационарной случайной функциипостоянна в диапазонеа
вне этого диапазона равна нулю, т.е.

Найти: 1. Корреляционную функцию;

2. Дисперсию случайного процесса
.

Решение. 1.Найдём искомую корреляционную
функцию:

,

Итак,

2. Найдём искомую дисперсию:

Следовательно,
на основании первого замечательного
предела получим

Тема 17. Марковские случайные процессы

1. Понятие Марковской цепи, марковские случайные процессы

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

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

Говорят, что последовательность испытаний
образуетцепь Маркова, точнее,
«простую цепь Маркова»,если
условная вероятность события

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

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

Для иллюстрации этого понятия (цепей
Маркова) рассмотрим примеры:

Пример 1. Представим, что частица,
находящаяся на прямой, движется по этой
прямой под влиянием случайных толчков,
происходящих в моменты.
Частица может находиться в точках с
целыми координатамипри
этом, в точкахинаходятся отражающие шкалы (стенки).
Каждый толчок перемещает частицу в
право с вероятностьюи
влево с вероятностью,
если только частица не находится у
стенки. Если же частица находится у
стенки, то любой толчок переводит её на
единицу внутрь промежутка между стенками.
Легко видит, что приведённый пример
«блуждания частицы» представляет
собой типичный пример цепи Маркова.
Аналогично можно было бы рассмотреть
случай, когда частица прилипает к одной
из стенок или к обеим стенкам.

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

Среди случайных процессов особое место
занимают марковские случайные процессы.

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

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

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

Другими словами, имеют место цепочка
равенств:

,

где
являются
возможные состояния системы.

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

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

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

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

Решение. Возможные состояние системы
(устройства):
пустьустройство
исправно,устройство
неисправно, требуется ремонт,устройство
неисправно, ремонту не подлежит, на
списание.

Процесс представляет собой случайное
блуждание системы
по состояниям, время проверки 1 час,
является шаг процесса. Граф системы
представлен на рисунке

Рис.68 (Письменный)

Одно из возможных реализация с.п.
блуждания системы может иметь такой
вид:
,
что означает: при 1-м, 2-м, 3-м осмотрах
устройство исправно; при 4-м осмотре
обнаружено неисправность, ремонтируется;
при 5-м, 6-м осмотрах обнаружено исправность,
при 7-м осмотре признано негодность,
устройство списано. Процесс закончился.

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

Естественно, что для любого момента
времени
сумма
вероятностей всех состояний равна
единице (как сумма вероятностей полной
группы несовместных событий):

Понравилась статья? Поделить с друзьями:
  • Как исправить готовый пересоленный суп
  • Как найти удаленную историю яндекс браузера на
  • Как можно исправить сломанные носы
  • Как найти бравла пятерочка
  • Как найти лампу в гта