Lewati ke konten
3/30Bab 3 dari 30

Menurun: Gradient Descent, dan Dua Langkah yang Selalu Dilewati

Hitung batas tepat learning rate, lalu lihat pencarian brute-force atas 3.600 arah menemukan kembali gradien tanpa diberi tahu.

Di halaman ini

Bab sebelumnya berakhir dengan sebuah lembah.

Bukan lembah metaforis: sebuah kurva sungguhan, loss diplot terhadap satu parameter, turun lalu naik lagi. Dan loss di bawahnya tidak dipilih karena rapi — ia diturunkan dari pernyataan tentang noise dalam pengukuran, dan squared error muncul di ujung lain sebagai konsekuensi, bukan konvensi.

Jadi kita punya lanskap dengan dasar, dan alasan untuk percaya bahwa dasar itu adalah tempat yang benar. Yang belum kita punya adalah cara untuk sampai ke sana.

Bab ini membangunnya, dan inilah algoritma yang melatih setiap model dalam sisa kursus ini — semuanya, tanpa kecuali, sampai termasuk yang memiliki ratusan miliar parameter. Ia muat dalam sekitar dua puluh baris. Dua bagian sulitnya tidak ada dalam dua puluh baris itu, dan keduanya adalah hal yang hampir selalu dilewati setiap penjelasan:

  • Kenapa tanda minus. Pembaruan mengurangkan gradien. Setiap tutorial menuliskannya; sangat sedikit yang menjelaskan kenapa gradien adalah arah yang naik ke atas, padahal hanya fakta itulah yang membuat tanda minus bukan sekadar tindakan iman.
  • Seberapa besar langkahnya. “Terlalu besar divergen, terlalu kecil lambat” itu benar dan tidak berguna. Ada angka tepatnya, bisa dihitung dari loss, dan bab ini menghitungnya dua kali — sekali untuk parabola mainan dan sekali untuk data sebenarnya.

Setup, dan kenapa kamu tidak bisa sekadar mencari

Tautan ke bagian: Setup, dan kenapa kamu tidak bisa sekadar mencari

Diulang agar bab ini berdiri sendiri: delapan komponen dari ban berjalan di Bab 1, tetapi dengan pertanyaan berbeda. Bukan terima atau tolak — itu kembali nanti — melainkan memprediksi berat sebuah komponen dari lebarnya.

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

Pengukurannya dipusatkan, persis seperti di Bab 1 dan karena alasan yang kembali dengan bunga sebelum bab ini selesai. Modelnya adalah garis, y^=ax+b\hat{y} = a x + b, dan loss-nya adalah mean squared error yang diturunkan bab sebelumnya:

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

Dua parameter. Kenapa tidak mencoba banyak nilai saja? Mari benar-benar melakukannya — grid dari a=0a = 0 sampai 55 dan b=5b = -5 sampai 55, dengan langkah 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

Setengah juta evaluasi untuk mengunci dua angka hingga dua tempat desimal — dan detik itu adalah wall clock pada satu mesin, jadi pengulangan bisa mendarat di mana saja dari tiga sampai enam; jumlah evaluasi dan minimumnya adalah bagian yang direproduksi. Gradient descent, di akhir bab ini, mendapatkan empat tempat desimal dalam delapan langkah dan jawaban float64 penuh dalam tiga puluh enam.

Tetapi kecepatan bukan argumennya, dan inilah titik yang menentukan seluruh kursus. Grid search membutuhkan kPk^P evaluasi untuk PP parameter pada kk nilai masing-masing. Dengan seribu nilai per sumbu:

modelparameterevaluasi grid
garis ini210610^{6}
jaringan XOR dari Bab 59102710^{27}
jaringan multilayer kecil20.0001060,00010^{60{,}000}

Baris ketiga bukan angka besar, melainkan angka yang tidak bermakna — ada kira-kira 108010^{80} atom di alam semesta teramati. Search bukan makin lambat saat model membesar; ia berhenti ada. Semua yang mengikuti ada karena tabel itu.

