Это пример нейронной сети, решающей XOR, и сделанной с нуля
Схема сети: 2 узла на входе(i1, i2), 2 узла на скрытом слое(h1, h2), 1 выходной узел(o1)
На каждом уровне используется bias, функция активации - сигмоида.
Ramda - это вспомогательная библиотека а-ля lodash, которая используется исключительно для удобства в вспомогательных функциях. Можно было бы обойтись и без нее
var R = require('ramda');
void 0;
Используем воспроизводимый random, чтобы понять, что мы сделали не так
var random = require('seed-random')(1337);
Данные для обучения - это все возможные комбинации XOR:
Эти функции можно посмотреть на WolframAlpha:
https://www.wolframalpha.com/input/?i=1+%2F+(1+%2B+e+^+-x)
https://www.wolframalpha.com/input/?i=dx+1+%2F+(1+%2B+e+^+-x)
var activation_sigmoid = x => 1 / (1 + Math.exp(-x));
var derivative_sigmoid = x => {
const fx = activation_sigmoid(x);
return fx * (1 - fx);
};
var train = () => {
const weight_deltas = {
i1_h1: 0,
i2_h1: 0,
bias_h1: 0,
i1_h2: 0,
i2_h2: 0,
bias_h2: 0,
h1_o1: 0,
h2_o1: 0,
bias_o1: 0,
};
for (var {input: [i1, i2], output} of data) {
//это код, просто скопированный из функции выше - чтобы научить сеть, нужно сначала делать проход вперед
var h1_input =
weights.i1_h1 * i1 +
weights.i2_h1 * i2 +
weights.bias_h1;
var h1 = activation_sigmoid(h1_input);
var h2_input =
weights.i1_h2 * i1 +
weights.i2_h2 * i2 +
weights.bias_h2;
var h2 = activation_sigmoid(h2_input);
var o1_input =
weights.h1_o1 * h1 +
weights.h2_o1 * h2 +
weights.bias_o1;
var o1 = activation_sigmoid(o1_input);
//Обучение начинается:
// мы расчитываем разницу
var delta = output - o1;
// затем берем производную (и выкидываем 2 *, потому что это нам не так важно)
var o1_delta = delta * derivative_sigmoid(o1_input);
//и для нашей формулы вида w1 * h1 + w2 * h2 мы вначале пытаемся обновить веса w1 и w2
weight_deltas.h1_o1 += h1 * o1_delta;
weight_deltas.h2_o1 += h2 * o1_delta;
weight_deltas.bias_o1 += o1_delta;
//А затем входные значения h1 и h2.
//Мы не можем просто взять и изменить их - это выход такой же функции активации
//Поэтому мы пропускаем эту ошибку дальше по тому же принципу
var h1_delta = o1_delta * derivative_sigmoid(h1_input);
var h2_delta = o1_delta * derivative_sigmoid(h2_input);
weight_deltas.i1_h1 += i1 * h1_delta;
weight_deltas.i2_h1 += i2 * h1_delta;
weight_deltas.bias_h1 += h1_delta;
weight_deltas.i1_h2 += i1 * h2_delta;
weight_deltas.i2_h2 += i2 * h2_delta;
weight_deltas.bias_h2 += h2_delta;
}
return weight_deltas;
}
А это уже похоже на правду. Чем больше мы будем повторять дальше, тем ближе мы приблизимся к верному ответу.
Итак, мы только что научили наш код воспроизводить модель, которую ему показали на примерах входных данных.
Loading…
3 comments
posted 8 years ago by makha
А должны ли менятся весы от bias-нейронов? Они ведь должны указывать порог активации?
posted 7 years ago by solar
при подобном выборе начальных весов var random = require('seed-random')(1337) почти ни одно обучение не дает сходимости. Ошибка стремится к значению 0.4 и не уменьшается. Нейронка стала обучаться, когда начальные веса стал выбирать с учетом отрицательных чисел math.random(-10,10)*math.random()
posted 7 years ago by denix77
Ошибка в коде обучающего примера, вот и сходимость поэтому редкая птица. При расчете h1_delta и h2_delta нужно еще умножать на веса h1_o1 и h2_o2 соответственно. Угодил день, пока не почитал Хайкина.