問題
Adam は重みごとに、勾配(m)と勾配の二乗(v)の2つの移動平均を持ち、それを使って各重みに固有のステップ幅を与えます。
すべての重みについて m = 0、v = 0 から始めます。勾配 g を持つ各ステップ t = 1, 2, 3, … について:
- 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)
adam_steps(gradients, weights, learning_rate, beta1, beta2, epsilon) を実装し、gradients の各行ごとに1回、順番に更新してください。
入力
gradients: ステップごとに1行。各行は重みごとに1つの勾配を持ちます。
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 は勾配の大きさに関係なく各重みを学習率ほど動かします。
入力:
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};
解説
2つの勾配は 10,000 倍違いますが、どちらの重みも約 0.01 動いて [-0.01, -0.01] になります。Adam は各ステップを勾配自身の大きさでスケーリングします。
参考資料
この課題はCodeUtility用に調整されています。上記の説明と制約に従ってください。