Многослойный перцептрон. Часть 2

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

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

#include <iostream> 
#include "MLP.h"

using namespace std;

int main(int argc, char* argv[]) {
double sample[] = { 0.0, 0.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0 }; // набор аргументов
double ts[] = { 0.0, 1.0, 1.0, 0.0 }; // набор значений
double res;

MLP mlp1(2); // перцептрон с двумя слоями (нет скрытых)
mlp1.set_layer_size(0, 2); // первый слой - два узла
mlp1.set_layer_size(1, 1); // второй слой - один узел
mlp1.init(); // инициализация весов и т.д.
for(int i=0; i<100000; i++) { // 100000 эпох обучения
for(int j=0; j<4; j++) {
mlp1.set_input(&sample[j*2]); // ввод аргументов
mlp1.set_ts(&ts[j]); // предполагаемый вывод
mlp1.forwrd(); // распространение сигнала в прямом направлении
mlp1.backwrd(); // расчет ошибок и корректировка весов
};
};

cout << "--- MLP with no hidden layers ---" << endl << endl;
cout << "A\t" << "B\t" << "A XOR B" << endl;
for(int i=0; i<4; i++) {
cout << sample[i*2] << "\t" << sample[i*2+1] << "\t";
mlp1.set_input(&sample[i*2]);
mlp1.forwrd();
mlp1.get_output(&res);
cout << res << endl;
};
cout << endl;
MLP mlp2(3); // второй перцептрон, но с тремя слоями
mlp2.set_layer_size(0, 2);
mlp2.set_layer_size(1, 3); // 3 узла во втором (скрытом) слое
mlp2.set_layer_size(2, 1);
mlp2.init();
for(int i=0; i<100000; i++) { // обучение второго перцептрона
for(int j=0; j<4; j++) {
mlp2.set_input(&sample[j*2]);
mlp2.set_ts(&ts[j]);
mlp2.forwrd();
mlp2.backwrd();
};
};
cout << "--- MLP with one hidden layer ---" << endl << endl;
cout << "A\t" << "B\t" << "A XOR B" << endl;
for(int i=0; i<4; i++) {
cout << sample[i*2] << "\t" << sample[i*2+1] << "\t";
mlp2.set_input(&sample[i*2]);
mlp2.forwrd();
mlp2.get_output(&res);
cout << res << endl;
};
return 0;
};

Следует отметить, что инициализировать веса перцептрона нужно случайными значениями от 0 до 1 для конкретной задачи. Для этого в файле MLP.h заменим строку

for(int j=0; j<tL0->w.size(); j++) tL0->w[j] = ((double)(rand() % tL0->n.size()) + 1) / (10*tL0->n.size());

на строку

for(int j=0; j<tL0->w.size(); j++) tL0->w[j] = ((double)(rand() % 1000) + 1) / 1000;

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

--- MLP with no hidden layers ---
A B A XOR B
0 0 0.523517
0 1 0.512693
1 0 0.509908
1 1 0.499071
--- MLP with one hidden layer ---
A B A XOR B
0 0 0.00204197
0 1 0.998262
1 0 0.998262
1 1 0.00172549

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

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

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

Добавить комментарий

Ваш e-mail не будет опубликован.