XOR (исключающее ИЛИ) — классическая задача, которую не может решить один нейрон, но может решить сеть с одним скрытым слоем. Обучите сеть с 2 входами, 2 скрытыми нейронами и 1 выходом, все сигмоидные (σ(z) = 1 / (1 + e^(-z))), с функцией потерь L = (y - target)².
Обучающие данные, которые в каждой эпохе всегда используются в этом порядке:
| вход | target |
|---|---|
| [0, 0] | 0 |
| [0, 1] | 1 |
| [1, 0] | 1 |
| [1, 1] | 0 |
Для каждого примера:
- Прямой проход: a_j = σ(x₁ × hidden_weights[j][0] + x₂ × hidden_weights[j][1] + hidden_biases[j]), y = σ(a₁ × output_weights[0] + a₂ × output_weights[1] + output_bias)
- delta_out = 2 × (y - target) × y × (1 - y) и delta_j = delta_out × output_weights[j] × a_j × (1 - a_j), с весами до обновления на этом примере
- Обновите каждый параметр со скоростью обучения lr: output_weights[j] -= lr × delta_out × a_j, output_bias -= lr × delta_out, hidden_weights[j][i] -= lr × delta_j × x_i, hidden_biases[j] -= lr × delta_j
Реализуйте train_xor(hidden_weights, hidden_biases, output_weights, output_bias, learning_rate, epochs).
Выходные данные
После обучения верните предсказания сети y для входов [0, 0], [0, 1], [1, 0] и [1, 1] в этом порядке.
Ограничения
0 ≤ epochs ≤ 5000
0 < learning_rate ≤ 5
-2 ≤ каждый начальный вес и смещение ≤ 2
Примеры
Ввод:
epochs = 0
output_bias = 0.0
hidden_biases = [0.0, 0.3]
learning_rate = 1.5
hidden_weights = [
[0.2, 0.8],
[0.9, -0.7]
]
output_weights = [-0.6, 0.4]
Ожидаемый вывод:
expected = [0.482451, 0.436972, 0.494378, 0.452729]
Ввод:
long long epochs = 0;
double output_bias = 0.0;
std::vector<double> hidden_biases = {0.0, 0.3};
double learning_rate = 1.5;
std::vector<std::vector<double>> hidden_weights = {
{0.2, 0.8},
{0.9, -0.7}
};
std::vector<double> output_weights = {-0.6, 0.4};
Ожидаемый вывод:
std::vector<double> expected = {0.482451, 0.436972, 0.494378, 0.452729};
Ввод:
long epochs = 0;
double output_bias = 0.0;
double[] hidden_biases = new double[] {0.0, 0.3};
double learning_rate = 1.5;
double[][] hidden_weights = new double[][] {
new double[] {0.2, 0.8},
new double[] {0.9, -0.7}
};
double[] output_weights = new double[] {-0.6, 0.4};
Ожидаемый вывод:
double[] expected = new double[] {0.482451, 0.436972, 0.494378, 0.452729};
Пояснение
При 0 эпох ничего не обучается, поэтому результат — это просто прямой проход с начальными весами: примерно [0.482, 0.437, 0.494, 0.453]. Необученная сеть даёт почти одинаковый ответ на любой вход.
Ввод:
epochs = 2000
output_bias = 0.0
hidden_biases = [0.0, 0.3]
learning_rate = 1.5
hidden_weights = [
[0.2, 0.8],
[0.9, -0.7]
]
output_weights = [-0.6, 0.4]
Ожидаемый вывод:
expected = [0.015727, 0.982119, 0.985515, 0.013682]
Ввод:
long long epochs = 2000;
double output_bias = 0.0;
std::vector<double> hidden_biases = {0.0, 0.3};
double learning_rate = 1.5;
std::vector<std::vector<double>> hidden_weights = {
{0.2, 0.8},
{0.9, -0.7}
};
std::vector<double> output_weights = {-0.6, 0.4};
Ожидаемый вывод:
std::vector<double> expected = {0.015727, 0.982119, 0.985515, 0.013682};
Ввод:
long epochs = 2000;
double output_bias = 0.0;
double[] hidden_biases = new double[] {0.0, 0.3};
double learning_rate = 1.5;
double[][] hidden_weights = new double[][] {
new double[] {0.2, 0.8},
new double[] {0.9, -0.7}
};
double[] output_weights = new double[] {-0.6, 0.4};
Ожидаемый вывод:
double[] expected = new double[] {0.015727, 0.982119, 0.985515, 0.013682};
Пояснение
После 2000 эпох предсказания примерно равны [0.016, 0.982, 0.986, 0.014]: низкие для [0, 0] и [1, 1], высокие для [0, 1] и [1, 0]. Сеть выучила XOR.
Как подступиться
Сначала попробуйте сами, затем открывайте подсказки по одной.
Подсказка 1
Обрабатывайте все четыре XOR-примера в одном и том же фиксированном порядке на каждой эпохе и сразу обновляйте параметры после каждого.
Подсказка 2
Сначала выполните прямой проход, затем распространите ошибку выхода назад в оба скрытых нейрона с текущими весами, и только потом применяйте обновления.
Подсказка 3
Следите за производной sigmoid и за тем, что обновления скрытого слоя зависят от значений входа, поэтому при нулевых входах часть весов не меняется.
Ожидаемая сложность
время O(epochs), доп. память O(1)
Ссылки
Это задание адаптировано для CodeUtility. Следуйте условию и ограничениям выше.