Đề bài
Hạ gradient theo mini-batch là cách huấn luyện gần như mọi mạng nơ-ron: chia dữ liệu thành các lô nhỏ và cập nhật trọng số sau mỗi lô.
Huấn luyện y = w × x + b trên sai số bình phương trung bình, bắt đầu từ w = 0 và b = 0. Ở mỗi epoch, chia các mẫu theo thứ tự thành các lô gồm batch_size mẫu (lô cuối có thể ít hơn). Với mỗi lô có n mẫu:
- errorᵢ = w × xᵢ + b - yᵢ cho từng mẫu trong lô
- dw = (2 / n) × Σ errorᵢ × xᵢ và db = (2 / n) × Σ errorᵢ
- w ← w - learning_rate × dw và b ← b - learning_rate × db
Hãy cài đặt train_mini_batch(xs, ys, batch_size, learning_rate, epochs).
Đầu vào
xs, ys: Các điểm dữ liệu (cùng độ dài).
batch_size: Số mẫu mỗi lô.
learning_rate, epochs: Bước nhảy và số lượt duyệt qua dữ liệu.
Đầu ra
Trả về [w, b] sau khi huấn luyện.
Ràng buộc
1 ≤ số điểm ≤ 1.000, 1 ≤ batch_size ≤ số điểm, 0 ≤ epochs ≤ 1.000
0 < learning_rate ≤ 1
-100 ≤ mỗi giá trị ≤ 100
Ví dụ
Đầu vào:
xs = [1.0, 2.0, 3.0]
ys = [2.0, 4.0, 6.0]
epochs = 1
batch_size = 2
learning_rate = 0.1
Kết quả mong đợi:
expected = [2.44, 1.08]
Đầu vào:
std::vector<double> xs = {1.0, 2.0, 3.0};
std::vector<double> ys = {2.0, 4.0, 6.0};
long long epochs = 1;
long long batch_size = 2;
double learning_rate = 0.1;
Kết quả mong đợi:
std::vector<double> expected = {2.44, 1.08};
Đầu vào:
double[] xs = new double[] {1.0, 2.0, 3.0};
double[] ys = new double[] {2.0, 4.0, 6.0};
long epochs = 1;
long batch_size = 2;
double learning_rate = 0.1;
Kết quả mong đợi:
double[] expected = new double[] {2.44, 1.08};
Giải thích
Lô 1 là x = [1, 2]: sai số -2 và -4, dw = -10, db = -6, nên w = 1 và b = 0.6. Lô 2 chỉ có x = [3]: sai số 1×3 + 0.6 - 6 = -2.4, dw = 2 × (-2.4) × 3 = -14.4 và db = -4.8, nên w = 2.44 và b = 1.08. Kết quả: [2.44, 1.08].
Đầu vào:
xs = [1.0, 2.0, 3.0, 4.0]
ys = [3.0, 5.0, 7.0, 9.0]
epochs = 1
batch_size = 4
learning_rate = 0.05
Kết quả mong đợi:
expected = [1.75, 0.6]
Đầu vào:
std::vector<double> xs = {1.0, 2.0, 3.0, 4.0};
std::vector<double> ys = {3.0, 5.0, 7.0, 9.0};
long long epochs = 1;
long long batch_size = 4;
double learning_rate = 0.05;
Kết quả mong đợi:
std::vector<double> expected = {1.75, 0.6};
Đầu vào:
double[] xs = new double[] {1.0, 2.0, 3.0, 4.0};
double[] ys = new double[] {3.0, 5.0, 7.0, 9.0};
long epochs = 1;
long batch_size = 4;
double learning_rate = 0.05;
Kết quả mong đợi:
double[] expected = new double[] {1.75, 0.6};
Giải thích
Một lô lớn bằng cả tập dữ liệu chính là hạ gradient theo lô thông thường: mỗi epoch một lần cập nhật. Kết quả: [1.75, 0.6].
Hướng tiếp cận
Hãy tự thử trước, sau đó mở từng gợi ý một.
Gợi ý 1
Xử lý các mẫu theo đúng thứ tự đã cho, cập nhật tham số sau mỗi batch thay vì sau từng điểm riêng lẻ.
Gợi ý 2
Với mỗi batch, tính prediction error trước, rồi dùng trung bình của chúng với giá trị x để lấy hai gradient.
Gợi ý 3
Batch cuối có thể nhỏ hơn các batch khác, nên việc scale gradient phải dùng kích thước của chính batch đó.
Độ phức tạp mong đợi
thời gian O(epochs × n), 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.