コンテンツへスキップ
3/30第3章 / 全30章

下り坂:Gradient Descentと、誰もが飛ばす2つのステップ

学習率の厳密な上限を計算し、3,600方向の総当たり探索が、教えられずにgradientを再発見する様子を見ます。

このページの内容

前章は谷で終わりました。

比喩としての谷ではありません。実際の曲線です。損失を1つのパラメータに対してプロットすると、下がって底を打ち、また上がっていく。その下にある損失は、扱いやすいから選ばれたものではありません。測定ノイズについての仮定から導かれ、その結果として二乗誤差が現れました。慣習ではなく、帰結としてです。

つまり、底のある地形があり、その底こそが行くべき場所だと信じる理由もあります。まだないのは、そこへたどり着く方法です。

この章ではその方法を作ります。そしてそれは、このコースの残りで登場するすべてのモデルを訓練するアルゴリズムです。例外なくすべて、数千億パラメータを持つものまで含めてです。コードにすれば20行ほどに収まります。難しい部分はその20行の中にはありません。ほとんどすべての説明が飛ばす、次の2つです。

  • なぜマイナス符号なのか。 更新ではgradientを引きます。どのチュートリアルもそう書きますが、なぜgradientが上りの方向なのかを説明するものはほとんどありません。この事実だけが、マイナス符号を信仰ではないものにします。
  • どれだけ大きく進むのか。 「大きすぎると発散し、小さすぎると遅い」は正しく、そして役に立ちません。厳密な数値があります。損失から計算でき、この章ではそれを2回計算します。1回はおもちゃの放物線で、もう1回は実データでです。

この章だけで読めるように言い直します。第1章のコンベヤーベルトから来た8個の部品を使いますが、問うことを変えます。受け入れるか拒否するかではありません。それは後で戻ってきます。ここでは、部品の幅から重さを予測します。

belt.pyPYTHON
import numpy as np

WIDTH  = np.array([18.0, 19.5, 20.2, 21.0, 24.0, 25.5, 23.0, 26.0])
WEIGHT = np.array([47.0, 52.0, 49.0, 55.0, 61.0, 66.0, 70.0, 58.0])

x = WIDTH  - WIDTH.mean()      # 22.15 mm
y = WEIGHT - WEIGHT.mean()     # 57.25 g

測定値は中心化されています。第1章とまったく同じで、この章が終わる前にその理由は利子つきで戻ってきます。モデルは直線 y^=ax+b\hat{y} = a x + b で、損失は前章で導いた平均二乗誤差です。

L(a,b)=1ni=1n(axi+byi)2L(a, b) = \frac{1}{n} \sum_{i=1}^{n} \left(a x_i + b - y_i\right)^2

パラメータは2つです。たくさん値を試せばよいのではないでしょうか。実際にやってみましょう。a=0a = 0 から 55 まで、b=5b = -5 から 55 までを、刻み幅 0.010.01 のグリッドで試します。

TEXT
grid 501 x 1001 = 501,501 evaluations in 3.67 s
  best found: a = 2.1000, b = -0.0000, L = 24.592450

2つの数値を小数第2位まで突き止めるのに、50万回の評価が必要です。そしてその秒数は1台のマシンでの壁時計時間なので、再実行すれば3秒から6秒のどこかに落ちます。再現するのは評価回数と最小値です。この章の最後でgradient descentは、8ステップで小数第4位まで、36ステップでfloat64の完全な答えまで到達します。

しかし速度が論点ではありません。ここがコース全体を決めるポイントです。グリッド探索は、PP 個のパラメータそれぞれについて kk 個の値を試すなら、kPk^P 回の評価を要します。各軸に1,000個の値があるとすると、こうなります。

モデルパラメータグリッド評価回数
この直線210610^{6}
第5章のXORネットワーク9102710^{27}
小さな多層ネットワーク20,0001060,00010^{60{,}000}

3行目は大きな数ではありません。意味を失った数です。観測可能な宇宙にある原子は、およそ 108010^{80} 個です。探索はモデルが大きくなると遅くなるのではありません。存在できなくなります。以降のすべては、この表があるから存在します。

ひとまず b=0b = 0 を固定し、パラメータが1つ、曲線が1本だけの状態にします。前章が最後に残した絵です。その上の点 a=1a = 1 を取り、こう問います。aa を小さな量 hh だけずらしたら、損失はずらし1単位あたりどれだけ動くでしょうか。

L(a+h)L(a)h\frac{L(a + h) - L(a)}{h}

この比は上昇量を水平距離で割ったもの、つまり曲線上の2点を通る直線の傾きです。hh が小さくなるにつれて、2点は互いに近づき、直線は接線になります。その傾きが導関数 L(a)L'(a) です。aa の変化1単位あたり、損失が変化する速さです。何かの近似ではなく、無限小の量でもありません。普通の比の極限です。

