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:

python
"""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:

text
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(): None

Mari kita telaah kode di atas baris per baris:

  1. Baris 9 (x = tf.Variable(3.0)): gradien hanya dihitung terhadap objek yang bisa berubah, jadi x dibuat sebagai Variable, bukan constant.
  2. Baris 11-12 (blok with): satu baris y = x ** 2 adalah seluruh “graf” yang terekam — itulah kepraktisan tape: tidak perlu membangun graf khusus di luar kode normal.
  3. Baris 14-16 (tape.gradient): hasilnya 6.0, persis turunan analitik 2x di x = 3; nilai y sendiri 9.0.
  4. Baris 18-20 (beda hingga): pendekatan numerik (f(x+ε) − f(x−ε)) / 2ε dengan ε = 0.001 menghasilkan 6.0 — dua metode yang sepenuhnya berbeda jalur bersepakat pada satu nilai.
  5. Baris 23-27 (persistent=True): tape biasa sudah terpakai setelah satu panggilan gradient; mode persistent membolehkan dua panggilan berturut-turut — 3x² = 27.0 untuk z = x³ dan 5.0 untuk a = 5x.
  6. Baris 30-38 (watch vs tanpa watch): dengan tape.watch(c), gradien 2c = 8.0 berhasil dihitung untuk sebuah constant. Tanpa watch, tape.gradient mengembalikan None — bukan angka nol, melainkan tanda bahwa c memang 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:

python
"""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:

text
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.0

Mari kita telaah kode di atas baris per baris:

  1. 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.
  2. Baris 13-15 (data): x adalah angka 1 sampai 10; targetnya 3x ditambah derau float32 sehingga ini regresi sungguhan, bukan hafalan garis mulus.
  3. Baris 17-18 (parameter dan laju): w mulai dari nol dan digeser dengan learning rate 0.01 — nilai kecil yang aman sesuai pembahasan Chapter Gradient Descent .
  4. Baris 21-26 (siklus pelatihan): di dalam tape, forward pass menghitung y_pred lalu loss MSE; tape.gradient(loss, w) menghasilkan turunan loss terhadap w; assign_sub menggeser w searah kebalikan gradien. Inilah seluruh isi model.fit() versi mini.
  5. Baris 27-28 (laporan berkala): loss runtuh dari 352.39 di epoch pertama menjadi 0.086 pada epoch kelima, lalu diam di 0.0833. Nilai itu tidak menuju nol karena derau data — sisa kesalahan yang tidak bisa model satu parameter serap.
  6. Baris 30-34 (uji silang): untuk model tanpa intersep, solusi kuadrat terkecil punya rumus tertutup Σxy / Σx². Hasil training 3.025 berimpit penuh dengan solusi analitik 3.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 :

Aspektf.GradientTapeAutograd PyTorch
Cakupan rekamanhanya di dalam blok withselama requires_grad=True
Titik mulai gradientape.gradient(y, x)y.backward()
Gradien tersimpan dihasil kembalian fungsiatribut x.grad
Beberapa gradien sekaliguspersistent=Truebackward() berulang
Tensor non-Variabletape.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.