Optimizer

Optimizer SGD, Adam, dan AdamW serta peran learning rate.

Daftar isi

Optimizer mengubah gradien menjadi pembaruan parameter, sehingga loss dapat ditekan iterasi demi iterasi. Topik ini memperkenalkan tiga optimizer bawaan torch.optim, yaitu SGD, Adam, dan AdamW, beserta learning rate, yakni besarnya langkah pembaruan yang menjadi hyperparameter paling menentukan. Kode di akhir topik memperlihatkan loss yang turun dalam lima iterasi pada model kecil, sehingga peran setiap baris terlihat secara konkret.

Peran optimizer dan learning rate#

Setiap optimizer menyimpan rujukan ke parameter model. Ketika optimizer.step() dipanggil, optimizer membaca atribut .grad pada setiap parameter dan memperbaruinya mengikuti aturan tertentu. Untuk SGD, aturannya adalah penurunan searah gradien dengan langkah sebesar η\eta:

θ←θ−η ∇θℓ\theta \leftarrow \theta - \eta \, \nabla_{\theta} \ell

dengan θ\theta menyatakan parameter, η\eta menyatakan learning rate, dan ∇θℓ\nabla_{\theta} \ell menyatakan gradien loss terhadap parameter. Learning rate termasuk hyperparameter, yaitu nilai yang ditetapkan sebelum pelatihan dan tidak dipelajari dari data. Nilai yang terlalu besar membuat pelatihan tidak stabil, bahkan divergen, sedangkan nilai yang terlalu kecil membuat perubahan loss nyaris tidak terlihat.

Method lain yang perlu dikenal mulai sekarang adalah optimizer.zero_grad(). Method itu mengosongkan gradien yang tersimpan karena backward() bersifat akumulatif, bukan menimpa. Tanpa pengosongan, gradien dari beberapa iterasi tercampur menjadi satu. Kedua method tersebut diperkenalkan sekilas di sini, sedangkan perulangan pelatihan yang merangkai semuanya dibahas utuh pada topik Anatomi perulangan.

SGD, Adam, dan AdamW#

Ketiga optimizer berikut dibuat dengan API yang seragam, yaitu parameter model diberikan sebagai argumen pertama dan learning rate diberikan melalui lr. Pada PyTorch 2.15, Adam dan AdamW memiliki learning rate bawaan 0.001, sedangkan SGD menuntut lr diberikan secara eksplisit. Apa pun optimizernya, nilainya hampir selalu ditetapkan eksplisit karena sangat menentukan hasil.

python Membuat tiga optimizer pada model yang sama
import torch
import torch.nn as nn

torch.manual_seed(0)

model = nn.Linear(4, 3)

sgd = torch.optim.SGD(model.parameters(), lr=0.01)
adam = torch.optim.Adam(model.parameters(), lr=0.001)
adamw = torch.optim.AdamW(model.parameters(), lr=0.001)

print(type(sgd).__name__, type(adam).__name__, type(adamw).__name__)
text Keluaran
SGD Adam AdamW

Ketiganya berbeda pada aturan pembaruannya, dan ringkasannya dirangkum pada tabel berikut.

OptimizerIde intiCatatan penggunaan
SGDLangkah searah gradien dengan learning rate yang sama untuk semua parameter.Sederhana dan mudah dianalisis, serta sering dipadukan dengan momentum.
AdamMenyimpan rata-rata bergerak gradien dan kuadrat gradien, sehingga besarnya langkah menyesuaikan tiap parameter.Pilihan awal yang umum karena bekerja baik pada rentang learning rate yang lebar.
AdamWAdam dengan weight decay yang diterapkan langsung pada bobot, terpisah dari gradien.Lazim menjadi standar untuk model berskala besar.

Istilah pada tabel itu dijelaskan sebagai berikut. Momentum, yaitu akumulasi kecepatan dari beberapa langkah sebelumnya, membuat arah pembaruan lebih stabil ketika gradien berfluktuasi. Sifat adaptif Adam berasal dari pembagian langkah dengan akar rata-rata kuadrat gradien, sehingga langkah pada parameter dengan gradien besar menjadi lebih kecil dan sebaliknya. Weight decay, yaitu penalti yang mendorong bobot tetap kecil, pada Adam versi lama tercampur ke dalam gradien. AdamW memisahkannya, sehingga regularisasi bekerja pada bobot secara langsung. Perbedaan ketiganya tampak pada kecepatan konvergensi dan kualitas model akhir, bukan pada API-nya, karena ketiganya digunakan dengan pola kode yang sama persis.

Lima iterasi pembaruan#