Turunan adalah pengukuran yang bisa kamu ambil

Tautan ke bagian: Turunan adalah pengukuran yang bisa kamu ambil

Bekukan b=0b = 0 sejenak agar hanya ada satu parameter dan satu kurva, yaitu gambar yang ditinggalkan bab terakhir. Ambil satu titik di atasnya, a=1a = 1, dan tanyakan: jika saya menggeser aa sedikit sebesar hh, seberapa banyak loss bergerak, per satuan geseran?

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

Rasio itu adalah rise over run — kemiringan garis lurus melalui dua titik pada kurva. Saat hh mengecil, kedua titik saling mendekat dan garis menjadi tangen. Kemiringannya adalah turunan L(a)L'(a): laju perubahan loss per satuan perubahan pada aa. Bukan aproksimasi apa pun, dan bukan kuantitas yang sangat kecil. Sebuah limit dari rasio biasa.

Ini layak dijalankan, karena angkanya mengatakan sesuatu yang tidak dikatakan definisi:

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

Dua hal terjadi di sini dan keduanya menopang bangunan.

Error tidak sekadar samar-samar proporsional terhadap hh — ia persis 7.445h7.445\,h. Bagi hh dengan seratus, error ikut terbagi seratus, sampai empat angka signifikan setiap kali. Konstanta itu bukan hiasan: ia adalah setengah dari turunan kedua loss, dan ia adalah kemunculan pertama dari ide dua bagian lagi — bahwa kurva di dekat titik terlihat seperti garis plus koreksi yang proporsional terhadap h2h^2.

Lalu polanya patah. Di bawah h=108h = 10^{-8} estimasinya menjadi lebih buruk, dan pada 101410^{-14} ia salah di digit kedua. Tidak ada yang matematis terjadi; kotak floating-point dari bab terakhir yang melakukannya. L(a+h)L(a+h) dan L(a)L(a) sama pada sepuluh digit pertamanya, mengurangkannya menghancurkan digit-digit itu, dan membagi puingnya dengan angka kecil memperbesar sisa yang ada. Ada hh terbaik — di sini sekitar 10810^{-8}, kira-kira akar kuadrat dari machine epsilon — dan menjadi lebih kecil bukan lebih hati-hati, melainkan kurang. Ingat itu; sebuah fungsi di akhir bab ini bergantung padanya.

Kemiringan tepatnya, dari kalkulus alih-alih pengukuran, adalah 16.385-16.385. Jadi kita bisa berhenti mengukur dan mulai menurunkan.

Inilah ide yang menjadi dasar sisa kursus ini, dinyatakan sekali, secara lugas.

Mengomposisikan dua fungsi berarti memberi satu fungsi ke fungsi lain: (fg)(x)=f(g(x))(f \circ g)(x) = f(g(x)). Tidak lebih.

Jaringan dalam bukan seperti komposisi. Ia adalah komposisi. Layer adalah fungsi; menumpuk layer berarti mengomposisikannya; “kedalaman” adalah jumlah fungsi dalam rantai. Saat Bab 5 membangun jaringan, ia membangun f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 dan tidak ada yang lain. Artinya aturan kalkulus paling penting untuk tujuan kita adalah aturan yang mendiferensiasi komposisi:

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

Laju saling mengalikan. Jika gg berubah tiga kali secepat xx, dan ff berubah dua kali secepat gg, maka ff berubah enam kali secepat xx. Itulah seluruh isinya, dan itulah mengapa sinyal yang lewat balik melalui sepuluh layer dikalikan oleh sepuluh angka — itulah sebabnya Bab 6 menghabiskan satu bagian untuk apa yang terjadi ketika semua angka itu sedikit kurang dari satu.