実行してみる価値があります。数字が、定義だけでは見えないことを語るからです。

slope.pyPYTHON
def loss1(a):
    return np.mean((a * x - y) ** 2)

for h in [1.0, 1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12, 1e-14]:
    q = (loss1(1.0 + h) - loss1(1.0)) / h
    print(f"h = {h:<8.0e}   slope estimate = {q:.10f}   error = {abs(q + 16.385):.3e}")
TEXT
h = 1e+00      slope estimate = -8.9400000000    error = 7.445e+00
h = 1e-02      slope estimate = -16.3105500000   error = 7.445e-02
h = 1e-04      slope estimate = -16.3842555001   error = 7.445e-04
h = 1e-06      slope estimate = -16.3849925556   error = 7.444e-06
h = 1e-08      slope estimate = -16.3850003787   error = 3.787e-07
h = 1e-10      slope estimate = -16.3850444324   error = 4.443e-05
h = 1e-12      slope estimate = -16.3851154866   error = 1.155e-04
h = 1e-14      slope estimate = -17.0530256582   error = 6.680e-01

ここでは2つのことが起きていて、どちらも本質的です。

誤差は hh に何となく比例しているのではありません。正確に 7.445h7.445\,h です。 hh を100分の1にすると、誤差も100分の1になります。毎回、有効数字4桁までそうなります。この定数は飾りではありません。損失の2階導関数の半分であり、2つ先の節で出てくる考え方の初登場です。すなわち、点の近くの曲線は、直線に h2h^2 に比例する補正を加えたものに見える、という考え方です。

そしてパターンが壊れます。 h=108h = 10^{-8} より下では推定が悪くなり、101410^{-14} では2桁目から間違います。数学的なことが起きたのではありません。前章の浮動小数点の箱が起きました。L(a+h)L(a+h)L(a)L(a) は最初の10桁が一致し、それらを引き算するとその桁が破壊されます。残骸を小さな数で割ると、残った誤差が増幅されます。最適な hh があります。ここでは 10810^{-8} あたり、機械イプシロンの平方根くらいです。小さくするほど慎重になるのではありません。むしろ不慎重になります。覚えておいてください。この章の最後に出てくる関数が、それに依存します。

測定ではなく微積分から得る正確な傾きは 16.385-16.385 です。これで測定をやめ、導出を始められます。

このコースの残りが依って立つ考え方を、ここで一度だけ、平明に述べます。

2つの関数を合成するとは、一方をもう一方に入力することです。(fg)(x)=f(g(x))(f \circ g)(x) = f(g(x))。それ以上のことではありません。

深いネットワークは合成に似ているのではありません。合成そのものです。層は関数であり、層を積み重ねることはそれらを合成することです。「深さ」とは、その鎖に含まれる関数の数です。第5章でネットワークを作るとき、作っているのは f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 であり、それ以外ではありません。つまり、私たちの目的にとって微積分で最も重要な規則は、合成を微分する規則です。

ddxf(g(x))=f(g(x))g(x)\frac{d}{dx} f(g(x)) = f'(g(x)) \cdot g'(x)

変化率は掛け合わされます。 ggxx の3倍の速さで変化し、ffgg の2倍の速さで変化するなら、ffxx の6倍の速さで変化します。内容はそれで全部です。そしてそれが、10層を逆向きに通る信号が10個の数を掛けられる理由です。だから第6章では、それらの数がすべて1より少し小さいと何が起きるかに1節を費やします。

これを私たちの損失に使います。残差を ri=axi+byir_i = a x_i + b - y_i と書くと、L=1nri2L = \frac{1}{n}\sum r_i^2 です。各 rir_i は、内側の関数 axia x_i を通じて aa に依存し、その導関数は xix_i です。項ごとに連鎖律を適用します。

La=1ni2rixi,Lb=1ni2ri1\frac{\partial L}{\partial a} = \frac{1}{n}\sum_i 2 r_i \cdot x_i, \qquad \frac{\partial L}{\partial b} = \frac{1}{n}\sum_i 2 r_i \cdot 1

波打った \partial の記号は偏導関数を表します。1つの変数について微分し、他のすべてを定数として扱うということです。新しいことは何も起きません。前と同じ極限を、1つの軸に沿って取っているだけです。偏導関数をベクトルにまとめると、gradientが得られます。

L=(La, Lb)\nabla L = \left( \frac{\partial L}{\partial a},\ \frac{\partial L}{\partial b} \right)

(a,b)=(1,4)(a, b) = (1, 4) で、このベクトルは (16.385, 8.0)(-16.385,\ 8.0) です。2つの数です。問題は、それが何を意味するかです。そしてここが、誰もが飛ばす最初のステップです。

