Section Tensor dan Komputasi
sudah membedah anatomi tensor — rank, shape, indexing, broadcasting, hingga manajemen memori. Yang belum kita latih adalah bagian yang paling sering dikerjakan sehari-hari: menghidupkan tensor dari data nyata, menjaga jenis angkanya (dtype), memindahkannya antar perangkat, dan mengubahnya bolak-balik antara NumPy dan torch. Chapter ini menyusun semuanya sebagai satu alur kerja utuh — dari berkas CSV di NumPy, masuk ke torch untuk dihitung, lalu kembali ke NumPy untuk divisualisasikan — bukan sekadar daftar fungsi penciptaan tensor.
Lima Pintu Membuat Tensor #
Hampir semua tensor lahir lewat lima pintu berikut, dan tiap pintu punya niat pemakaian yang berbeda:
| Pintu | Fungsi | Kapan dipakai |
|---|---|---|
torch.tensor(data) | dari list/nilai Python (menyalin data) | data kecil, eksperimen cepat |
torch.from_numpy(arr) | dari ndarray NumPy (berbagi memori) | data hasil pandas/NumPy |
torch.zeros, ones, full, eye | cetakan berisi nilai tetap | penampung hasil, matriks identitas |
torch.arange, linspace | rentang beraturan | sumbu waktu, kisi koordinat |
torch.randn, rand | angka acak | inisialisasi bobot, data sintetis |
Berkas pembuatan_tensor.py mengeksekusi kelima pintu tersebut sekaligus:
"""Lima pintu membuat tensor: dari data, dari NumPy, dari cetakan, dari rentang, dari acak."""
import numpy as np
import torch
torch.manual_seed(42)
# Pintu 1: dari data Python (list bersarang) -- torch.tensor MENYALIN data
data = [[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]]
t_data = torch.tensor(data)
print("torch.tensor(data) :", tuple(t_data.shape), t_data.dtype)
# Pintu 2: dari ndarray NumPy -- from_numpy BERBAGI memori dengan sumbernya
arr = np.array([[1.0, 2.0],
[3.0, 4.0]], dtype=np.float32)
t_arr = torch.from_numpy(arr)
print("torch.from_numpy :", tuple(t_arr.shape), t_arr.dtype)
# Pintu 3: cetakan berisi nilai tetap
print("torch.zeros(2, 3) :", torch.zeros(2, 3).tolist())
print("torch.ones(2, 2) :", torch.ones(2, 2).tolist())
print("torch.full((2,), 7):", torch.full((2,), 7.0).tolist())
print("torch.eye(3) :", torch.eye(3).tolist())
# Pintu 4: rentang beraturan
print("arange(0, 10, 2) :", torch.arange(0, 10, 2).tolist())
print("linspace(0, 1, 5) :", [round(v, 2) for v in torch.linspace(0.0, 1.0, 5).tolist()])
# Pintu 5: angka acak (bobot awal model) -- wajib manual_seed agar bisa direproduksi
print("randn(2, 3) :", [[round(v, 4) for v in baris] for baris in torch.randn(2, 3).tolist()])Output yang Dihasilkan:
torch.tensor(data) : (2, 3) torch.float32
torch.from_numpy : (2, 2) torch.float32
torch.zeros(2, 3) : [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
torch.ones(2, 2) : [[1.0, 1.0], [1.0, 1.0]]
torch.full((2,), 7): [7.0, 7.0]
torch.eye(3) : [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
arange(0, 10, 2) : [0, 2, 4, 6, 8]
linspace(0, 1, 5) : [0.0, 0.25, 0.5, 0.75, 1.0]
randn(2, 3) : [[0.3367, 0.1288, 0.2345], [0.2303, -1.1229, -0.1863]]Perhatikan dua hal dari keluaran tersebut. Pertama, torch.tensor mengenali list bersarang dan memilih float32 secara otomatis untuk angka desimal. Kedua, arange(0, 10, 2) menghasilkan int64, bukan float32 — jenis angka mengikuti jenis masukan, dan perbedaan ini menjadi penting di subbab berikutnya.
dtype: Kesepakatan Jenis Angka #
dtype menentukan berapa bit dipakai tiap angka dan akibatnya seberapa presisi perhitungan. PyTorch memilih float32 sebagai default untuk angka desimal — setengah memori float64 dan format asli yang diproses GPU dan TPU, sehingga hampir seluruh deep learning berjalan di atasnya. NumPy justru memilih float64 sebagai default-nya, dan perbedaan kecil ini sering mengejutkan pendatang baru.
Berkas dtype_dan_device.py memeriksa default tersebut, konversi .to(), dan efek presisinya:
"""dtype menentukan cara angka disimpan; device menentukan tempat angka dihitung."""
import torch
torch.manual_seed(42)
# Default torch untuk angka desimal adalah float32
a = torch.randn(3)
print("randn default :", a.dtype)
# Tensor bilangan bulat dibuat sebagai int64
b = torch.tensor([1, 2, 3])
print("tensor [1, 2, 3] :", b.dtype)
# Konversi dtype dengan .to()
print("b ke float32 :", b.to(torch.float32).tolist())
print("a ke float64 :", a.to(torch.float64).dtype)
# float32 vs float64: presisi 0.1 + 0.2
c32 = torch.tensor([0.1, 0.2], dtype=torch.float32).sum()
c64 = torch.tensor([0.1, 0.2], dtype=torch.float64).sum()
print("0.1+0.2 float32 :", repr(c32.item()))
print("0.1+0.2 float64 :", repr(c64.item()))
# device: tempat tensor disimpan; default cpu pada wheel CPU
print("device default :", a.device)
print("device setelah .to('cpu'):", a.to("cpu").device)Output yang Dihasilkan:
randn default : torch.float32
tensor [1, 2, 3] : torch.int64
b ke float32 : [1.0, 2.0, 3.0]
a ke float64 : torch.float64
0.1+0.2 float32 : 0.30000001192092896
0.1+0.2 float64 : 0.30000000000000004
device default : cpu
device setelah .to('cpu'): cpuMari kita telaah kode di atas baris per baris:
- Baris 7-13:
torch.randn(3)menghasilkanfloat32, sedangkantorch.tensor([1, 2, 3])menghasilkanint64— default dtype mengikuti jenis angka yang kita berikan. - Baris 15-17:
.to(torch.float32)dan.to(torch.float64)mengembalikan tensor baru dengan jenis angka yang dituju; tensor aslinya tidak berubah. - Baris 19-23: penjumlahan
0.1 + 0.2memperlihatkan presisi tiap dtype. Angka0.1tidak bisa direpresentasikan persis dalam biner, sehinggafloat32menyimpan pembulatannya lebih kasar (0.3000000119...) daripadafloat64(0.30000000000000004). Deep learning menoleransi selisih sekecil ini demi kecepatan dan hemat memori. - Baris 25-27:
devicemencatat tempat tensor disimpan. Mesin uji ini memasang wheel CPU sehingga default-nyacpu; pola.to(device)untuk GPU dibahas tuntas di chapter Komputasi CPU dan GPU .
Alur Kerja Nyata: NumPy ke torch dan Kembali #
Alur kerja sehari-hari jarang murni torch: data mentah biasanya lahir sebagai ndarray (dari pandas, berkas .npz, atau hasil pra-proses), dihitung sebagai tensor, lalu hasilnya dikembalikan ke NumPy untuk digambar dengan matplotlib atau dihitung metriknya. Berkas alur_numpy_torch.py menjalankan siklus penuh itu:
"""Alur kerja nyata: data NumPy -> tensor torch -> hitung -> kembali ke NumPy."""
import numpy as np
import torch
torch.manual_seed(42)
np.random.seed(42)
# Tahap 1: data mentah berupa ndarray (misalnya hasil membaca berkas CSV)
X_np = np.random.uniform(10.0, 100.0, size=(6, 3)).astype(np.float32)
print("Tahap 1 - data mentah NumPy :", X_np.shape, X_np.dtype)
print(" baris pertama:", [round(v, 2) for v in X_np[0].tolist()])
# Tahap 2: pindah ke tensor lalu standardisasi tiap kolom (reduksi dim=0)
X = torch.from_numpy(X_np)
rata2 = X.mean(dim=0)
simpangan = X.std(dim=0)
X_std = (X - rata2) / simpangan
print("Tahap 2 - tensor terstandardisasi:", tuple(X_std.shape), X_std.dtype)
print(" baris pertama:", [round(v, 3) for v in X_std[0].tolist()])
# Tahap 3: hitung prediksi dengan bobot tetap (placeholder model)
w = torch.tensor([0.5, -0.3, 0.8])
prediksi = X_std @ w
print("Tahap 3 - prediksi:", [round(v, 3) for v in prediksi.tolist()])
# Tahap 4: hasil kembali ke NumPy untuk alat non-deep-learning (plot, metrik)
prediksi_np = prediksi.numpy()
print("Tahap 4 - prediksi NumPy:", prediksi_np.shape, prediksi_np.dtype)
# Ingat: from_numpy dan .numpy() berbagi memori dengan sumbernya
print()
print("Demonstrasi memori bersama:")
sebelum = X[0, 0].item()
X_np[0, 0] = 999.0
print(" ubah X_np[0,0] menjadi 999.0")
print(" X[0, 0] ikut berubah menjadi", X[0, 0].item(), "| nilai semula:", round(sebelum, 2))Output yang Dihasilkan:
Tahap 1 - data mentah NumPy : (6, 3) float32
baris pertama: [43.71, 95.56, 75.88]
Tahap 2 - tensor terstandardisasi: (6, 3) torch.float32
baris pertama: [-0.278, 1.36, 0.646]
Tahap 3 - prediksi: [-0.03, -0.51, -0.815, 1.831, -0.104, -0.372]
Tahap 4 - prediksi NumPy: (6,) float32
Demonstrasi memori bersama:
ubah X_np[0,0] menjadi 999.0
X[0, 0] ikut berubah menjadi 999.0 | nilai semula: 43.71Mari kita telaah kode di atas baris per baris:
- Baris 9-12 (Tahap 1): data
(6, 3)— enam contoh, tiga fitur — dibuat sebagaifloat32secara sadar lewatastype, menyesuaikan dtype model yang akan memakainya. - Baris 14-20 (Tahap 2):
from_numpymemindahkan ndarray ke tensor tanpa menyalin. Reduksimean(dim=0)danstd(dim=0)menghitung statistik per kolom — operasi yang sama dengannp.mean(axis=0)dari section Dasar Python . - Baris 22-25 (Tahap 3): operator
@menjalankan perkalian matriks-vektor, meniru satu layer linear dengan bobot tetap. Katalog operasi lengkap — elementwise, matmul bertingkat, broadcasting — tersedia di chapter Operasi Tensor . - Baris 27-29 (Tahap 4):
.numpy()mengembalikan ndarray tanpa menyalin. Dari sini matplotlib dan scikit-learn bisa memakai hasilnya seperti ndarray biasa. - Baris 31-37 (demonstrasi): karena
XdanX_npberbagi satu blok memori, menulisX_np[0, 0] = 999.0langsung mengubahX[0, 0].
Ringkasan Pemilihan #
| Kebutuhan | Panggilan | Efek memori |
|---|---|---|
| Eksperimen data kecil | torch.tensor(data) | salinan baru |
| Data NumPy siap pakai | torch.from_numpy(arr) | berbagi memori |
| Hasil torch untuk NumPy | tensor.numpy() | berbagi memori |
| Hasil torch yang lepas | tensor.clone().numpy() | salinan baru |
| Ubah jenis angka | tensor.to(torch.float32) | tensor baru |
| Pindah perangkat | tensor.to(device) | tensor baru di device tujuan |
Tensor kini bisa kita lahirkan dari mana saja dan rawat jenisnya. Satu potensi kekuatan masih tersimpan: tensor tahu cara menghitung turunannya sendiri. Chapter berikutnya melanjutkan langsung dari chapter Autograd
— bukan untuk mengulang graph-nya, melainkan untuk memakainya dalam siklus pelatihan sungguhan: backward(), step(), dan zero_grad().