Trung bình 0 đã giải Tỷ lệ chấp nhận 0%

Bộ tối ưu Adam

Cài đặt Adam, bộ tối ưu mặc định của học sâu, với trung bình trượt và hiệu chỉnh độ lệch.

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

  • Theo dõi trung bình moment bậc một và bậc hai
  • Hiệu chỉnh độ lệch ở các bước đầu
  • Cài đặt bộ tối ưu mặc định của deep learning

Đề bài

Xem bản gốc (English)

Adam giữ hai trung bình trượt cho mỗi trọng số, của gradient (m) và của bình phương gradient (v), rồi dùng chúng để mỗi trọng số có bước nhảy riêng.

Bắt đầu với m = 0 và v = 0 cho mọi trọng số. Với mỗi bước t = 1, 2, 3, … có gradient g:

  1. m ← beta1 × m + (1 - beta1) × g
  2. v ← beta2 × v + (1 - beta2) × g²
  3. m̂ = m / (1 - beta1ᵗ) và v̂ = v / (1 - beta2ᵗ) (hiệu chỉnh độ lệch)
  4. w ← w - learning_rate × m̂ / (√v̂ + epsilon)

Hãy cài đặt adam_steps(gradients, weights, learning_rate, beta1, beta2, epsilon), áp dụng một lần cập nhật cho mỗi hàng của gradients, theo thứ tự.

Đầu vào

gradients: Mỗi bước một hàng; mỗi hàng có một gradient cho mỗi trọng số.

weights: Các trọng số ban đầu.

learning_rate, beta1, beta2, epsilon: Các siêu tham số của Adam (thường là 0.001, 0.9, 0.999 và 0.00000001).

Đầu ra

Trả về các trọng số sau tất cả các bước.

Ràng buộc

0 ≤ số bước ≤ 1.000, 1 ≤ số trọng số ≤ 100

0 < learning_rate ≤ 1, 0 ≤ beta1, beta2 < 1, 0 < epsilon ≤ 0.001

-100 ≤ mỗi giá trị ≤ 100

Ví dụ

Ví dụ 1
Đầu vào:
beta1 = 0.9
beta2 = 0.999
epsilon = 1.0e-08
weights = [1.0]
gradients = [
    [2.0]
]
learning_rate = 0.1

Kết quả mong đợi:
expected = [0.9]
Đầu vào:
double beta1 = 0.9;
double beta2 = 0.999;
double epsilon = 1.0e-08;
std::vector<double> weights = {1.0};
std::vector<std::vector<double>> gradients = {
    {2.0}
};
double learning_rate = 0.1;

Kết quả mong đợi:
std::vector<double> expected = {0.9};
Đầu vào:
double beta1 = 0.9;
double beta2 = 0.999;
double epsilon = 1.0e-08;
double[] weights = new double[] {1.0};
double[][] gradients = new double[][] {
    new double[] {2.0}
};
double learning_rate = 0.1;

Kết quả mong đợi:
double[] expected = new double[] {0.9};

Giải thích

m = 0.1 × 2 = 0.2 và v = 0.001 × 4 = 0.004. Hiệu chỉnh độ lệch cho m̂ = 0.2 / 0.1 = 2 và v̂ = 0.004 / 0.001 = 4, nên bước nhảy là 0.1 × 2 / √4 = 0.1: w = 0.9. Ở bước đầu tiên, Adam dịch mỗi trọng số khoảng bằng learning rate, bất kể độ lớn của gradient.

Ví dụ 2
Đầu vào:
beta1 = 0.9
beta2 = 0.999
epsilon = 1.0e-08
weights = [0.0, 0.0]
gradients = [
    [100.0, 0.01]
]
learning_rate = 0.01

Kết quả mong đợi:
expected = [-0.01, -0.01]
Đầu vào:
double beta1 = 0.9;
double beta2 = 0.999;
double epsilon = 1.0e-08;
std::vector<double> weights = {0.0, 0.0};
std::vector<std::vector<double>> gradients = {
    {100.0, 0.01}
};
double learning_rate = 0.01;

Kết quả mong đợi:
std::vector<double> expected = {-0.01, -0.01};
Đầu vào:
double beta1 = 0.9;
double beta2 = 0.999;
double epsilon = 1.0e-08;
double[] weights = new double[] {0.0, 0.0};
double[][] gradients = new double[][] {
    new double[] {100.0, 0.01}
};
double learning_rate = 0.01;

Kết quả mong đợi:
double[] expected = new double[] {-0.01, -0.01};

Giải thích

Hai gradient chênh nhau 10.000 lần, nhưng cả hai trọng số đều dịch khoảng 0.01, thành [-0.01, -0.01]: Adam co giãn mỗi bước theo chính độ lớn của gradient đó.

Hướng tiếp cận

Hãy tự thử trước, sau đó mở từng gợi ý một.

Gợi ý 1

Theo dõi hai moving average cho mỗi weight: một cho hướng gradient và một cho bình phương độ lớn của nó.

Gợi ý 2

Áp dụng bias correction theo số bước hiện tại trước khi chia cho hạng tử căn bậc hai.

Gợi ý 3

Chú ý epsilon ở mẫu số, vì nó ngăn chia cho 0 và làm thay đổi nhẹ cập nhật cuối cùng.

Độ phức tạp mong đợi

thời gian O(s × w), bộ nhớ phụ O(w)

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.