File berikut merangkai komponen-komponen pada modul ini menjadi satu perulangan mini. Data berupa regresi sintetis dengan empat fitur dan dua target. Modelnya Linear(4, 2) dengan loss MSELoss, sedangkan optimizernya SGD dengan learning rate 0.05. Setiap iterasi menjalankan zero_grad(), forward, backward(), lalu step().

python latih-mini.py
import torch
import torch.nn as nn

torch.manual_seed(0)

# data sintetis regresi: y = x @ W + b
x = torch.randn(32, 4)                                     # (N, in_features) = (32, 4)
W = torch.tensor([[1.0, -1.0], [0.5, 2.0], [-0.5, 0.25], [2.0, 0.0]])
b = torch.tensor([0.1, -0.2])
y = x @ W + b                                              # (N, out_features) = (32, 2)

model = nn.Linear(4, 2)
loss_fn = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)

for i in range(5):
    optimizer.zero_grad()       # kosongkan gradien dari iterasi sebelumnya
    prediksi = model(x)         # forward
    loss = loss_fn(prediksi, y)
    loss.backward()             # hitung gradien
    optimizer.step()            # perbarui parameter

    print(f"iterasi {i + 1}: loss = {loss.item():.4f}")
text Keluaran
iterasi 1: loss = 5.9632
iterasi 2: loss = 5.2088
iterasi 3: loss = 4.5603
iterasi 4: loss = 4.0019
iterasi 5: loss = 3.5199

Loss turun dari 5.9632 menjadi 3.5199 dalam lima iterasi, dengan penurunan yang stabil pada setiap langkah. Perlu dicatat bahwa nilai loss yang dicetak dihitung sebelum step() pada iterasi yang sama diterapkan, sehingga deret itu menyatakan kemajuan dari pembaruan sebelumnya. Perulangan pelatihan yang lengkap, termasuk pemrosesan per batch dan mode train/eval, dibahas pada topik perulangan pelatihan.

Memilih learning rate#

Peran learning rate paling mudah dilihat ketika tiga nilai yang jauh berbeda diberikan pada masalah yang sama. Kode berikut melatih model yang sama dengan inisialisasi yang sama, hanya learning ratenya yang diubah.

python Perbandingan tiga nilai learning rate
import torch
import torch.nn as nn

torch.manual_seed(0)

x = torch.randn(32, 4)
W = torch.tensor([[1.0, -1.0], [0.5, 2.0], [-0.5, 0.25], [2.0, 0.0]])
b = torch.tensor([0.1, -0.2])
y = x @ W + b

def latih(lr):
    torch.manual_seed(0)               # inisialisasi model yang identik
    model = nn.Linear(4, 2)
    loss_fn = nn.MSELoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=lr)
    nilai = []
    for _ in range(5):
        optimizer.zero_grad()
        loss = loss_fn(model(x), y)
        loss.backward()
        optimizer.step()
        nilai.append(loss.item())
    return nilai

for lr in (0.00001, 0.05, 2.0):
    deret = " ".join(f"{v:.4f}" for v in latih(lr))
    print(f"lr={lr:>8}: {deret}")
text Keluaran
lr=   1e-05: 5.6564 5.6563 5.6562 5.6560 5.6559
lr=    0.05: 5.6564 5.0004 4.4313 3.9363 3.5050
lr=     2.0: 5.6564 16.0180 89.1739 563.2015 3700.5664

Tiga baris keluaran itu memperlihatkan tiga perilaku yang berbeda. Learning rate 0.00001 membuat loss nyaris diam karena langkah pembaruannya terlalu kecil. Nilai 0.05 menurunkan loss secara stabil sampai 3.5050. Nilai 2.0 justru menaikkan loss dari 5.6564 menjadi 3700.5664, karena setiap langkah melampaui lembah yang hendak dituju. Inilah alasan learning rate disebut hyperparameter paling menentukan: satu angka itu memengaruhi jalannya pelatihan lebih besar daripada perbedaan antara optimizer.

Ringkasan#

  • Optimizer membaca .grad pada setiap parameter dan memperbaruinya saat step(), sementara zero_grad() wajib dipanggil karena backward() bersifat akumulatif.
  • SGD menggunakan langkah seragam searah gradien, Adam menyesuaikan langkah per parameter melalui rata-rata bergerak, dan AdamW menambahkan weight decay yang terpisah dari gradien.
  • Learning rate menentukan besarnya langkah pembaruan: terlalu besar membuat loss divergen dan terlalu kecil membuat pelatihan lambat, sehingga nilainya hampir selalu ditetapkan eksplisit.

Referensi#