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:

PintuFungsiKapan 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, eyecetakan berisi nilai tetappenampung hasil, matriks identitas
torch.arange, linspacerentang beraturansumbu waktu, kisi koordinat
torch.randn, randangka acakinisialisasi bobot, data sintetis

Berkas pembuatan_tensor.py mengeksekusi kelima pintu tersebut sekaligus:

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

text
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:

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

text
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'): cpu

Mari kita telaah kode di atas baris per baris:

  1. Baris 7-13: torch.randn(3) menghasilkan float32, sedangkan torch.tensor([1, 2, 3]) menghasilkan int64 — default dtype mengikuti jenis angka yang kita berikan.
  2. Baris 15-17: .to(torch.float32) dan .to(torch.float64) mengembalikan tensor baru dengan jenis angka yang dituju; tensor aslinya tidak berubah.
  3. Baris 19-23: penjumlahan 0.1 + 0.2 memperlihatkan presisi tiap dtype. Angka 0.1 tidak bisa direpresentasikan persis dalam biner, sehingga float32 menyimpan pembulatannya lebih kasar (0.3000000119...) daripada float64 (0.30000000000000004). Deep learning menoleransi selisih sekecil ini demi kecepatan dan hemat memori.
  4. Baris 25-27: device mencatat tempat tensor disimpan. Mesin uji ini memasang wheel CPU sehingga default-nya cpu; 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:

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

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

Mari kita telaah kode di atas baris per baris:

  1. Baris 9-12 (Tahap 1): data (6, 3) — enam contoh, tiga fitur — dibuat sebagai float32 secara sadar lewat astype, menyesuaikan dtype model yang akan memakainya.
  2. Baris 14-20 (Tahap 2): from_numpy memindahkan ndarray ke tensor tanpa menyalin. Reduksi mean(dim=0) dan std(dim=0) menghitung statistik per kolom — operasi yang sama dengan np.mean(axis=0) dari section Dasar Python .
  3. 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 .
  4. Baris 27-29 (Tahap 4): .numpy() mengembalikan ndarray tanpa menyalin. Dari sini matplotlib dan scikit-learn bisa memakai hasilnya seperti ndarray biasa.
  5. Baris 31-37 (demonstrasi): karena X dan X_np berbagi satu blok memori, menulis X_np[0, 0] = 999.0 langsung mengubah X[0, 0].

Ringkasan Pemilihan #

KebutuhanPanggilanEfek memori
Eksperimen data keciltorch.tensor(data)salinan baru
Data NumPy siap pakaitorch.from_numpy(arr)berbagi memori
Hasil torch untuk NumPytensor.numpy()berbagi memori
Hasil torch yang lepastensor.clone().numpy()salinan baru
Ubah jenis angkatensor.to(torch.float32)tensor baru
Pindah perangkattensor.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().