gradientは、軸に沿った傾きのベクトルです。ここまでで証明したのはそれだけです。それらをベクトルに組み立てると、どこか特定の方向を向く、というのは自明ではありません。自明であってはいけません。

そこで、私たちが本当に欲しいものを定義します。単位ベクトル u\mathbf{u}、つまり方向を1つ選びます。方向微分とは、その方向に歩いたときに損失が変わる速さです。

DuL=limh0L(θ+hu)L(θ)hD_{\mathbf{u}} L = \lim_{h \to 0} \frac{L(\boldsymbol{\theta} + h\mathbf{u}) - L(\boldsymbol{\theta})}{h}

連鎖律により、これは計算可能なものになります。u\mathbf{u} に沿って歩くと、aau1u_1 の速さで、bbu2u_2 の速さで変わり、それらの寄与が足し合わされます。

DuL=Lau1+Lbu2=LuD_{\mathbf{u}} L = \frac{\partial L}{\partial a} u_1 + \frac{\partial L}{\partial b} u_2 = \nabla L \cdot \mathbf{u}

任意の方向の変化率は、gradientとその方向の内積です。そして結論は、幾何の1行です。2つのベクトルの間の角度を ϕ\phi として内積を書くと、

Lu=Lucosϕ=Lcosϕ\nabla L \cdot \mathbf{u} = \lVert \nabla L \rVert \, \lVert \mathbf{u} \rVert \cos\phi = \lVert \nabla L \rVert \cos\phi

u\mathbf{u} の長さは1なのでこうなります。あなたが制御できるのは cosϕ\cos\phi だけです。これは ϕ=0\phi = 0 で最大になり、半回転した ϕ=180\phi = 180 度で最小になります。つまり、

  • 最急上昇L\nabla L そのものの方向で、その傾きは正確に L\lVert \nabla L \rVert です。
  • 最急下降L-\nabla L の方向で、その傾きは L-\lVert \nabla L \rVert です。
  • gradientに垂直な方向では、損失はまったく変わりません。等高線図の線がgradientと直角に交わるのはそのためです。

これがマイナス符号です。慣習ではなく、誰かが選んだ符号反転でもありません。最速で減少する方向が負のgradientであるのは、cosϕ\cos\phi が半回転で最小化されるからであり、それ以外の理由はありません。

これは全方向についての主張なので、全方向に対してテストします。3,600方向を、0.1度ごとに1つずつサンプリングし、それぞれを少しずらして測ります。

directions.pyPYTHON
theta = np.array([1.0, 4.0])
g = grad(theta)
print("gradient      ", g)
print("its length    ", np.linalg.norm(g))
print("its angle     ", np.degrees(np.arctan2(g[1], g[0])) % 360, "degrees")

best = max(
    ((loss(theta + 1e-6 * u) - loss(theta - 1e-6 * u)) / 2e-6, np.degrees(ang))
    for ang, u in (
        (a, np.array([np.cos(a), np.sin(a)])) for a in np.arange(3600) * 2 * np.pi / 3600
    )
)
print("steepest slope", best[0], "at", best[1], "degrees")
TEXT
gradient       [-16.385   8.   ]
its length     18.23371122399386
its angle      153.97598928042032 degrees
steepest slope 18.233709624837502 at 154.0 degrees

gradientについて何も知らない探索が、3,600方向の中から最も急な上りを154.0度に見つけます。探索の0.1度の解像度の範囲で、gradient自身の方向です。そしてそこで見つけた傾き18.2337は、gradientの長さと6桁一致します。この定理はgradientが何を意味するかについての物語ではありません。測定可能な事実であり、これがその測定です。

次は2つ目の飛ばされがちなステップです。どちらが下りかは分かりました。しかし、その方向に歩けば損失が下がるとは限りません。「下り」は無限小のずらしについての発言であり、ステップは無限小ではないからです。

橋渡しになるのが線形化です。滑らかな関数は、ある点の近くでは接線に補正を加えたものです。

L(θ+δ)=L(θ)+Lδ+O(δ2)L(\boldsymbol{\theta} + \boldsymbol{\delta}) = L(\boldsymbol{\theta}) + \nabla L \cdot \boldsymbol{\delta} + O(\lVert\boldsymbol{\delta}\rVert^2)

これは1次のTaylor展開です。捨てられた O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) が曲率です。傾きの表の推定を正確に 7.445h7.445\,h だけ間違わせた、あの項です。これから取るつもりのステップ δ=ηL\boldsymbol{\delta} = -\eta \nabla L を入れます。

L(θηL)L(θ)ηL2L(\boldsymbol{\theta} - \eta \nabla L) \approx L(\boldsymbol{\theta}) - \eta \lVert \nabla L \rVert^2

