โจทย์
Adam เก็บค่าเฉลี่ยเคลื่อนที่สองตัวต่อน้ำหนัก คือของ gradient (m) และของ gradient ยกกำลังสอง (v) แล้วใช้ค่าเหล่านี้กำหนดขนาดก้าวเฉพาะให้แต่ละน้ำหนัก
เริ่มด้วย m = 0 และ v = 0 สำหรับทุกน้ำหนัก สำหรับแต่ละก้าว t = 1, 2, 3, … ที่มี gradient g:
- m ← beta1 × m + (1 - beta1) × g
- v ← beta2 × v + (1 - beta2) × g²
- m̂ = m / (1 - beta1ᵗ) และ v̂ = v / (1 - beta2ᵗ) (การแก้ความเอนเอียง)
- w ← w - learning_rate × m̂ / (√v̂ + epsilon)
จง implement adam_steps(gradients, weights, learning_rate, beta1, beta2, epsilon) โดยอัปเดตหนึ่งครั้งต่อแถวของ gradients ตามลำดับ
อินพุต
gradients: หนึ่งแถวต่อก้าว แต่ละแถวมี gradient หนึ่งค่าต่อน้ำหนัก
weights: น้ำหนักเริ่มต้น
learning_rate, beta1, beta2, epsilon: ไฮเปอร์พารามิเตอร์ของ Adam (ค่าที่ใช้ทั่วไปคือ 0.001, 0.9, 0.999 และ 0.00000001)
เอาต์พุต
คืนค่าน้ำหนักหลังจากทุกก้าว
เงื่อนไข
0 ≤ จำนวนก้าว ≤ 1,000, 1 ≤ จำนวนน้ำหนัก ≤ 100
0 < learning_rate ≤ 1, 0 ≤ beta1, beta2 < 1, 0 < epsilon ≤ 0.001
-100 ≤ แต่ละค่า ≤ 100
ตัวอย่าง
อินพุต:
beta1 = 0.9
beta2 = 0.999
epsilon = 1.0e-08
weights = [1.0]
gradients = [
[2.0]
]
learning_rate = 0.1
ผลลัพธ์ที่คาดหวัง:
expected = [0.9]
อินพุต:
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;
ผลลัพธ์ที่คาดหวัง:
std::vector<double> expected = {0.9};
อินพุต:
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;
ผลลัพธ์ที่คาดหวัง:
double[] expected = new double[] {0.9};
คำอธิบาย
m = 0.1 × 2 = 0.2 และ v = 0.001 × 4 = 0.004 การแก้ความเอนเอียงได้ m̂ = 0.2 / 0.1 = 2 และ v̂ = 0.004 / 0.001 = 4 ดังนั้นก้าวคือ 0.1 × 2 / √4 = 0.1: w = 0.9 ในก้าวแรก Adam ขยับแต่ละน้ำหนักประมาณเท่ากับ learning rate ไม่ว่า gradient จะใหญ่แค่ไหน
อินพุต:
beta1 = 0.9
beta2 = 0.999
epsilon = 1.0e-08
weights = [0.0, 0.0]
gradients = [
[100.0, 0.01]
]
learning_rate = 0.01
ผลลัพธ์ที่คาดหวัง:
expected = [-0.01, -0.01]
อินพุต:
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;
ผลลัพธ์ที่คาดหวัง:
std::vector<double> expected = {-0.01, -0.01};
อินพุต:
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;
ผลลัพธ์ที่คาดหวัง:
double[] expected = new double[] {-0.01, -0.01};
คำอธิบาย
Gradient ต่างกันถึง 10,000 เท่า แต่ทั้งสองน้ำหนักขยับประมาณ 0.01 ไปเป็น [-0.01, -0.01]: Adam ปรับสเกลแต่ละก้าวตามขนาดของ gradient ของตัวเอง
แหล่งอ้างอิง
โจทย์นี้ปรับให้เหมาะกับ CodeUtility กรุณาทำตามคำชี้แจงและข้อจำกัดด้านบน