Gunakan pada loss kita. Tulis residual ri=axi+byir_i = a x_i + b - y_i, sehingga L=1nri2L = \frac{1}{n}\sum r_i^2. Setiap rir_i bergantung pada aa melalui fungsi dalam axia x_i, yang turunannya adalah xix_i. Chain rule, suku demi suku:

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

Simbol keriting \partial itu menandai turunan parsial: mendiferensiasi terhadap satu variabel dan memperlakukan semua yang lain sebagai konstanta. Tidak ada hal baru terjadi — ini limit yang sama seperti sebelumnya, diambil sepanjang satu sumbu. Kumpulkan partial ke dalam vektor dan kamu punya gradien:

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

Pada titik (a,b)=(1,4)(a, b) = (1, 4) vektor itu adalah (16.385, 8.0)(-16.385,\ 8.0). Dua angka. Pertanyaannya adalah apa artinya, dan ini adalah langkah pertama yang semua orang lewati.

Gradien adalah vektor kemiringan di sepanjang sumbu. Itu saja yang sudah kita buktikan. Tidak jelas — dan memang seharusnya tidak jelas — bahwa menyusunnya menjadi vektor menghasilkan sesuatu yang menunjuk ke arah tertentu.

Jadi definisikan hal yang sebenarnya kita inginkan. Pilih vektor satuan u\mathbf{u}, sebuah arah. Turunan arah adalah laju perubahan loss saat kamu berjalan ke arah itu:

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

Chain rule mengubah ini menjadi sesuatu yang bisa dihitung. Berjalan sepanjang u\mathbf{u} mengubah aa pada laju u1u_1 dan bb pada laju u2u_2, dan kontribusinya dijumlahkan:

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}

Laju perubahan dalam arah mana pun adalah dot product gradien dengan arah itu. Dan sekarang punchline-nya, satu baris geometri. Menulis dot product dengan sudut ϕ\phi di antara vektor,

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

karena u\mathbf{u} memiliki panjang 1. Satu-satunya hal yang kamu kendalikan adalah cosϕ\cos\phi, yang paling besar pada ϕ=0\phi = 0 dan paling kecil pada setengah putaran, ϕ=180\phi = 180 derajat. Jadi:

  • Kenaikan paling curam adalah sepanjang L\nabla L itu sendiri, dan kemiringan di sana persis L\lVert \nabla L \rVert.
  • Penurunan paling curam adalah sepanjang L-\nabla L, dan kemiringan di sana adalah L-\lVert \nabla L \rVert.
  • Tegak lurus terhadap gradien, loss tidak berubah sama sekali. Itulah mengapa garis pada peta kontur memotong gradien pada sudut siku-siku.

Itulah tanda minus. Bukan konvensi, bukan pembalikan tanda yang dipilih seseorang: arah penurunan tercepat adalah gradien negatif karena cosϕ\cos\phi diminimalkan pada setengah putaran, dan tidak ada alasan lain.

Karena ini klaim tentang semua arah, uji terhadap semua arah. Ambil sampel 3.600 arah, satu per sepersepuluh derajat, dan ukur masing-masing dengan menggeser:

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

Search yang tidak tahu apa pun tentang gradien, atas 3.600 arah, menemukan tanjakan tercuramnya pada 154,0 derajat — arah gradien itu sendiri, dalam resolusi 0,1 derajat search. Dan kemiringan yang ditemukannya di sana, 18,2337, adalah panjang gradien hingga enam angka. Teorema ini bukan cerita tentang arti gradien; ia fakta yang terukur, dan itulah pengukurannya.

Kenapa langkah kecil menurun benar-benar membantu

Tautan ke bagian: Kenapa langkah kecil menurun benar-benar membantu

Sekarang langkah kedua yang dilewati. Kita tahu arah turun. Tidak berarti berjalan ke arah itu menurunkan loss, karena “turun” adalah pernyataan tentang geseran infinitesimal dan sebuah langkah bukan infinitesimal.

Jembatannya adalah linearisasi. Di dekat sebuah titik, fungsi mulus adalah tangennya plus koreksi:

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

