Gradien adalah bahan bakar seluruh pelatihan model: tanpa tahu arah kemiringan loss terhadap tiap parameter, gradient descent tidak punya apa pun untuk diikuti. Pada section dasar neural network kita menghitung gradien itu manual dengan aturan rantai NumPy, dan pada section tensor PyTorch kita menyerahkan tugas itu ke autograd. TensorFlow menempuh pendekatan yang lebih eksplisit: operasi harus terekam di dalam konteks tf.GradientTape terlebih dahulu, barulah gradien bisa dihitung mundur. Chapter ini membedah cara kerja tape, membuktikan ketepatannya lewat beda hingga, lalu merakit siklus pelatihan satu parameter lengkap — forward, gradien, dan pembaruan assign_sub — pada data regresi kecil yang diuji nyata.
Cara Kerja GradientTape #
tf.GradientTape bekerja seperti perekam: seluruh operasi tensor yang terjadi di dalam blok with ditulis ke tape bersama nilai yang menyertainya. Saat kita memanggil tape.gradient(y, x), TensorFlow memutar ulang rekaman itu secara mundur sambil menerapkan aturan rantai — konsep yang sama dengan yang kita turunkan manual pada Chapter Backpropagation
. Diagram berikut merangkum daur hidupnya:
Dua aturan penting mengelilingi tape. Pertama, hanya tf.Variable yang direkam secara otomatis; tf.constant harus ditandai tape.watch(c) agar diperhitungkan. Kedua, tape sekali pakai — setelah tape.gradient dipanggil, sumber dayanya dilepas, kecuali kita membuatnya persistent=True untuk beberapa panggilan gradien berturut-turut.
Gradien Pertama: y = x² #
Kita mulai dari contoh yang bisa dihitung tangan: turunan y = x² di x = 3 semestinya 2x = 6. Berkas gradien_tape.py menguji tape pada kasus ini, memverifikasinya dengan beda hingga ala Chapter Kalkulus untuk Deep Learning
, lalu menguji persistent, watch, dan akibat melanggar keduanya:
"""tf.GradientTape: merekam operasi lalu menghitung gradien otomatis."""
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
import tensorflow as tf
x = tf.Variable(3.0)
with tf.GradientTape() as tape:
y = x ** 2
dy_dx = tape.gradient(y, x)
print("y = x^2 di x = 3 : y =", y.numpy())
print("tape.gradient(dy/dx) :", dy_dx.numpy())
eps = 1e-3
beda_hingga = ((3.0 + eps) ** 2 - (3.0 - eps) ** 2) / (2 * eps)
print("verifikasi beda hingga:", round(float(beda_hingga), 6))
print()
with tf.GradientTape(persistent=True) as tape2:
z = x ** 3
a = 5.0 * x
print("dz/dx untuk z = x^3 :", tape2.gradient(z, x).numpy())
print("da/dx untuk a = 5x :", tape2.gradient(a, x).numpy())
print()
c = tf.constant(4.0)
with tf.GradientTape(watch_accessed_variables=False) as tape3:
tape3.watch(c)
yc = c ** 2
print("constant + watch() :", tape3.gradient(yc, c).numpy())
with tf.GradientTape() as tape4:
yc2 = c ** 2
print("constant tanpa watch():", tape4.gradient(yc2, c))Output yang Dihasilkan:
y = x^2 di x = 3 : y = 9.0
tape.gradient(dy/dx) : 6.0
verifikasi beda hingga: 6.0
dz/dx untuk z = x^3 : 27.0
da/dx untuk a = 5x : 5.0
constant + watch() : 8.0
constant tanpa watch(): NoneMari kita telaah kode di atas baris per baris:
- Baris 9 (
x = tf.Variable(3.0)): gradien hanya dihitung terhadap objek yang bisa berubah, jadixdibuat sebagaiVariable, bukanconstant. - Baris 11-12 (blok
with): satu barisy = x ** 2adalah seluruh “graf” yang terekam — itulah kepraktisan tape: tidak perlu membangun graf khusus di luar kode normal. - Baris 14-16 (
tape.gradient): hasilnya6.0, persis turunan analitik2xdix = 3; nilaiysendiri9.0. - Baris 18-20 (beda hingga): pendekatan numerik
(f(x+ε) − f(x−ε)) / 2εdenganε = 0.001menghasilkan6.0— dua metode yang sepenuhnya berbeda jalur bersepakat pada satu nilai. - Baris 23-27 (
persistent=True): tape biasa sudah terpakai setelah satu panggilangradient; mode persistent membolehkan dua panggilan berturut-turut —3x² = 27.0untukz = x³dan5.0untuka = 5x. - Baris 30-38 (
watchvs tanpawatch): dengantape.watch(c), gradien2c = 8.0berhasil dihitung untuk sebuah constant. Tanpawatch,tape.gradientmengembalikanNone— bukan angka nol, melainkan tanda bahwacmemang tidak direkam.
Melatih Satu Parameter dengan Tape #
Sekarang kita rakit siklus pelatihan mini: model satu parameter y_pred = w * x pada 10 titik data bersumbu kecil. Data dibangkitkan dari kemiringan 3.0 ditambah derau Gaussian bermula seed 42, sehingga deterministik. Berkas latih_regresi_tape.py menjalankan gradient descent manual — tape menghitung gradien, assign_sub menggeser w:
"""Melatih satu parameter regresi linear dengan GradientTape dan assign_sub."""
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
import numpy as np
import tensorflow as tf
np.random.seed(42)
tf.random.set_seed(42)
x = tf.constant(np.arange(1.0, 11.0), dtype=tf.float32)
noise = np.random.normal(0.0, 0.4, size=10).astype(np.float32)
y_target = tf.constant(3.0 * np.arange(1.0, 11.0) + noise, dtype=tf.float32)
w = tf.Variable(0.0)
learning_rate = 0.01
print("Melatih model y_pred = w * x (target kemiringan sekitar 3.0)")
for epoch in range(1, 101):
with tf.GradientTape() as tape:
y_pred = w * x
loss = tf.reduce_mean(tf.square(y_pred - y_target))
gradien = tape.gradient(loss, w)
w.assign_sub(learning_rate * gradien)
if epoch in (1, 5, 10, 50, 100):
print(f"epoch {epoch:3d} | loss {float(loss):8.4f} | w {float(w):7.4f}")
w_analitik = float(tf.reduce_sum(x * y_target) / tf.reduce_sum(x * x))
print()
print("w hasil training :", round(float(w), 4))
print("w solusi analitik :", round(w_analitik, 4))
print("selisih :", round(abs(float(w) - w_analitik), 6))Output yang Dihasilkan:
Melatih model y_pred = w * x (target kemiringan sekitar 3.0)
epoch 1 | loss 352.3852 | w 2.3293
epoch 5 | loss 0.0861 | w 3.0231
epoch 10 | loss 0.0833 | w 3.0250
epoch 50 | loss 0.0833 | w 3.0250
epoch 100 | loss 0.0833 | w 3.0250
w hasil training : 3.025
w solusi analitik : 3.025
selisih : 0.0Mari kita telaah kode di atas baris per baris:
- Baris 10-11 (seed ganda):
np.random.seed(42)menjaga derau data,tf.random.set_seed(42)menjaga operasi acak TensorFlow — kombinasi yang membuat keluaran identik di setiap eksekusi. - Baris 13-15 (data):
xadalah angka 1 sampai 10; targetnya3xditambah deraufloat32sehingga ini regresi sungguhan, bukan hafalan garis mulus. - Baris 17-18 (parameter dan laju):
wmulai dari nol dan digeser dengan learning rate0.01— nilai kecil yang aman sesuai pembahasan Chapter Gradient Descent . - Baris 21-26 (siklus pelatihan): di dalam tape, forward pass menghitung
y_predlalu loss MSE;tape.gradient(loss, w)menghasilkan turunan loss terhadapw;assign_submenggeserwsearah kebalikan gradien. Inilah seluruh isimodel.fit()versi mini. - Baris 27-28 (laporan berkala): loss runtuh dari
352.39di epoch pertama menjadi0.086pada epoch kelima, lalu diam di0.0833. Nilai itu tidak menuju nol karena derau data — sisa kesalahan yang tidak bisa model satu parameter serap. - Baris 30-34 (uji silang): untuk model tanpa intersep, solusi kuadrat terkecil punya rumus tertutup
Σxy / Σx². Hasil training3.025berimpit penuh dengan solusi analitik3.025— gradient descent menemukan titik optimum yang sama dengan kalkulus.
GradientTape vs Autograd PyTorch #
Kedua mekanisme menghasilkan gradien yang sama persis; bedanya ada pada gaya perekaman. Tabel berikut merangkumnya, dan padanan praktis PyTorch sudah kita jalankan pada Chapter Automatic Differentiation versi PyTorch :
| Aspek | tf.GradientTape | Autograd PyTorch |
|---|---|---|
| Cakupan rekaman | hanya di dalam blok with | selama requires_grad=True |
| Titik mulai gradien | tape.gradient(y, x) | y.backward() |
| Gradien tersimpan di | hasil kembalian fungsi | atribut x.grad |
| Beberapa gradien sekaligus | persistent=True | backward() berulang |
| Tensor non-Variable | tape.watch(c) | c.requires_grad_(True) |
Rujukan resmi: tensorflow.org/guide/autodiff dan halaman API tf.GradientTape . Perbandingan pemakaian nyata di sisi PyTorch tersedia di Chapter Autograd .
Ringkasan #
tf.GradientTape merekam operasi yang terjadi pada Variable, lalu menghitung gradien mundur dengan aturan rantai — terbukti tepat pada y = x² dan selaras dengan beda hingga. Kita juga telah merakit siklus pelatihan manual: tape menghitung gradien, assign_sub memperbarui parameter, dan hasilnya berimpit dengan solusi analitik regresi. Siklus lima baris ini persis seperti yang dijalankan model.fit() di balik layar. Pertanyaan berikutnya: bagaimana cara membangun model yang lebih dari satu parameter tanpa menulis loop itu sendiri? Jawabannya adalah Keras — Chapter berikutnya menyusun model pertama kita dengan Sequential dan Functional API.