損失は ηL2\eta \lVert \nabla L \rVert^2 だけ下がります。その各部分は非負なので、約束は本物です。ただし、十分小さい η\eta に対してです。無視した項は η2\eta^2 のように増え、やがてそれを食い尽くすからです。理論はこれで全部です。約束が守られ、そして破られる様子を見ます。

TEXT
eta = 0.2       promised    66.49364500   delivered   -16.01619240   ratio -0.240868
eta = 0.1       promised    33.24682250   delivered    12.61936315   ratio  0.379566
eta = 0.01      promised     3.32468225   delivered     3.11840766   ratio  0.937957
eta = 0.001     promised     0.33246822   delivered     0.33040548   ratio  0.993796
eta = 0.0001    promised     0.03324682   delivered     0.03322620   ratio  0.999380
eta = 1e-05     promised     0.00332468   delivered     0.00332448   ratio  0.999938

下から読みます。η\eta が小さくなるにつれ、実際に得られた低下量は約束された低下量に収束します。比は0.99938、次に0.99994です。Taylorの定理が正しいということです。上から読むと、η=0.2\eta = 0.2 では実際の「低下量」はマイナス16です。ステップは下りに進んだのに、損失は上がりました。

したがって更新規則は

θθηL(θ)\boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta \nabla L(\boldsymbol{\theta})

です。そして誰も述べない条件が付いています。η\eta が十分小さいことです。では、正確には何と比べて十分小さいのでしょうか。次の節です。

最も単純な谷 f(x)=x2f(x) = x^2 から始めます。ここで f(x)=2xf'(x) = 2x です。gradient descentの1ステップは

xxη2x=x(12η)x \leftarrow x - \eta \cdot 2x = x\,(1 - 2\eta)

です。

位置は各ステップで (12η)(1 - 2\eta) 倍されます。これは等比数列です。そして等比数列には規則が1つだけあります。倍率の絶対値が1より小さいと縮み、そうでなければ成長します。したがって 12η<1\lvert 1 - 2\eta \rvert < 1、つまり 0<η<10 < \eta < 1 です。

境界は正確に η=1\eta = 1 です。 「1くらい」ではなく、「1はたいてい大きすぎる」でもありません。η=1\eta = 1 では倍率が 1-1 となり、点は xxx-x の間を永遠に跳ね返ります。近づくことも逃げることもありません。それ未満なら収束し、それを超えると発散します。この区間は η=0.5\eta = 0.5 でもう一度分かれます。そこで倍率の符号が変わるからです。それ未満では単調に近づき、それを超えると点は行き過ぎて左右を交互に移ります。ちょうど 0.50.5 では倍率が0になり、たった1ステップで最小値に着地します。

4行の代数から4つの領域が出ます。自分で境界を越えてみてください。

14 ステップ、x = -0.0836 で終了。

表形式でデータを見る
ステップxf(x)
0⁨-1.9000⁩⁨3.6100⁩
1⁨-1.5200⁩⁨2.3104⁩
2⁨-1.2160⁩⁨1.4787⁩
3⁨-0.9728⁩⁨0.9463⁩
4⁨-0.7782⁩⁨0.6057⁩
5⁨-0.6226⁩⁨0.3876⁩
6⁨-0.4981⁩⁨0.2481⁩
7⁨-0.3985⁩⁨0.1588⁩
8⁨-0.3188⁩⁨0.1016⁩
9⁨-0.2550⁩⁨0.0650⁩
10⁨-0.2040⁩⁨0.0416⁩
11⁨-0.1632⁩⁨0.0266⁩
12⁨-0.1306⁩⁨0.0170⁩
13⁨-0.1045⁩⁨0.0109⁩
14⁨-0.0836⁩⁨0.0070⁩
勾配降下法(インタラクティブ)

x=1.9x = -1.9 から、率0.1で14ステップ、最後は 0.0836-0.0836 です。率を0.5に押し上げると、最初の1ステップで底に着地します。0.9まで押し上げると、0.1のときと同じ 0.0836-0.0836 で終わります。距離は同じで、スタイルが逆です。どちらも 12η\lvert 1 - 2\eta \rvert が0.8だからです。ただし、片側を歩いて下るのではなく、谷をジグザグに横切ってそこへ向かいます。

そして興味深いものがこちらです。

14 ステップ、x = -1.9000 で終了。