Itu ekspansi Taylor orde pertama. O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) yang dibuang adalah kelengkungan — suku yang sama yang membuat estimasi tabel kemiringan salah persis sebesar 7.445h7.445\,h. Masukkan langkah yang ingin kita ambil, δ=η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

Loss turun sebesar ηL2\eta \lVert \nabla L \rVert^2. Setiap bagiannya non-negatif, jadi janjinya nyata — untuk η\eta yang cukup kecil, karena suku yang diabaikan tumbuh seperti η2\eta^2 dan akhirnya memakannya. Itulah seluruh teorinya. Berikut janji itu ditepati, lalu dilanggar:

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

Baca dari bawah. Saat η\eta mengecil, penurunan yang diberikan konvergen ke yang dijanjikan — rasio 0,99938, lalu 0,99994 — yaitu teorema Taylor yang benar. Baca dari atas dan pada η=0.2\eta = 0.2 “penurunan” yang diberikan adalah negatif enam belas. Langkahnya menuju turun dan loss justru naik.

Jadi aturan pembaruannya adalah

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

dan ia datang dengan syarat yang tidak dinyatakan siapa pun, yaitu η\eta harus cukup kecil. Cukup kecil dibanding apa, tepatnya, adalah bagian berikutnya.

Learning rate punya plafon, dan bisa dihitung

Tautan ke bagian: Learning rate punya plafon, dan bisa dihitung

Mulai dengan lembah paling sederhana, f(x)=x2f(x) = x^2, di mana f(x)=2xf'(x) = 2x. Satu langkah gradient descent adalah

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

Posisi dikalikan (12η)(1 - 2\eta) setiap langkah. Itu deret geometri, dan deret geometri punya tepat satu aturan: ia mengecil ketika pengalinya lebih kecil dari 1 dalam nilai absolut dan membesar jika tidak. Jadi 12η<1\lvert 1 - 2\eta \rvert < 1, yaitu 0<η<10 < \eta < 1.

Batasnya tepat di η=1\eta = 1. Bukan “sekitar 1”, bukan “1 biasanya terlalu besar”. Pada η=1\eta = 1 pengalinya adalah 1-1 dan titik memantul antara xx dan x-x selamanya, tidak mendekat maupun kabur. Di bawahnya, konvergen; di atasnya, divergen. Interval itu terbelah lagi pada η=0.5\eta = 0.5, ketika pengali berubah tanda: di bawah itu pendekatannya monoton, di atasnya titik overshoot dan berganti sisi, dan tepat pada 0.50.5 pengalinya 0 dan satu langkah saja mendarat di minimum.

Empat rezim, dari empat baris aljabar. Silakan lintasi batasnya sendiri:

14 langkah, berakhir di x = -0.0836.

