PyTorch adalah pustaka deep learning yang menjadi penerus semangat NumPy: array-nya disebut tensor, operasinya vectorized, dan antarmukanya terasa akrab. Namun pustaka ini tidak terpasang di lingkungan penulisan bab ini, jadi kita menetapkan aturan yang sama seperti bab NumPy dan TensorFlow: setiap blok yang diuji membuktikan konsepnya hanya dengan NumPy, dan kode PyTorch muncul sebagai ilustrasi # notest yang memerlukan pip install torch untuk dijalankan sendiri.
Meski demikian, inti interoperabilitas NumPy–PyTorch justru bisa dibuktikan tanpa PyTorch: aturan zero-copy (berbagi memori), pemetaan dtype, dan migrasi antar perangkat. Kita mulai dari yang pertama.
Zero-copy: Berbagi Memori ala from_numpy #
torch.from_numpy(a) tidak menyalin data; tensor yang dihasilkan menunjuk ke buffer array NumPy yang sama, sehingga perubahan satu sisi langsung terlihat di sisi lain. Perilaku ini identik dengan view NumPy — dan view bisa kita buktikan sekarang juga:
import numpy as np
a = np.array([1.0, 2.0, 3.0])
v = a.view()
print("view berbagi memori ->", np.shares_memory(a, v))
a[0] = 99.0
print("isi view:", v)Output yang Dihasilkan:
view berbagi memori -> True
isi view: [99. 2. 3.]Bedah singkat:
- Baris 3-4 (
a.view()): view adalah ndarray baru yang berbagi buffer dengana;np.shares_memorymembuktikanTrue. - Baris 5-6: menulisi
a[0]otomatis mengubahv— dua nama, satu memori. Inilah semantik yang diterapkantorch.from_numpy: mutasi array NumPy terlihat oleh tensor, dan sebaliknya.
Membedah Semantik Konversi lewat Simulasi #
PyTorch menyediakan dua pintu masuk dengan semantik berbeda: from_numpy (zero-copy) dan torch.tensor (salinan penuh). Untuk membuktikan perbedaannya tanpa pustaka torch, kita buat tensor tiruan — kelas kecil yang membungkus array dan lapor status memorinya:
import numpy as np
class TensorMock:
"""Meniru tensor yang membungkus array NumPy."""
def __init__(self, arr):
self._data = arr
def berbagi_dengan(self, arr):
return np.shares_memory(self._data, arr)
a = np.array([10, 20, 30])
t = TensorMock(a)
print("zero-copy ->", t.berbagi_dengan(a))
c = TensorMock(a.copy())
print("salinan penuh ->", c.berbagi_dengan(a))
a[0] = 99
print("isi t:", t._data)Output yang Dihasilkan:
zero-copy -> True
salinan penuh -> False
isi t: [99 20 30]Mari kita baca:
- Baris 3-8 (
class TensorMock): kelas menyimpan referensi ke array, persis sepertifrom_numpymenyimpan referensi ke buffer NumPy; methodberbagi_denganmemeriksa status memori. - Baris 10-11 (
t = TensorMock(a)): membungkus tanpa menyalin — analogitorch.from_numpy(a)— laporannyaTrue. - Baris 12-13 (
TensorMock(a.copy())): membungkus salinan — analogitorch.tensor(a)— laporannyaFalse. - Baris 14-15: mutasi
a[0]terbawa kettetapi tidak kec; dua pintu masuk, dua konsekuensi.
Tabel berikut merangkum matriks konversi antar kedua pustaka:
| Arah | API | Memori |
|---|---|---|
| ndarray → tensor | torch.from_numpy(a) | zero-copy, berbagi buffer |
| ndarray → tensor | torch.tensor(a) | salinan baru |
| tensor → ndarray | t.numpy() | zero-copy (tensor CPU) |
| tensor GPU → ndarray | t.cpu().numpy() | salinan (harus pindah ke CPU dulu) |
Pemetaan dtype NumPy ke torch #
Konversi juga menuntut kesesuaian dtype. Detail yang sering mengejutkan: ndarray bawaan NumPy bertipe float64, sedangkan tensor bawaan PyTorch bertipe float32. Konversi eksplisit dikerjakan di sisi NumPy dengan astype:
import numpy as np
a64 = np.array([1.5, 2.5])
print(a64.dtype)
a32 = a64.astype(np.float32)
print(a32.dtype, a32)
print(np.array([1, 2, 3]).dtype, "vs", np.array([1, 2, 3], dtype=np.int32).dtype)Output yang Dihasilkan:
float64
float32 [1.5 2.5]
int64 vs int32Penjelasan:
- Baris 3-4: array default NumPy adalah
float64(8 byte per nilai). - Baris 5-6 (
astype(np.float32)): konversi menurunkan presisi menjadi 4 byte — ukuran yang biasa dipakai pelatihan model; nilainya masih[1.5 2.5]. - Baris 7: bilangan bulat bawaan NumPy adalah
int64, sementara sebagian API model mengharapkanint32— pemetaannya harus diketahui di muka.
| NumPy | PyTorch | Ukuran |
|---|---|---|
np.float64 | torch.float64 (torch.double) | 8 byte |
np.float32 | torch.float32 (torch.float) | 4 byte |
np.int64 | torch.int64 (torch.long) | 8 byte |
np.int32 | torch.int32 (torch.int) | 4 byte |
np.uint8 | torch.uint8 | 1 byte |
np.bool_ | torch.bool | 1 byte |
Kode Integrasi (Ilustratif) #
Berikut pola lengkapnya — blok ini tidak diuji karena memerlukan pustaka torch yang terpasang:
# notest
import numpy as np
import torch # perlu dipasang sendiri: pip install torch
a = np.array([1.0, 2.0, 3.0])
t = torch.from_numpy(a) # zero-copy: berbagi memori dengan a
c = torch.tensor(a) # salinan penuh
kembali = t.numpy() # tensor CPU -> ndarray, juga zero-copy
device = "cuda" if torch.cuda.is_available() else "cpu"
t_pindah = t.to(device) # migrasi antar perangkat
print(t_pindah.device)Baris demi baris:
- Baris 5 (
torch.from_numpy(a)): tensor CPU berbagi buffer dengana— semantik yang telah dibuktikan lewat view danTensorMockdi atas. - Baris 6-7:
torch.tensor(a)menyalin;t.numpy()mengembalikan ndarray tanpa salinan selama tensor berada di CPU. - Baris 8-9 (
t.to(device)): migrasi perangkat memilih"cuda"bila GPU tersedia, jika tidak tetap di"cpu"; tensor hasil GPU memakai memori kartu grafis dan tidak bisa langsung diserahkan ke.numpy()— ia harus dikembalikan dengant_pindah.cpu()lebih dulu.
Dua kalimat penutup: untuk data masuk, susun array NumPy dengan bentuk dan dtype yang benar, lalu serahkan dengan from_numpy atau torch.tensor sesuai kebutuhan salin; untuk data keluar, panggil .numpy() setelah tensor berada di CPU. Pola pemeriksaan ketersediaan pustaka (try/except ImportError) dibahas serupa pada bab NumPy dan TensorFlow.