Нейронная Сеть На Схеме

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

Заодно расскажу подробно, как все это работает для новичков в жанре.

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



Нейронная сеть на схеме

Начнем с описания биологической составляющей.

Если опустить подробности, нейрон — это сложная клетка с несколькими входами — дендритами и одним выходом — аксоном.

Входы нейронов периодически получают электрические сигналы от органов чувств или других нейронов.

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

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



Нейронная сеть на схеме

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

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

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



Нейронная сеть на схеме

Нейроны группируются в слои.

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

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

Сигнал распространяется только в одном направлении — от входного слоя к выходному слою.

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

У кого-то может возникнуть вопрос – для чего все это? Фактически нейронная сеть позволяет решать задачи, которые сложно описать стандартными алгоритмами.

Одной из типичных целей является распознавание изображений.

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

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

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

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

Использовался язык Scheme, который, ИМХО, является самым красивым и лаконичным диалектом LISP. Реализация написана на MIT-Scheme, но должна без проблем работать на других интерпретаторах.

В реализации используются следующие типы списков: (n lst) – нейрон.

нейрон вместо lst содержит список с коэффициентами усиления сигналов синапса.

Например: (n (1 2 4)).

(l лст) – слой.

слой, состоящий из нейронов.

Вместо lst он содержит список нейронов.

Пример слоя из 2-х нейронов (l ( (n (0,1 0,6 0,2)) (n (0,8 0,4 0,4)) )) (lst) – обычный список, без какого-либо символа в начале списка – обычная нейронная сеть.

Вместо lst он содержит список слоев.

Например: ( (l ((n (0,1 0,6 0,2)) (n (0,5 0,1 0,7)) (n (0,8 0,4 0,4)))) (l ((n (0,3 0,8 0,9)) (n (0,4 0,9 0,1)) (n (0,9 0,9 0,9))))) ) В коде используются некоторые сокращения: ; out – выход нейрона – выход нейрона ; wh – вес – вес соединения ; res — result — результат расчета всего слоя нейронов ; akt – активация – результат активации нейрона ; n – нейрон ; л – слой ; сеть-сеть ; МК-сделай ; proc-процесс Начнем с создания нейрона, это скучно и не интересно:

(define (n-mk lst) ;make new neuron (list 'n lst))

Перейдем к созданию слоя:

(define (l-mk-new input n lst) ;makes new layer (let ( (lst1 (append lst (list (n-mk (lst-rand input))))) ) (if (= n 1) (l-mk lst1) (l-mk-new input (- n 1) lst1))))

вход – количество входов в нейроны этого слоя n — количество нейронов в слое.

lst – список готовых нейронов.

Зная количество входов, мы заказываем как можно больше случайных чисел из lst-rand, упаковываем их с помощью n-mk и помещаем в lst1. Проверяем, нужны ли еще нейроны; если это был не последний нейрон, то уменьшаем счетчик и повторяем операцию.

Если это последний нейрон, упаковываем их с помощью l-mk. Далее мы создадим саму сеть.

Это делается с помощью функции net-make. Lay – список с количеством нейронов для каждого слоя.

Первое число — это список входных данных для первого слоя.



(define (net-make lay) (net-mk-new (car lay) (cdr lay) '()))

Эта функция является синтаксическим сахаром и предназначена для упрощения вызова.

На самом деле это будет называться net-make-new, который просто имеет отдельный параметр для количества входов на первый уровень.

Функция l-mk-new вызывается рекурсивно для создания слоев и добавления их в lst1.

(define (net-mk-new input n-lst lst) (let ( (lst1 (lst-push-b (l-mk-new input (car n-lst) '()) lst)) ) (if (= (length n-lst) 1) lst1 (net-mk-new (car n-lst) (cdr n-lst) lst1))))

Теперь у нас есть только что созданная сеть.

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

Функция активации выбрана:

(define (n-akt x param) ;neuron activation function (/ x (+ (abs x) param)))

Для тех, кто не знаком с синтаксисом Lisp, это просто x/(|x|+p), где p — параметр, который делает график функции более четким или гладким.



(define (n-proc neuron input) ;process single neuron (n-akt (lst-sum (lst-mul (n-inp-lst neuron) input )) 4))

нейрон – содержит активированный нейрон, input – входные значения синапса.

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

4 – параметр функции активации, выбран экспериментально.

Вы можете смело поменять его на любой другой.

Теперь приступим к активации всего слоя.



(define (l-proc l input) ;proceses a layer (map (lambda(x)(n-proc x input)) (n-inp-lst l)))

вход – выходы нейронов предыдущего слоя, l – сам слой n-inp-lst — возвращает список нейронов этого слоя.

Давайте поднимемся на уровень выше и создадим функцию активации всей сети.



(define (net-proc net input) (let ( (l (l-proc (car net) input)) ) (if (= 1 (length net)) l (net-proc (cdr net) l))))

net – сама сеть, вход – данные, подаваемые на входы первого слоя.

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

Если слой последний, его результат отображается как результат всей сети.

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

Теперь перейдем к более интересной части – тренировкам.

Здесь мы пойдем сверху вниз.

Те.

с сетевого уровня на более низкие уровни.

Функция обучения сети – сетевое исследование.



(define (net-study net lst spd) ;studys net for each example from the list smpl ((inp)(out)) (if (net-check-lst net lst) net (net-study (net-study-lst net lst spd) lst spd)))

где net — сама сеть, lst - список примеров, spd — скорость обучения.

net-check-lst — проверяет корректность ответов сети на примеры из lst net-study-lst — выполняет обучение сети Скорость обучения — это приращение изменения весов каждого нейрона.

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

net-study выполняется до тех пор, пока не будет пройден net-check-lst.

(define (net-study-lst net lst spd) (let ( (x (net-study1 net (caar lst) (cadar lst) spd)) ) (if (= 1 (length lst)) x (net-study-lst x (cdr lst) spd)))) (define (net-study1 net inp need spd) (net-study2 net (l-check inp) need spd))

L-проверка добавлена сюда.

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



(define (net-study2 net inp need spd) (let ( (x (net-study3 net inp need spd))) (if (= (caar (lst-order-max need))(caar (lst-order-max (net-proc x inp)))) x (net-study2 x inp need spd))))

Net-study2 сравнивает максимальное число выходных данных в шаблоне с фактическим максимальным числом выходных данных.

Если они совпадают, мы возвращаем обученную сеть; если нет, продолжаем обучение.



(define (net-study3 net inp need spd) (let ((err (net-spd*err spd (slice (net-err net inp need) 1 0))) (wh (net-inp-wh net)) (out (slice (net-proc-res-out net inp) 0 -1))) (net-mk-data (net-err+wh wh (map (lambda(x y)(lst-lst-mul x y)) out err)))))

wh — список входящих весов всех нейронов сети out — содержит список выходов всех нейронов сети err — список ошибок для каждого нейрона Net-spd*err — умножает скорость со списком ошибок на веса нейрона.

Net-err+wh — добавляет рассчитанную ошибку в список весов.

Net-make-data создает новую сеть из полученных данных.

Ну а теперь самое интересное — вычисление ошибки.

Для изменения весов будет использоваться функция:

Нейронная сеть на схеме

Где Wij — вес от нейрона i до нейрона j, Xi — выход нейрона i, R – шаг обучения, Gj — значение ошибки для нейрона j. При расчете ошибки для выходного слоя используется функция:

Нейронная сеть на схеме

Где Dj — желаемый выход нейрона j, Yj — текущий выход нейрона j. Для всех предыдущих слоев используется функция:

Нейронная сеть на схеме

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

(define (net-err net inp need) ;networks error list for each neuron (let ( (wh-lst (reverse (net-out-wh net))) (err-lst (list (net-out-err net inp need))) (out-lst (reverse (slice (net-proc-res-out net inp) 0 -1)))) (net-err2 out-lst err-lst wh-lst))) (define (net-err2 out-lst err-lst wh-lst) (let ( (err-lst1 (lst-push (l-err (car out-lst) (car err-lst) (first wh-lst)) err-lst)) ) (if (lst-if out-lst wh-lst) err-lst1 (net-err2 (cdr out-lst) err-lst1 (cdr wh-lst)))))

Теперь немного о том, что происходит: Wh-lst – содержит значения связей каждого нейрона со следующим слоем, Err-lst — содержит ошибку выходного слоя сети, в дальнейшем мы будем пополнять его ошибками других слоев, Out-lst — выходное значение каждого нейрона Мы обрабатываем эти три списка рекурсивно в соответствии с функцией.

Теперь, собственно, как все это работает. Пример будет простым, но при желании можно распознавать и более сложные вещи.

У нас будет всего четыре простых шаблона для букв Т, О, И, У.

Объявляем их отображение: (определить t'( 1 1 1 0 1 0 0 1 0)) (определить o '( 1 1 1 1 0 1 1 1 1)) (определить я '( 0 1 0 0 1 0 0 1 0)) (определить тебя '( 1 0 1 1 0 1 1 1 1)) Теперь создадим форму для обучения.

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

(определить буквы (перечислить (список o '(1 0 0 0)) (список t '(0 1 0 0)) (список я '(0 0 1 0)) (список и'(0 0 0 1)))) Теперь давайте создадим новую сеть с 9 входами, 3 слоями и 4 выходами.

(определить тест (net-make '(9 8 4))) Давайте обучим сеть на нашем примере.

Письма - список примеров.

0,5 — шаг обучения.

(определить test1 (буквы теста net-study 0,5)) В результате у нас есть сеть test1, которая распознает наши шаблоны.

Давайте проверим это: (net-proc test1 o) > (.

3635487227069449 .

32468771459315143 .

20836372502023912 .

3635026264793502) Чтобы было немного понятнее, вы можете сделать это: (net-proc-num test1 o) > 0 Те.

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

То же самое для других шаблонов: (net-proc-num test1 i) > 2 Можем немного повредить изображение и проверить, распознается ли оно: (net-proc-num test1 '( 0 1 0 1 0 1 0 1 0)) > 0 (net-proc-num test1 '( 0 0 1 0 1 0 0 1 0)) > 1 Даже поврежденное изображение вполне различимо.

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

(net-proc test1 '( 0 1 1 0 1 0 0 0 0)) > (.

17387815810580473 .

2731127800467817 .

31253464734295566 -6.323399331678244e-3) Как видите, сеть предлагает 1 и 2 выхода.

Те.

шаблоны настолько похожи, что сеть считает шаблон 2 более вероятным.

PS Наверное, это все слишком сумбурно, но, возможно, кому-то будет интересно.

PSS Отдельный запрос к ЛИСПерам.

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

Источники: 1. oasis.peterlink.ru/~dap/nneng/nnlinks/nbdoc/bp_task.htm#learn - за особенности и вдохновение 2. ru.wikipedia.org/wiki/Artificial_neural_network — отсюда украдена картинка и некоторая информация 3. alife.narod.ru/lectures/neural/Neu_ch03.htm - Я взял фотографию отсюда 4. iiclub.рф/neur-1.html Полный исходный код на Pastbin: Pastebin.com/erer2BnQ Теги: #искусственный интеллект #нейронные сети #Lisp #схема #схема

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.