Quay lại danh sách thử thách
Khó 0 đã giải Tỷ lệ chấp nhận 0%

Huấn luyện mạng nơ-ron giải bài toán XOR

Kết hợp tất cả: huấn luyện mạng sigmoid 2-2-1 bằng lan truyền ngược cho đến khi học được XOR.

Bạn sẽ học được gì

  • Huấn luyện một mạng nhỏ từ đầu đến cuối
  • Hiểu vì sao XOR cần lớp ẩn
  • Kết hợp lan truyền xuôi và ngược

Đề bài

Xem bản gốc (English)

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àotarget
[0, 0]0
[0, 1]1
[1, 0]1
[1, 1]0

Với mỗi ví dụ:

  1. 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)
  2. 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
  3. 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ụ

Ví dụ 1
Đầ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.

Ví dụ 2
Đầ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.

Học hỏi từ người khác

Giải pháp cộng đồng

Chưa có giải pháp nào được chia sẻ. Hãy giải thử thách này để đăng giải pháp đầu tiên.