表形式でデータを見る
ステップxf(x)
0⁨-1.9000⁩⁨3.6100⁩
1⁨1.9000⁩⁨3.6100⁩
2⁨-1.9000⁩⁨3.6100⁩
3⁨1.9000⁩⁨3.6100⁩
4⁨-1.9000⁩⁨3.6100⁩
5⁨1.9000⁩⁨3.6100⁩
6⁨-1.9000⁩⁨3.6100⁩
7⁨1.9000⁩⁨3.6100⁩
8⁨-1.9000⁩⁨3.6100⁩
9⁨1.9000⁩⁨3.6100⁩
10⁨-1.9000⁩⁨3.6100⁩
11⁨1.9000⁩⁨3.6100⁩
12⁨-1.9000⁩⁨3.6100⁩
13⁨1.9000⁩⁨3.6100⁩
14⁨-1.9000⁩⁨3.6100⁩
勾配降下法(インタラクティブ)

境界ちょうどです。率1で14ステップ進むと、最後は 1.9-1.9 です。始点と正確に同じ場所で、跳ね返っただけです。ほんの少し上げると、跳ね返りは保たれるのではなく大きくなります。1.2では4ステップでグラフの外へ出ます。大きすぎる率はゆっくり収束するのではありません。収束しません。

一般則も、同じ議論から出てきます。倍率 12η1 - 2\eta は実は 1ηf1 - \eta f'' であり、最小値の近くでは、多パラメータの損失に方向ごとにそのような数が1つあります。2階導関数の行列の固有値です。すべての方向が同時に安定でなければならないので、上限は最大のものによって決まります。

η<2λmax\eta < \frac{2}{\lambda_{\max}}

f(x)=x2f(x) = x^2 では f=2f'' = 2、上限は1で、これは今導いた通りです。私たちのベルトでは、2階導関数の行列は 2nAA\frac{2}{n} A^{\top} A で、AA は入力の2列行列です。その固有値は2と14.89なので、上限は 2/14.89=0.134322 / 14.89 = 0.13432 です。これは有効数字5桁を持つ予測です。テストします。

TEXT
lr=0.1343    -> L =      24.5924
lr=0.13431   -> L =      24.5924
lr=0.13432   -> L =       4707.8       BLEW UP
lr=0.13433   -> L =  4.00452e+16       BLEW UP
lr=0.1344    -> L = 1.18229e+107       BLEW UP

線形代数の1行と、for ループ10万回との一致は小数第5位までです。

そしてここで第1章が戻ってきます。 ここまでのすべては中心化された測定値を使っていました。生のミリメートルとグラムに対して同じコードを実行すると、固有値は2と14.89ではなく、0.0298と998.1になります。上限は0.134から0.002004へ崩れます。同じく厳密に、lr=0.002003 では収束し、lr=0.002004 では吹き飛びます。

上限より悪いのが、固有値同士の比です。条件数は、谷がどれほど丸くないかを測ります。長く細い溝では、急な壁に合わせて率を小さくせざるを得ず、その同じ遅さで溝の床を歩くことになります。私たちの場合、中心化では7.44だったものが、生では33,452になります。それぞれが取れる最良の率を使うと、こうなります。

特徴量条件数最良の率最適値の1%以内に到達するステップ数
中心化7.440.118410
生のミリメートルとグラム33,4520.002003779,513

同じデータ、同じコード、最後の答えも同じです。ただし平均を引かなかっただけで、作業量は8,000倍です。第1章では、同じ省略がパーセプトロンにエポック数6,000倍の代償を払わせました。そこでの診断は幾何でした。データが原点から遠く浮いていたのです。ここでも同じ幾何が、最適化の衣装を着て現れています。だから入力の正規化は衛生上の助言ではなく、算術なのです。1

ここまでのことにライブラリは不要でした。これがoptimiser全体です。

descent.pyPYTHON
def loss(theta):
    a, b = theta
    return np.mean((a * x + b - y) ** 2)


def grad(theta):
    a, b = theta
    residual = a * x + b - y
    return np.array([np.mean(2 * residual * x), np.mean(2 * residual)])


def descend(theta, lr, steps):
    theta = np.array(theta, dtype=float)
    for _ in range(steps):
        theta = theta - lr * grad(theta)   
    return theta


theta = descend([0.0, 0.0], lr=0.05, steps=60)
print(theta, loss(theta))
TEXT
[ 2.10040296e+00 -2.76445533e-15] 24.592448791134984

この8点に対する閉形式の最小二乗解は a=2.100403a = 2.100403b=0b = 0 で、損失は 24.59244924.592449 です。ループは、閉形式が存在することを知らないまま、有効数字8桁までそれを見つけました。これが重要なのは、第5章以降では閉形式が存在しないからです。

軌跡です。見ることこそが要点です。

TEXT
   0 a=0.000000 b=0.000000 L=57.437500
   1 a=1.563750 b=0.000000 L=26.736582
   2 a=1.963288 b=-0.000000 L=24.732418
   5 a=2.098116 b=-0.000000 L=24.592488
  10 a=2.100400 b=-0.000000 L=24.592449
  60 a=2.100403 b=-0.000000 L=24.592449