Lihat data sebagai tabel
Langkahxf(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⁩
Penurunan gradien, interaktif

Empat belas langkah pada rate 0,1, dari x=1.9x = -1.9, berakhir di 0.0836-0.0836. Dorong rate ke 0,5 dan langkah pertama langsung mendarat di dasar. Dorong ke 0,9 dan ia berakhir di 0.0836-0.0836 yang sama seperti 0,1 — jarak yang sama, gaya yang berlawanan, karena 12η\lvert 1 - 2\eta \rvert adalah 0,8 untuk keduanya — tetapi ia sampai ke sana dengan zig-zag melintasi lembah alih-alih berjalan turun di satu sisi.

Dan sekarang yang menarik:

14 langkah, berakhir di x = -1.9000.

Lihat data sebagai tabel
Langkahxf(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⁩
Penurunan gradien, interaktif

Tepat di batas. Empat belas langkah pada rate 1, dan ia selesai di 1.9-1.9: persis tempat ia mulai, tanpa melakukan apa pun selain memantul. Satu geseran lebih tinggi dan pantulannya membesar alih-alih bertahan; pada 1,2 ia keluar dari grafik dalam empat langkah. Rate yang terlalu besar tidak konvergen perlahan. Ia tidak konvergen.

Sekarang aturan umumnya, yang keluar dari argumen yang sama. Pengali 12η1 - 2\eta sebenarnya adalah 1ηf1 - \eta f'', dan di dekat minimum loss multi-parameter punya satu angka seperti itu per arah — eigenvalue dari matriks turunan kedua. Setiap arah harus stabil sekaligus, jadi plafonnya ditentukan oleh yang terbesar:

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

Untuk f(x)=x2f(x) = x^2, f=2f'' = 2, plafon 1, yaitu yang baru saja kita turunkan. Untuk sabuk kita, matriks turunan keduanya adalah 2nAA\frac{2}{n} A^{\top} A dengan AA matriks input dua kolom, dan eigenvalue-nya 2 dan 14,89, jadi plafonnya 2/14.89=0.134322 / 14.89 = 0.13432. Itu prediksi dengan lima angka signifikan. Uji:

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

Lima tempat desimal kesesuaian antara satu baris aljabar linear dan seratus ribu iterasi loop for.

Dan di sinilah Bab 1 kembali. Semua di atas menggunakan pengukuran yang dipusatkan. Jalankan code identik pada milimeter dan gram mentah dan eigenvalue-nya 0,0298 dan 998,1 alih-alih 2 dan 14,89. Plafonnya runtuh dari 0,134 menjadi 0,002004 — sama persisnya, konvergen pada lr=0.002003 dan meledak pada lr=0.002004.

Yang lebih buruk dari plafon adalah rasio antara eigenvalue. Condition number mengukur seberapa jauh lembah dari bulat: parit panjang dan tipis memaksa rate cukup kecil untuk dinding curamnya, lalu lantai parit ditempuh dengan rayapan yang sama. Punya kita berubah dari 7,44 saat dipusatkan menjadi 33.452 saat mentah. Dengan rate terbaik yang bisa diambil masing-masing versi:

fiturcondition numberrate terbaiklangkah hingga dalam 1% dari optimum
dipusatkan7,440,118410
milimeter dan gram mentah33.4520,002003779.513

Data yang sama, code yang sama, jawaban akhir yang sama — dan kerja delapan ribu kali lipat, karena tidak ada yang mengurangkan mean. Di Bab 1 kelalaian yang sama membuat perceptron membayar faktor enam ribu dalam epoch, dan diagnosisnya di sana geometris: data mengapung jauh dari origin. Ini geometri yang sama dalam kostum optimisasi, dan itulah kenapa normalisasi input bukan nasihat kebersihan melainkan aritmetika.1

Tidak ada di atas yang membutuhkan library. Inilah seluruh 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

Jawaban least-squares closed-form untuk delapan titik ini adalah a=2.100403a = 2.100403, b=0b = 0, dengan loss 24.59244924.592449. Loop menemukannya hingga delapan angka signifikan tanpa tahu bahwa closed form ada — dan itu penting, karena mulai Bab 5 tidak akan ada.

Trajektorinya, karena melihatnya adalah intinya:

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

Sebagian besar jarak ditempuh dalam dua langkah pertama, karena gradien paling besar ketika kamu paling jauh dari dasar dan mengecil saat mendekat. Gradient descent melambat otomatis di dekat minimum. Itu fitur dan juga, di Bab 6, masalah.

Argumen sejauh ini punya lubang. Langkah berhenti ketika L=0\nabla L = \mathbf{0}, dan kita menyebutnya “minimum”. Titik dengan gradien nol adalah titik kritis, dan menjadi minimum hanya salah satu caranya:

  • minimum lokal: naik di setiap arah, tetapi mungkin bukan titik terendah semacam itu di mana pun;
  • maksimum lokal: turun di setiap arah;
  • titik saddle: naik di beberapa arah dan turun di arah lain. Permukaan f(x,y)=x2y2f(x,y) = x^2 - y^2 memiliki f=(2x,2y)\nabla f = (2x, -2y), yang bernilai nol di origin, tempat fungsi adalah minimum sepanjang sumbu xx dan maksimum sepanjang sumbu yy pada saat yang sama.

Gradient descent tidak bisa membedakan semua ini, karena ia hanya pernah melihat gradien, dan gradien bernilai nol pada ketiganya.

Garis kita punya satu titik kritis dan itu jawabannya — loss squared-error atas model linear adalah convex, sebuah mangkuk tunggal, dan descent di atasnya tidak bisa gagal menemukan minimum global. Sifat itu tidak bertahan saat bersentuhan dengan kursus ini. Loss neural network tidak convex, dan mulai Bab 5 “minimum” bukan sesuatu yang ada: ada banyak, dengan kedalaman berbeda, dan mana yang kamu dapat bergantung pada tempat kamu mulai. Itu satu kalimat dan tetap satu kalimat, karena teorinya besar dan konsekuensi praktisnya kecil.

Kamu bisa melihat seluruh konsekuensinya pada satu kurva. Ambil f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10}, yang memiliki dua lembah dengan kedalaman berbeda:

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 langkah, berakhir di x = 0.9456.

Lihat data sebagai tabel
Langkahxf(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⁩
Penurunan gradien, interaktif

Empat puluh langkah dari x=0.11x = 0.11, menetap di 0.94560.9456 — lembah yang lebih dangkal dari keduanya. Sekarang geser titik awal satu takik ke kiri, ke 0.100.10. Rate yang sama, empat puluh langkah yang sama, dan ia menetap di 1.0461-1.0461 alih-alih, tempat loss 0,199747 lebih rendah. Batas aliran airnya adalah bukit di 0.1010310.101031, dan seluruh perbedaan antara dua jawaban itu adalah di sisi mana kamu kebetulan mulai.

Mendarat di lembah dangkal 56,7% lebih buruk dalam loss, dan algoritma tidak punya cara untuk tahu, karena dari dalam lembah setiap arah adalah naik. Tidak ada perbaikan untuk ini dalam gradient descent dan tidak akan ada. Yang ada, dalam praktik, adalah temuan bahwa ini jauh kurang penting daripada yang disarankan gambar ini — dalam dimensi sangat tinggi dari jaringan nyata, sebagian besar titik kritis ternyata saddle, bukan jebakan,2 dan Bab 5 mengukur seberapa sering jaringan kecil benar-benar tersangkut.

Langkah lebih murah: stochastic, minibatch, momentum

Tautan ke bagian: Langkah lebih murah: stochastic, minibatch, momentum

Ada satu hal tentang grad di atas yang seharusnya mengganggumu: ia menjumlahkan seluruh dataset untuk setiap langkah. Delapan komponen bukan apa-apa. Satu juta berarti satu juta komputasi gradien untuk menggerakkan parameter sekali.

Jalan keluarnya adalah bahwa gradien adalah rata-rata, dan rata-rata bisa diestimasi dari sampel. Hitung pada segenggam acak — sebuah minibatch — lalu melangkah berdasarkan itu. Estimasinya noisy; ia juga unbiased, dan ratusan langkah noisy murah mengalahkan satu langkah exact yang mahal. Pada seratus ribu komponen sintetis, menghitung gradien per contoh alih-alih langkah:

metodelangkah hingga dalam 0,1% dari optimumgradien per contoh
full batch7700.000
minibatch 321003.200
satu contoh setiap kali17.58017.580

Dua ratus sembilan belas kali lebih sedikit aritmetika untuk mencapai tempat yang sama. Dan ekstremnya — satu contoh setiap kali, aproksimasi stochastic asli dari Robbins dan Monro3bukan pemenangnya: ia lima kali lebih buruk daripada batch 32, karena 32 contoh hampir tidak lebih mahal daripada satu pada hardware yang mengalikan matriks, sementara noise turun dengan akar kuadrat ukuran batch. Trade-off itulah yang membuat setiap script training yang pernah kamu baca memiliki batch_size di dalamnya.

Momentum adalah perbaikan murah lainnya, dan ia diarahkan tepat ke parit. Dalam lembah dengan conditioning buruk, langkah zig-zag melintasi arah sempit sambil merayap di arah panjang. Momentum menyimpan rata-rata berjalan dari gradien masa lalu, sehingga komponen yang berosilasi saling membatalkan dan komponen yang konsisten terakumulasi:4

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

Dua baris ekstra. Pada sabuk mentah yang tidak dipusatkan — condition number 33.452, kasus terburuk yang kita punya — pada rate terbaik yang bisa diambil plain descent:

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%

Faktor 172 untuk dua baris code. Bab 6 mengubah ini menjadi Adam; mekanismenya sudah ada di sini.

Pemeriksaan yang akan kamu butuhkan di Bab 5

Tautan ke bagian: Pemeriksaan yang akan kamu butuhkan di Bab 5

Setiap gradien dalam bab ini diturunkan dengan tangan dan karena itu bisa salah. Perbaikannya adalah tabel kemiringan dari awal: ukur turunan secara numerik dan bandingkan. Gunakan selisih central, L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h}, yang membatalkan suku error utama dan jauh lebih akurat untuk hh yang sama.

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)))

