Нейронная сеть. Растет ошибка только на последнем сете обучающей выборки
Реализовал свою нейронную сеть и решил проверить ее работоспособность.
Структура нейронной сети: 3 входных нейрона, скрытый слой с 2 нейронами и 2 выходных нейрона.
Обучающая выборка:
double_t** trainSet = new double_t * [] {
new double_t[]{ 0, 0, 0 },
new double_t[]{ 0, 1, 0 },
new double_t[]{ 1, 0, 0 },
new double_t[]{ 1, 1, 0 },
new double_t[]{ 0, 0, 1 },
new double_t[]{ 0, 1, 1 },
new double_t[]{ 1, 0, 1 },
new double_t[]{ 1, 1, 1 },
new double_t[]{ 0, 0, 2 },
new double_t[]{ 0, 1, 2 },
new double_t[]{ 1, 0, 2 },
new double_t[]{ 1, 1, 2 }
};
Первый и второй нейрон это значения операндов, а третий нейрон это номер операции 1 - &, 2 - | или 3 - ^ (xor). Эти числа нормализуются в 0, 0.5 и 1 соответственно.
Вот последние результаты работы:

Как видно на приложенном скрине, ошибки во всех сетах, кроме последнего уменьшается. И если я из обучающей выборки уберу этот сет, то нейронная сеть будет отлично работать.
В чем может быть проблема? Как её исправить? Я проверил код функций и вроде не нашел ошибки, но если нужно попросите и я дополню вопрос кодом.
Дополнено
Ошибка выходного нейрона: error = (ideal - output) * derivative(output)
Ошибка скрытого нейрона: error[i] = derivative(output[i]) * summa(weights[i][i+1] * error[i+1]
Ошибка скрытого нейрона = значение производной * на сумму произведений веса между этим нейроном и нейроном следующего слоя, на ошибку нейрона следующего слоя.
Ошибка для выбранного сета:
trainError = summa(ideals[i] - output[i]) / countOutputNeurons
Ошибка эпохи: epochError = summa(trainError[i]) / countSets
Ошибка эпохи = сумма ошибок каждого сета разделенного на количество сетов.
Дополнено
Убрал умножение на производную при поиске ошибок выходных нейронов и нейронка наконецто обучилась, а не зациклилась.
И получил следующее:
Почему то при входных значениях (1, 1) нейронная сеть хуже обучается.
Объясните пожалуйста кто разбирается почему так.
При начальной структуре (3 входных, 2 скрытых и 2 выходных), ошибки некоторых сетов например на 50 эпохе < 0.00001, у большинства в районе (0.01, 0.09) и только проклятого двенадцатого сета ошибка растет.
Обновлено
В общем проблема заключается именно в том, чтобы научить нейронную сеть операции xor и именно сету (1, 1). Ошибка уменьшается медленней чем при других сетах.
Добавил случайное перемешивание сетов каждую эпоху, что вроде ускорило обучение (раньше было в среднем 250+ эпох сейчас 150+).
Но тут есть еще одна проблема, решение которой я не могу найти. Если раз десять перезапустить обучение, то почему то может произойти такое, что нейронная сеть не будет обучаться и зациклиться на одних ошибках. Это как то случайно получается.
Ответы (2 шт):
Посмотрите, у меня на Python(можно конвертировать) такая функция вспомогательная:
ready = False
y = 0
def operations(op, x, nn_params: Nn_params):
global ready, y
alpha_leaky_relu = nn_params.alpha_leaky_relu
alpha_sigmoid = nn_params.alpha_sigmoid
alpha_tan = nn_params.alpha_tan
beta_tan = nn_params.beta_tan
if op == RELU:
if (x <= 0):
return 0
else:
return x
elif op == RELU_DERIV:
if (x <= 0):
return 0
else:
return 1
elif op == TRESHOLD_FUNC:
if (x > 0.5):
return 1
else:
return 0
elif op == TRESHOLD_FUNC_DERIV:
return 1
elif op == LEAKY_RELU:
if (x <= 0):
return alpha_leaky_relu
else:
return 1
elif op == LEAKY_RELU_DERIV:
if (x <= 0):
return alpha_leaky_relu
else:
return 1
elif op == SIGMOID:
y = 1 / (1 + math.exp(- alpha_sigmoid * x))
return y
elif op == SIGMOID_DERIV:
return (alpha_sigmoid * y * (1 - y))
elif op == SOFTMAX_DERIVATE:
return 1
elif op == INIT_W_MY:
if ready:
ready = False
return -0.567141530112327
ready = True
return 0.567141530112327
elif op == INIT_W_RANDOM:
return random.random()
elif op == TAN:
f = alpha_tan * math.tanh(beta_tan * x)
return f
elif op == TAN_DERIV:
return beta_tan / alpha_tan * (alpha_tan * alpha_tan - y * y)
elif op == INIT_W_CONST:
return 0.567141530112327
elif op == INIT_RANDN:
return np.random.randn()
else:
print("Op or function does not support ", op)
Справа op это enum, попробуйте инициализацию, возьмите одну OR/AND или XOR, простые операции мне помогли TRESHOLD_FUNC и INIT_W_MY(так даже для "нерешаемой" XOR)
У вас оказывается довольно большая конфигурируемая сеть(не 3 слоя), и мне стала непонятна архитектура, поэтому предлагаю сначала изменить код так(более логично):
struct NeuralNetwork
{
private:
struct Lay
{
int _in;
int _out;
Matrix<double_t> *matrix; // веса
Vector<double_t> *hidden; // вектор после функции активации
Vector<double_t> *errors; // вектор ошибок
double_t getRandomWeight() { return (double_t)(rand()) / RAND_MAX - 0.5; };
public:
Lay(int in, int out) : _in(in), _out(out) // 1 слой
{
matrix = new Matrix<double_t>(_in, _out); // Создание матрицы весов
srand(time(NULL));
for (int row = 0; row < _out; row++) // ее заполнение
for (int elem = 0; elem < _in; elem++)
matrix[row][elem] = getRandomWeight();
hidden = new Vector<double_t>(out);
errors = new Vector<double_t>(out);
}
};
Vector<Lay> *net; // сеть как стек Слоев
size_t count_layers;
double_t E;
double_t a;
//Функция активации
double_t (*activationFunc)(const double_t value) = [](const double_t value) {
return value > 0.5 ? (double_t)1 : 0;
};
//Производная функции активации
double_t (*derivativeFunc)(const double_t value) = [](const double_t value) {
return (double_t)1;
};
public:
NeuralNetwork(const size_t *dimensions, const size_t size,
const double_t E = 0.5, const double_t a = 0.5)
{
this->count_layers = size;
this->net = new Vector<Lay>(this->count_layers);
for (size_t i = 0; i < this->count_layers; i++)
this->net[i]=new Lay(dimensions[i + 1], dimensions[i]);
this->E = E;
this->a = a;
};
...
}