距離の大半は最初の2ステップで進みます。底から遠いほどgradientが大きく、近づくほど小さくなるからです。gradient descentは最小値の近くで自動的に遅くなります。それは機能であり、第6章では問題でもあります。

ここまでの議論には穴があります。ステップは L=0\nabla L = \mathbf{0} で止まり、私たちはそれを「最小値」と呼んできました。gradientがゼロの点は臨界点であり、最小値であることは、その一種にすぎません。

  • 局所最小値:どの方向にも上りだが、全体で最も低い点とは限らない。
  • 局所最大値:どの方向にも下り。
  • 鞍点:ある方向では上り、別の方向では下り。曲面 f(x,y)=x2y2f(x,y) = x^2 - y^2f=(2x,2y)\nabla f = (2x, -2y) を持ち、これは原点でゼロです。そこでは関数は xx 軸に沿っては最小であり、同時に yy 軸に沿っては最大です。

gradient descentはこれらを区別できません。見るのは常にgradientだけであり、3つすべてでgradientはゼロだからです。

私たちの直線には臨界点が1つだけあり、それが答えです。線形モデル上の二乗誤差損失はで、単一のボウルであり、その上の降下は大域最小値を見つけ損ねることがありません。この性質は、このコースと接触すると生き残りません。ニューラルネットワークの損失は凸ではありません。第5章以降、「その最小値」というものは存在しません。深さの異なる多数の最小値があり、どれを得るかは開始点に依存します。これは1文であり、1文のままにします。理論は大きく、実用上の帰結は小さいからです。

その帰結全体は1本の曲線で見られます。深さの異なる2つの谷を持つ f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10} を取ります。

TEXT
   x =  -1.046681   f(x) =  -0.352386   minimum
   x =   0.101031   f(x) =   0.005026   maximum
   x =   0.945649   f(x) =  -0.152639   minimum

40 ステップ、x = 0.9456 で終了。

表形式でデータを見る
ステップxf(x)
0⁨0.1100⁩⁨0.0050⁩
1⁨0.1122⁩⁨0.0050⁩
2⁨0.1149⁩⁨0.0049⁩
3⁨0.1182⁩⁨0.0049⁩
4⁨0.1223⁩⁨0.0048⁩
5⁨0.1275⁩⁨0.0047⁩
6⁨0.1338⁩⁨0.0045⁩
7⁨0.1416⁩⁨0.0042⁩
8⁨0.1513⁩⁨0.0038⁩
9⁨0.1633⁩⁨0.0032⁩
10⁨0.1781⁩⁨0.0022⁩
11⁨0.1962⁩⁨0.0007⁩
12⁨0.2183⁩⁨-0.0014⁩
13⁨0.2453⁩⁨-0.0046⁩
14⁨0.2779⁩⁨-0.0093⁩
15⁨0.3170⁩⁨-0.0160⁩
16⁨0.3633⁩⁨-0.0253⁩
17⁨0.4172⁩⁨-0.0377⁩
18⁨0.4783⁩⁨-0.0535⁩
19⁨0.5455⁩⁨-0.0721⁩
20⁨0.6163⁩⁨-0.0922⁩
21⁨0.6869⁩⁨-0.1116⁩
22⁨0.7526⁩⁨-0.1277⁩
23⁨0.8092⁩⁨-0.1393⁩
24⁨0.8540⁩⁨-0.1463⁩
25⁨0.8868⁩⁨-0.1499⁩
26⁨0.9091⁩⁨-0.1516⁩
27⁨0.9236⁩⁨-0.1522⁩
28⁨0.9325⁩⁨-0.1525⁩
29⁨0.9379⁩⁨-0.1526⁩
30⁨0.9411⁩⁨-0.1526⁩
31⁨0.9430⁩⁨-0.1526⁩
32⁨0.9441⁩⁨-0.1526⁩
33⁨0.9448⁩⁨-0.1526⁩
34⁨0.9451⁩⁨-0.1526⁩
35⁨0.9454⁩⁨-0.1526⁩
36⁨0.9455⁩⁨-0.1526⁩
37⁨0.9455⁩⁨-0.1526⁩
38⁨0.9456⁩⁨-0.1526⁩
39⁨0.9456⁩⁨-0.1526⁩
40⁨0.9456⁩⁨-0.1526⁩
勾配降下法(インタラクティブ)

x=0.11x = 0.11 から40ステップ進むと、0.94560.9456 に落ち着きます。2つの谷のうち浅い方です。開始点を1つ左に動かして 0.100.10 にします。同じ率、同じ40ステップで、今度は 1.0461-1.0461 に落ち着きます。そこでは損失が0.199747低いです。分水嶺は 0.1010310.101031 の丘で、2つの答えの違いは、たまたまそのどちら側から始めたかだけです。