Bentuk relatif dari perbandingan itu penting: selisih absolut sebesar 10410^{-4} adalah bencana pada gradien berukuran 10310^{-3} dan tidak relevan pada yang berukuran 10610^{6}.

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

Baris pertama adalah gradien yang diturunkan dengan tangan di atas. Yang kedua adalah fungsi yang sama dengan faktor 2 tertinggal dari satu komponen — typo satu karakter — dan pemeriksaan langsung menangkapnya. Apa pun di bawah sekitar 10710^{-7} berarti sepakat; apa pun di atas 10410^{-4} adalah bug. Simpan fungsi ini: Bab 5 menggunakannya untuk debug engine automatic differentiation, dan inilah satu-satunya alasan gradien yang salah bisa ditemukan sama sekali.

Semua dalam bab ini bertumpu pada satu asumsi yang tidak pernah dinyatakan: bahwa kamu bisa menuliskan L/θ\partial L / \partial \theta.

Untuk garis dengan dua parameter, itu satu baris aljabar. Ia berhenti menjadi demikian hampir seketika. Minta sistem aljabar simbolik menurunkan loss sebuah jaringan terhadap satu weight layer pertama, untuk satu contoh, dan hitung aritmetika dalam jawabannya:

jaringanoperasi dalam satu turunan parsial
empat hidden unit, satu layer40
empat hidden unit, dua layer301
empat hidden unit, tiga layer1.717

