Целью данной работы было разработать программное-алгоритмическое обеспечение позволяющее обучить однослойный перцептрон на основе произвольной обучающей выборки и рассчитать выходной вектор по заданному набору входных перменных. Провести исследование влияния настроек алгоритма на скорость и качество процесса обучения.
Небольшой обзор о том что из себя представляют нейроны и нейронная сеть я приводил в предыдущей статье - Зависимости выходного значения нейрона от синаптических коэффициентов. В этой статье поговорим о том же такое обучение нейронной сети.
Обучение нейронной сети представляет собой процедуру точного или итерационного расчета всех ее весовых коэффициентов, соединяющих внешние входы с нейронами и нейроны между собой.
Для обучения нейронной сети используется специально обучающая выборка, представляющая собой множество примеров, относящихся в одному объекту при определенных условиях его исследования.
Под эпохой понимается цикл однократного предъявления всех примеров обучающей выборки для коррекции весовых коэффициентов при одинаковых настройках самой сети и алгоритма ее обучения.
Входные сигналы целесообразно использовать в нормализованном виде.
Линейная нормализация и денормализация в пределах [-1, 1] подробно описано в статье Способы нормализации переменных:
(x_jk)norm=2*(X_ik-X_min)/(X_max-X_min)-1
y_jk=y_min+((y_jk)norm + 1)(y_max-y_min)/2
Группа искусственных нейронов, соединенных связями одного уровня с нейронами других групп, входами нейронной сети или друг с другом, образует отдельный слой нейронов.
Слой искусственных нейронов, выходы которого являются выходами всей нейронной сети называется выходным слоем. Все остальные слои искусственных нейронов, кроме выходного, называются скрытыми слоями.
Искусственная нейронная сеть с единственным выходным слоем называется однослойной сетью. Искусственная нейронная сеть с одним скрытым слоем называется двухслойной сетью и т.д.
Алгоритм:
1. Выбор и установка значений скорости и ошибки обучения.
2. Инициализация весовых коэффициентов значениями, близкими к 0.
3. Подача на вход НС векторов распознаваемых образов и вычисление выходных значений.
4. Вычисление разности между выходными и эталонными значениями. Если разность меньше ошибки обучения, заданной на первом шаге, то процедура завершается, иначе происходит модификация весовых коэффициентов и порога срабатывания и переходим к шагу 1.
Общая ошибка нейронной сети с обучающей выборкой оценивается по соотношению:
E = sqrt(1/(K*N) * sum(sum(Yjk(примера)-Yjk(расчетная)^2))
Коррекция весовых коэффициентов происходит по соотношению:
Wij(q+1)=Wij(q) + v(Yj(примера) - Yj(расчетная))Xj
Сложность алгоритма заключается в выборе скорости и ошибки обучения. Также большое значение на результат обучения НС оказывает количество подаваемых примеров.
Средой для разработки я снова выбрал Qt. Не знаю как вам, но мне она очень нравится. Описание одного нейрона из НС выглядит следующим образом:
class Neuron { private: QVectorw; QVector x; double energy, speed; double y1, y2, a, b, b0, b1, min, max, minY1, maxY1, minY2, maxY2; int kol_vh; int id_neuron, id_function; public: Neuron(); void set(int, int, int); void setW(int, int, QVector , int); void norm_in(bool);//Normalizaciya vhodnih dannih void out(); //Raschet Y void aktivation_Function(); void set_X(QVector ); void w_corekt(); void set_A_B(double, double, double, double, double, double, double, double, double, double, double); double get_out(); double get_eps(); QVector get_w(); double get_speed(); void set_speed(double); };
Немного о описанных методах и полях нейрона:
Стоит отметить что в описании класса Neuron также присутсвуют такие переменные как id_neuron и id_function. Так как для своих исследования я реализовал разные типы активационной функции то для того чтобы их менять была объявлена переменная id_function.
Про методы данного класса рассказывать не буду, в принципе названия функций говорят сами за себя.
Далее из нейронов собиралась соответсвующая нейронная сеть, количество нейронов в сети можно регулировать в главном меню приложения, но об этом чуть позже:
class NeuronSety { private: int kol_ep, kol_neu, kol_vh, kol_vh_d, id_function; double eps, a, b, b0, b1, epsilon_set, speed, min, max, minY1, maxY1, minY2, maxY2; QVectorneuron_seti; QVector >w; bool flag, flag2; public: NeuronSety(); NeuronSety(int kol_ep, int kol_neu, int kol_vh, bool flag, int id_function){this->kol_ep = kol_ep; this->kol_vh = kol_vh; this->kol_neu = kol_neu; this->flag = flag; this->flag2 = false; this->id_function = id_function;} NeuronSety(int kol_ep, int kol_neu, int kol_vh, QVector >w, bool flag2, int id_function){this->kol_ep = kol_ep; this->kol_vh = kol_vh; this->kol_neu = kol_neu; this->w = w; this->flag2 = flag2; this->flag = false; this->id_function = id_function;} void set_A_B_X(double a, double b, double kol_vh_d, double b0, double b1, double eps, double speed){ this->a = a; this->b = b; this->kol_vh_d = kol_vh_d; this->b0 = b0; this->b1 = b1; this->eps = eps; this->speed = speed;} double get_eps(int i){return neuron_seti[i].get_eps() - neuron_seti[i].get_out();} double get_Eps(){return this->epsilon_set;} QVector get_w(int i){return neuron_seti[i].get_w();} void zapuskator(); };
Программа писала достаточно давно и переделывать его времени не было, поэтому в ней достаточно много чего можно поменять, сократить и доработать, но она вполне рабочая и пользоваться можно.
Соответственно был разработан простой интерфейс и реализован алгоритм Удироу-Хоффа для обучения нейронной сети.
Входные параметры:
• b0, b1 - коэффициенты уравнения;
• Count generation - количество эпох;
• Epsilon - желательная погрешность нейронной сети;
• Neuron count - количество нейронов в сети;
• In count - количество входов у нейрона;
• The number of input data - количество данных посылаемых на обучение / тестирование нейронной сети;
• Speed - коэффициент скорости обучения нейронной сети.
Входные данные для нейронной сети считываются из файла test.txt который находится в одной папке с скомпилированным алгоритмом.
Результат программы выводится в QTableView в виде электронной таблицы.
После удачного обучения нейронной сети, можно сохранить параметры обученной сети нажав на кнопку Save. Для тестирования нейронной сети нужно нажать на Test (Не работает если до этого не была нажата Save).
Для вывода графика зависимости количества эпох от коэффициента скорости обучения необходимо запустить программу с разными значения коэффициента скорости и выбрать нужный пункт во вкладке графики.
Исследуем зависимость скорости обучения от количества эпох. Зададимся пороговым уровнем обучения нейронной сети 0,2. Начальные весовые коэффициенты: Wi = (i + 1) / 6, где i = 0...количество входных данных + 1.
Как видно по графику наилучшим коэффициентом скорости для данной нейронной сети является значение 0,9. При таком значении нейронная сеть обучается за 94 поколений.
Проведем обучение однослойного нейронного перцептрона с линейно убывающим коэффициентом скорости. Количество эпох возьмем лучшее значение из предыдущего эксперимента то есть 94 поколений. А шаг изменения коэффициента скорости (1 - 0) / 94 = 0,01.
Таким образом, для данной выборки из 12 обучающих примеров и для функции активации линейный порог [-1;1] лучшим вариантом является фиксированное значение коэффициента скорости обучения равное 0,9. Линейно убывающий коэффициент скорости показал ошибку 0,277 за 94 поколения. Пороговая ошибка обучения 0,2, а это значит что нейронная сеть не прошла обучение.
Выполним тестирование обученного нейронного перцептрона. Ошибка обучающей выборки составляла 0,2. При тестировании перцептрона с помощью независимой выборки из 5 примеров ошибка составила 0,66. Таким образом можно сделать вывод что нейронная сеть обучилась довольно неплохо.
Весь проект находится в репозитории