浅い谷に着地すると、損失は56.7%悪くなります。そしてアルゴリズムにはそれを知る方法がありません。谷の内側から見れば、どの方向も上りだからです。gradient descentにこれを修理する方法はなく、これからも出てきません。実際にあるのは、これがこの図から想像するほど重要ではない、という発見です。実ネットワークの非常に高い次元では、ほとんどの臨界点は罠ではなく鞍点であることが分かっています2。第5章では、小さなネットワークが実際にどれほど頻繁に詰まるかを測ります。

上の grad について、気になるべきことが1つあります。すべてのステップでデータセット全体にわたって和を取っていることです。8個の部品なら何でもありません。100万個なら、パラメータを1回動かすのに100万回のgradient計算です。

逃げ道は、gradientが平均であり、平均はサンプルから推定できるという点です。ランダムな少数、つまりminibatchで計算し、それでステップします。推定はノイズを含みます。しかし不偏でもあり、高価で正確な1ステップより、安くてノイズの多い数百ステップの方が勝ちます。10万個の合成部品で、ステップ数ではなく例ごとのgradient数を数えると、こうなります。

手法最適値の0.1%以内に到達するステップ数例ごとのgradient数
full batch7700,000
minibatch of 321003,200
1例ずつ17,58017,580

同じ場所に到達するのに、算術量は219分の1です。そして極端な場合、つまり1例ずつの処理、RobbinsとMonroの元の確率近似3は、勝者ではありません。32個のbatchより5倍悪いのです。行列を掛けるハードウェアでは32例は1例よりほとんど高くつかず、一方でノイズはbatchサイズの平方根で減るからです。このトレードオフこそ、あなたがこれから読むあらゆる訓練スクリプトに batch_size が含まれる理由です。

Momentumはもう1つの安い修正で、狙いはまさに溝です。条件の悪い谷では、ステップは狭い方向をジグザグに横切りながら、長い方向にはゆっくり進みます。Momentumは過去のgradientの移動平均を保つので、振動する成分は打ち消され、一貫した成分は蓄積します。4

vβv+L(θ),θθηv\mathbf{v} \leftarrow \beta \mathbf{v} + \nabla L(\boldsymbol{\theta}), \qquad \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta \mathbf{v}

追加は2行です。中心化していない生のベルト、条件数33,452という、私たちが持つ最悪ケースで、通常の降下が取れる最良の率を使うと、

TEXT
momentum beta=0.0   ->    79,513 steps to 1%
momentum beta=0.9   ->     1,609 steps to 1%
momentum beta=0.99  ->       461 steps to 1%

2行のコードで172倍です。第6章ではこれがAdamになります。仕組みはすでにここにあります。

この章のすべてのgradientは手で導出したので、間違っている可能性があります。修正は冒頭の傾き表です。導関数を数値的に測り、比較します。中心差分 L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h} を使います。これは主要な誤差項を打ち消し、同じ hh でずっと高精度です。

gradcheck.pyPYTHON
def numeric_grad(f, theta, h=1e-5):
    theta = np.asarray(theta, dtype=float)
    out = np.zeros_like(theta)
    for i in range(theta.size):
        bump = np.zeros_like(theta)
        bump[i] = h
        out[i] = (f(theta + bump) - f(theta - bump)) / (2 * h)     
    return out


def gradcheck(f, df, theta, h=1e-5):
    analytic = np.asarray(df(theta), dtype=float)
    numeric = numeric_grad(f, theta, h)
    return np.max(np.abs(analytic - numeric) / np.maximum(1e-8, np.abs(analytic) + np.abs(numeric)))

比較では相対形式が重要です。10410^{-4} の絶対差は、大きさ 10310^{-3} のgradientでは大惨事ですが、大きさ 10610^{6} のgradientでは無関係です。

TEXT
relative error: 1.8929136036763527e-11
with 2 dropped: 0.33333333331650744

1行目は上で手で導いたgradientです。2行目は、片方の成分で2倍の係数を落とした同じ関数です。たった1文字のタイプミスですが、チェックは即座に捕まえます。およそ 10710^{-7} 未満なら一致、10410^{-4} を超えればバグです。この関数を残しておいてください。第5章ではautomatic differentiationエンジンのデバッグに使います。そして、間違ったgradientをそもそも見つけられる唯一の理由です。

この章のすべては、明言されなかった1つの仮定に支えられていました。L/θ\partial L / \partial \theta を書き下せる、という仮定です。

2パラメータの直線なら、それは代数1行でした。ほとんどすぐに、そうではなくなります。シンボリック代数システムに、ネットワークの損失を単一の第1層の重みに関して、単一の例について微分させ、その答えに含まれる演算を数えると、こうなります。