Baris ketiga adalah jaringan dengan 57 parameter — jaringan yang begitu kecil hingga akan menjadi catatan kaki di Bab 6 — dan menulis gradiennya dengan tangan berarti sekitar 97.869 operasi untuk satu contoh training. Tidak ada notasi yang menyelamatkan ini. Yang menyelamatkannya adalah pengamatan bahwa chain rule yang diterapkan pada komposisi punya struktur sangat besar, bahwa kuantitas perantara yang sama muncul berulang-ulang, dan bahwa menghitungnya dalam urutan yang benar mendapatkan semua turunan dengan kira-kira harga satu forward pass. Itulah Bab 5.

Tetapi ada masalah yang lebih kecil dulu, dan ia menunggu tepat di depan.

Sekarang kita punya mesin yang akan bergulir turun pada loss terdiferensiasi apa pun. Arahkan ke pertanyaan asli sabuk — terima atau tolak, target 1 atau 0 — letakkan sigmoid pada output agar ia memprediksi probabilitas, dan minimalkan squared error. Ia akan berjalan. Ia juga hampir tidak bergerak saat paling salah, dan gradien menjelaskan kenapa:

output zzprediksikebenarangradien dengan squared errorgradien dengan cross-entropy
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

Model yang yakin, secara katastrofik salah — memprediksi 0,0000454 ketika jawabannya 1 — menghasilkan gradien squared-error sebesar 9×1059 \times 10^{-5}. Ia tidak tahu bahwa ia sedang bermasalah. Kolom lain, dari loss yang belum kita turunkan, melaporkan 1,0: urgensi maksimum, tepat di tempat yang pantas.

