Topik ini membahas dua cara menghentikan pelacakan autograd, yaitu konteks torch.no_grad() yang menonaktifkan pencatatan untuk seluruh blok kode dan method detach() yang memutus satu tensor dari riwayat operasinya. Keduanya lazim digunakan pada tahap inferensi (inference), evaluasi, dan penyalinan bobot, yaitu situasi ketika gradien tidak lagi dibutuhkan. Pemahaman atas perbedaan keduanya menjaga memori tetap hemat sekaligus mencegah perubahan yang tidak disengaja pada tensor asal.
Mode inferensi dengan no_grad#
Selama model digunakan untuk memprediksi, tidak ada parameter yang perlu diperbarui, sehingga graf komputasi kehilangan kegunaannya. Pencatatan graf tetap memakan memori dan waktu apabila dibiarkan berjalan. Konteks torch.no_grad() menonaktifkan pencatatan untuk semua operasi di dalam bloknya, sehingga hasil operasinya tidak membawa grad_fn dan tidak menyimpan riwayat apa pun:
import torch
w = torch.tensor(2.0, requires_grad=True)
y = w * 3
print("di luar no_grad: y.requires_grad =", y.requires_grad,
"| y.grad_fn =", type(y.grad_fn).__name__)
with torch.no_grad():
z = w * 3
print("di dalam no_grad: z.requires_grad =", z.requires_grad,
"| z.grad_fn =", z.grad_fn)di luar no_grad: y.requires_grad = True | y.grad_fn = MulBackward0
di dalam no_grad: z.requires_grad = False | z.grad_fn = NoneTensor z lahir dari operasi yang sama dengan y, yaitu perkalian dengan 3, tetapi statusnya sama sekali berbeda karena pencatatan dimatikan pada saat ia dihitung. Perbedaan ini murni menyangkut waktu pembuatan, bukan isi tensor. Nilai z sama dengan nilai y, sedangkan riwayat pelacakannya tidak ada.
Pola evaluasi pada model#
Konteks torch.no_grad() biasanya membungkus seluruh perulangan evaluasi, bukan satu operasi saja. Program berikut menggunakan layer nn.Linear kecil sebagai model dan menjalankannya dalam mode inferensi:
import torch
import torch.nn as nn
torch.manual_seed(0)
model = nn.Linear(2, 1)
x = torch.randn(4, 2)
with torch.no_grad():
y = model(x)
print("bentuk y =", tuple(y.shape))
print("y.requires_grad =", y.requires_grad)
print("y.grad_fn =", y.grad_fn)
print("model.weight.requires_grad =", model.weight.requires_grad)bentuk y = (4, 1)
y.requires_grad = False
y.grad_fn = None
model.weight.requires_grad = TruePerhatikan bahwa model.weight tetap berstatus requires_grad=True karena hakikatnya parameter, sedangkan keluaran y tidak membawa grad_fn sama sekali karena seluruh operasinya dijalankan di dalam konteks tanpa pencatatan. Kondisi inilah yang diharapkan pada evaluasi, yaitu parameter tetap utuh, tetapi tidak ada graf yang menumpuk di memori. Method model.eval() yang sering menyertai pola ini mengatur perilaku layer tertentu seperti dropout, sedangkan no_grad() mengatur pencatatan graf, sehingga keduanya saling melengkapi dan bukan saling menggantikan.
Memutus riwayat dengan detach()#
Method detach() bekerja pada satu tensor, bukan satu blok kode. Ia menghasilkan tensor baru yang berbagi penyimpanan dengan tensor asal, tetapi terputus dari riwayat operasinya, sehingga membawa status requires_grad=False dan is_leaf=True:
import torch
w = torch.tensor(2.0, requires_grad=True)
y = w * 3
d = y.detach()
print("y.requires_grad =", y.requires_grad)
print("d.requires_grad =", d.requires_grad)
print("d.is_leaf =", d.is_leaf)
print("nilai d =", d.item())
try:
d.sum().backward()
except RuntimeError as e:
print("RuntimeError:", e)y.requires_grad = True
d.requires_grad = False
d.is_leaf = True
nilai d = 6.0
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fnPercobaan backward() pada tensor hasil detach() gagal dengan pesan “element 0 of tensors does not require grad and does not have a grad_fn” karena tensor itu memang tidak terhubung ke graf mana pun. Kegagalan ini wajar dan justru menegaskan fungsinya, yaitu memutus sambungan secara sadar.
Pola detach().clone() di atas adalah bentuk baku untuk mengambil salinan bobot yang bebas pelacakan, misalnya saat menyimpan bobot sementara atau saat memindahkan nilai antara model. Apabila berbagi penyimpanan justru diinginkan untuk menghemat memori pada data yang tidak akan diubah, detach() saja sudah cukup.
Memilih antara no_grad dan detach#
Tabel berikut merangkum perbedaan keduanya:
| Aspek | torch.no_grad() | detach() |
|---|---|---|
| Cakupan | seluruh operasi di dalam blok | satu tensor tertentu |
| Bentuk | context manager | method pada tensor |
| Hasil | operasi tidak membentuk graf | tensor putus dari riwayat |
| Penggunaan lazim | evaluasi, inferensi, pembaruan parameter | penyalinan bobot, pemutusan cabang tertentu |
Keduanya bukan pilihan yang saling menggantikan. no_grad() adalah alat tingkat blok yang praktis untuk membungkus tahap utuh, sedangkan detach() adalah alat tingkat tensor untuk kasus yang memerlukan ketelitian, misalnya memutus satu cabang perhitungan sambil membiarkan cabang lain tetap terlacak. Dalam praktiknya, inferensi dan evaluasi menggunakan no_grad(), sedangkan manipulasi bobot dan pembersihan keluaran menggunakan detach().
Kesalahan umum#
Kesalahan pertama adalah memanggil backward() di dalam konteks no_grad(). Tidak ada tensor yang terlacak di dalam blok itu, sehingga pemanggilan gagal dengan pesan “element 0 of tensors does not require grad and does not have a grad_fn”. Perbaikannya adalah memindahkan pemanggilan keluar dari konteks, karena penghitungan gradien memang tidak boleh dilakukan di dalam no_grad().
Kesalahan kedua adalah menganggap detach() sebagai penyalin data. Tensor hasilnya berbagi penyimpanan dengan tensor asalnya, sehingga pengubahan melalui operasi in-place pada salah satunya ikut mengubah yang lain tanpa pesan apa pun. Perbaikannya adalah menambahkan .clone() setiap kali salinan yang berdiri sendiri dibutuhkan.
Kesalahan ketiga adalah lupa membungkus perulangan evaluasi dengan no_grad(). Tidak ada error yang muncul, tetapi memori membengkak karena graf menumpuk setiap iterasi dan proses ikut melambat. Karena gejalanya tidak langsung terlihat, kebiasaan membungkus evaluasi sebaiknya ditetapkan sejak pola kode pertama kali ditulis.
Ringkasan#
torch.no_grad()menonaktifkan pencatatan graf untuk seluruh blok kode, sehingga cocok untuk inferensi dan evaluasi.detach()memutus satu tensor dari riwayat operasinya tanpa mengubah nilai, dan hasilnya berbagi penyimpanan dengan tensor asalnya.- Penyalinan bobot yang bebas pelacakan ditulis
detach().clone(), bukandetach()saja. - Keduanya saling melengkapi, yaitu
no_grad()untuk tingkat blok dandetach()untuk tingkat tensor.
Referensi#
- PyTorch documentation — PyTorch
— Rujukan API resmi PyTorch, termasuk
torch.no_graddan methoddetachyang dibahas di sini. - Learn the Basics — PyTorch
— Seri pengantar resmi yang mencontohkan konteks
no_grad()pada tahap inferensi.