ネットワーク1つの偏導関数に含まれる演算
隠れユニット4個、1層40
隠れユニット4個、2層301
隠れユニット4個、3層1,717

3行目は57パラメータのネットワークです。第6章なら脚注にしかならないほど小さいネットワークです。それでも、そのgradientを手で書き出すとは、1つの訓練例につき約97,869演算を意味します。これを救う記法はありません。救うのは、合成に連鎖律を適用すると巨大な構造が現れるという観察です。同じ中間量が何度も現れ、それらを正しい順序で計算すれば、1回のforward passとおおよそ同じ代価ですべての導関数が得られます。それが第5章です。

しかしその前に、もっと小さな問題があり、すぐ先で待っています。

いま私たちは、任意の微分可能な損失の上を下り坂に転がる機械を手にしました。それをベルトの元の問い、つまり受け入れるか拒否するか、targetが1または0である問題に向けます。出力にシグモイドを付けて確率を予測させ、二乗誤差を最小化します。動きます。しかし最も間違っているときに、ほとんど動きません。gradientがその理由を示します。

出力 zz予測真値二乗誤差でのgradientcross-entropyでのgradient
000.500012.5×1012.5 \times 10^{-1}5.0×1015.0 \times 10^{-1}
2-20.119211.850×1011.850 \times 10^{-1}8.808×1018.808 \times 10^{-1}
6-60.002514.921×1034.921 \times 10^{-3}9.975×1019.975 \times 10^{-1}
10-104.54×1054.54 \times 10^{-5}19.079×1059.079 \times 10^{-5}1.0001.000

答えが1なのに0.0000454と予測する、自信満々で破滅的に間違ったモデルが、二乗誤差では 9×1059 \times 10^{-5} のgradientしか出しません。自分が困った状況にいることをまったく理解していません。まだ導いていない損失から来るもう一方の列は、1.0を報告します。最大の緊急度です。まさにそれがふさわしい場所で。

ここで、次章が冒頭で扱う問いが立ち上がります。前章は、損失とはノイズについての仮定だと言いました。そして二乗誤差はガウスノイズを仮定します。では、はい・いいえの答えにはどんなノイズモデルがあり、同じ導出をそれに適用すると、どんな損失が出てくるのでしょうか。


この方法は、これらすべてより古いものです。Cauchyは1847年にAcadémie des Sciencesへのノートで、残差二乗和の上を下り坂に歩くことで方程式系を解く方法として記述しました。この章と並べて読む価値があるものとして、Sebastian Ruderの An overview of gradient descent optimization algorithmsarXiv:1609.04747)があります。momentumからAdamまでを読みやすい14ページで扱っています。Nocedal and Wrightの Numerical Optimization(第2版、Springer、2006)の第3章も有用です。定理3.3は、二次関数上の最急降下の収束率を条件数で与えており、条件付けがステップ数を決める理由の背後にある理論です。ただし、ここで測定した固定ステップの 2/λmax2/\lambda_{\max} 上限ではなく、line searchを扱っています。同じ範囲を少ない装置で学ぶなら、Deisenroth, Faisal and Ongの Mathematics for Machine Learning の§5.8と§7.1もあります。Princeの Understanding Deep Learning §6.1、Goodfellow, Bengio and Courvilleの Deep Learning §4.3、そして Dive into Deep Learning §12.1–12.3もあります。後者は、ここに入れる余地がないほど多くの測定とともにminibatch分析を扱っています。Géronの Hands-On Machine Learning(第3版)第4章は、学習率を導出するものではなくチューニングするものとして扱う、最も実践的な説明です。MIT 6.390のノートは、このコースと同じく、分類の前にgradient descentを置いています。理由も同じです。

  1. LeCun, Y., Bottou, L., Orr, G. B. and Müller, K.-R. Efficient BackProp, in Neural Networks: Tricks of the Trade (Springer, 1998), pp. 9–50. 4.3節は推奨を、5.1節は上の詳細ボックスで使った議論を与えています。入力を中心化・スケーリングすると、2階導関数行列の固有値が変わり、したがってステップ数が変わります。単なる数値上の快適さではありません。

  2. Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. and Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). 高次元では臨界点の圧倒的多数が局所最小値ではなく鞍点である、という議論です。最小値であるには、何千もの方向すべてが同時に上向きに曲がっていなければならないからです。

  3. Robbins, H. and Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). 適切に縮むステップサイズがあれば、gradientのノイズを含む推定で十分であることを確立した論文です。

  4. Polyak, B. T. Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), pp. 1–17 (1964). heavy-ball法です。これは上のmomentum更新であり、backpropagationがこの分野に届く22年前のものです。

モデル選びは、LIAにおまかせ。

すべてのAIモデルをひとつの場所で。今日から無料で。