Đề bài
XOR (hoặc loại trừ) là bài toán kinh điển mà một nơ-ron đơn không giải được, nhưng mạng có một lớp ẩn thì giải được. Hãy huấn luyện mạng có 2 đầu vào, 2 nơ-ron ẩn và 1 đầu ra, tất cả dùng sigmoid (σ(z) = 1 / (1 + e^(-z))), với hàm mất mát L = (y - target)².
Dữ liệu huấn luyện, luôn dùng theo đúng thứ tự này ở mọi epoch:
| đầu vào | target |
|---|---|
| [0, 0] | 0 |
| [0, 1] | 1 |
| [1, 0] | 1 |
| [1, 1] | 0 |
Với mỗi ví dụ:
- Lan truyền xuôi: 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), và delta_j = delta_out × output_weights[j] × a_j × (1 - a_j), dùng trọng số trước khi cập nhật cho ví dụ này
- Cập nhật mọi tham số với learning rate 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
Hãy cài đặt train_xor(hidden_weights, hidden_biases, output_weights, output_bias, learning_rate, epochs).
Đầu ra
Sau khi huấn luyện, trả về dự đoán y của mạng cho các đầu vào [0, 0], [0, 1], [1, 0] và [1, 1], theo đúng thứ tự đó.
Ràng buộc
0 ≤ epochs ≤ 5.000
0 < learning_rate ≤ 5
-2 ≤ mỗi trọng số và bias ban đầu ≤ 2
Ví dụ
Đầu vào:
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]
Kết quả mong đợi:
expected = [0.482451, 0.436972, 0.494378, 0.452729]
Đầu vào:
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};
Kết quả mong đợi:
std::vector<double> expected = {0.482451, 0.436972, 0.494378, 0.452729};
Đầu vào:
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};
Kết quả mong đợi:
double[] expected = new double[] {0.482451, 0.436972, 0.494378, 0.452729};
Giải thích
Với 0 epoch thì chưa huấn luyện gì, nên kết quả chỉ là lan truyền xuôi với trọng số ban đầu: khoảng [0.482, 0.437, 0.494, 0.453]. Mạng chưa huấn luyện trả lời gần như giống nhau cho mọi đầu vào.
Đầu vào:
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]
Kết quả mong đợi:
expected = [0.015727, 0.982119, 0.985515, 0.013682]
Đầu vào:
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};
Kết quả mong đợi:
std::vector<double> expected = {0.015727, 0.982119, 0.985515, 0.013682};
Đầu vào:
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};
Kết quả mong đợi:
double[] expected = new double[] {0.015727, 0.982119, 0.985515, 0.013682};
Giải thích
Sau 2.000 epoch, các dự đoán vào khoảng [0.016, 0.982, 0.986, 0.014]: thấp với [0, 0] và [1, 1], cao với [0, 1] và [1, 0]. Mạng đã học được XOR.
Hướng tiếp cận
Hãy tự thử trước, sau đó mở từng gợi ý một.
Gợi ý 1
Duyệt bốn mẫu XOR theo cùng một thứ tự cố định ở mọi epoch, và cập nhật tham số ngay sau từng mẫu.
Gợi ý 2
Dùng forward propagation, rồi backpropagate lỗi đầu ra vào cả hai neuron hidden với weights hiện tại trước khi cập nhật.
Gợi ý 3
Chú ý đạo hàm sigmoid và việc cập nhật tầng hidden phụ thuộc vào giá trị input, nên input bằng 0 sẽ làm một số weight không đổi.
Độ phức tạp mong đợi
thời gian O(epochs), bộ nhớ phụ O(1)
Tham khảo
Thử thách này đã được điều chỉnh cho CodeUtility. Hãy làm theo đề bài và các ràng buộc ở trên.