Yang mengangkat pertanyaan pembuka bab berikutnya. Bab terakhir mengatakan loss adalah asumsi tentang noise, dan squared error mengasumsikan noise Gaussian. Model noise apa yang dimiliki jawaban ya-atau-tidak — dan loss apa yang keluar ketika kamu menjalankan penurunan yang sama padanya?


Metodenya lebih tua dari semua ini: Cauchy mendeskripsikannya dalam catatan kepada Académie des Sciences pada 1847, sebagai cara menyelesaikan sistem persamaan dengan berjalan menurun pada jumlah residual kuadratnya. Juga layak dibaca bersama bab ini: An overview of gradient descent optimization algorithms karya Sebastian Ruder (arXiv:1609.04747), yang membahas momentum hingga Adam dalam empat belas halaman yang mudah dibaca; bab 3 dari Numerical Optimization karya Nocedal dan Wright (ed. ke-2, Springer, 2006), yang teorema 3.3-nya memberikan laju konvergensi steepest descent pada kuadratik dalam bentuk condition number — itulah teori di balik kenapa conditioning menentukan jumlah langkah, meskipun ia membahas line search alih-alih plafon 2/λmax2/\lambda_{\max} langkah tetap yang diukur di atas, atau §5.8 dan §7.1 dari Mathematics for Machine Learning karya Deisenroth, Faisal dan Ong untuk medan yang sama dengan perangkat yang lebih ringan; §6.1 dari Understanding Deep Learning karya Prince dan §4.3 dari Deep Learning karya Goodfellow, Bengio dan Courville; Dive into Deep Learning §12.1–12.3, yang memiliki analisis minibatch dengan lebih banyak pengukuran daripada ruang yang tersedia di sini; dan bab 4 dari Hands-On Machine Learning karya Géron (ed. ke-3), perlakuan paling praktis terhadap learning rate sebagai sesuatu yang kamu tune, bukan turunkan. Catatan MIT 6.390 menempatkan gradient descent sebelum klasifikasi, seperti kursus ini, dan untuk alasan yang sama.

  1. LeCun, Y., Bottou, L., Orr, G. B. dan Müller, K.-R. Efficient BackProp, dalam Neural Networks: Tricks of the Trade (Springer, 1998), hlm. 9–50. Bagian 4.3 memberikan rekomendasi dan bagian 5.1 argumen yang digunakan dalam kotak detail di atas: memusatkan dan menskalakan input mengubah eigenvalue dari matriks turunan kedua, dan karena itu jumlah langkah, bukan sekadar kenyamanan numerik.

  2. Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. dan Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). Argumen bahwa dalam dimensi tinggi titik kritis secara luar biasa lebih banyak berupa saddle daripada minimum lokal, karena minimum mengharuskan setiap dari ribuan arah melengkung ke atas sekaligus.

  3. Robbins, H. dan Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), hlm. 400–407 (1951). Makalah yang menetapkan bahwa estimasi noisy dari gradien sudah cukup, jika ukuran langkah mengecil dengan cara yang benar.

  4. Polyak, B. T. Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), hlm. 1–17 (1964). Metode heavy-ball, yaitu pembaruan momentum di atas, dua puluh dua tahun sebelum backpropagation mencapai bidang ini.

Siap membiarkan LIA yang memilih?

Berkarya dengan semua model AI dalam satu tempat — mulai gratis hari ini.