Sebuah model deep learning, betapapun besarnya, tersusun dari operasi tensor yang jenisnya bisa dihitung dengan jari: operasi elementwise untuk aktivasi, reduksi untuk menghitung loss dan statistik, matmul untuk mengalikan input dengan bobot, serta penggabungan untuk merakit hasil antar layer. Kita sudah mengenal keempat keluarga ini pada NumPy; chapter ini menjalankannya di PyTorch dengan tambahan dua hal yang khas: matmul versi batch yang memproses lusinan matriks sekaligus, dan argumen dim/keepdim yang bentuk hasilnya sering menjadi sumber kebingungan. Seluruh contoh memakai angka kecil yang bisa dihitung ulang dengan mata.

Operasi Elementwise #

Operasi elementwise memasangkan elemen yang seposisi; syaratnya shape kedua operand sama (atau kompatibel lewat broadcasting yang dibahas pada chapter berikutnya). Berkas operasi_dasar.py memuat keluarga ini sekaligus reduksi:

python
"""Operasi elementwise dan reduksi dengan dim serta keepdim."""

import torch

A = torch.tensor([[1.0, 2.0, 3.0],
                  [4.0, 5.0, 6.0]])
B = torch.tensor([[10.0, 20.0, 30.0],
                  [40.0, 50.0, 60.0]])

print("A + B:")
print(A + B)
print("A * B:")
print(A * B)
print("A ** 2:")
print(A ** 2)
print("torch.relu(A - 3.5):")
print(torch.relu(A - 3.5))

print()
print("A.sum()                   :", A.sum().item())
print("A.mean()                  :", A.mean().item())
print("A.sum(dim=0)  per kolom   :", A.sum(dim=0).tolist())
print("A.sum(dim=1)  per baris   :", A.sum(dim=1).tolist())
print("A.sum(dim=1, keepdim=True):")
print(A.sum(dim=1, keepdim=True))
nilai, posisi = A.max(dim=1)
print("A.max(dim=1)  values      :", nilai.tolist())
print("A.max(dim=1)  indices     :", posisi.tolist())

Output yang Dihasilkan:

text
A + B:
tensor([[11., 22., 33.],
        [44., 55., 66.]])
A * B:
tensor([[ 10.,  40.,  90.],
        [160., 250., 360.]])
A ** 2:
tensor([[ 1.,  4.,  9.],
        [16., 25., 36.]])
torch.relu(A - 3.5):
tensor([[0.0000, 0.0000, 0.0000],
        [0.5000, 1.5000, 2.5000]])

A.sum()                   : 21.0
A.mean()                  : 3.5
A.sum(dim=0)  per kolom   : [5.0, 7.0, 9.0]
A.sum(dim=1)  per baris   : [6.0, 15.0]
A.sum(dim=1, keepdim=True):
tensor([[ 6.],
        [15.]])
A.max(dim=1)  values      : [3.0, 6.0]
A.max(dim=1)  indices     : [2, 2]

Mari kita telaah kode di atas baris per baris:

  1. Baris 10-17 (empat operasi elementwise): +, *, dan ** bekerja per elemen dan shape hasil selalu mengikuti operand; torch.relu(A - 3.5) menunjukkan komposisi khas model — pengurangan elementwise diikuti fungsi aktivasi yang memangkas nilai negatif menjadi nol.
  2. Baris 20-21 (reduksi penuh): tanpa dim, sum dan mean menggabungkan seluruh tensor menjadi satu angka: total 21.0 dan rata-rata 3.5.
  3. Baris 22-23 (dim=0 versus dim=1): sumbu yang disebutkan adalah sumbu yang dihapus. dim=0 menggabungkan sepanjang baris sehingga hasilnya per kolom [5, 7, 9]; dim=1 menggabungkan sepanjang kolom sehingga hasilnya per baris [6, 15].
  4. Baris 24-25 (keepdim=True): sumbu yang digabungkan dipertahankan dengan panjang 1 — hasil (2, 1) bukan (2,). Bentuk ini penting supaya hasil reduksi bisa langsung dikurangkan kembali ke A lewat broadcasting.
  5. Baris 26-28 (max dengan dim): max mengembalikan dua keluaran sekaligus — values berisi nilai terbesar tiap baris dan indices posisinya; indeks inilah yang dipakai saat menghitung akurasi klasifikasi.

Matmul: 2D, Vektor, dan Batch 3D #

Operator @ menjalankan perkalian matriks dengan aturan shape yang ketat: (n, k) @ (k, m) menghasilkan (n, m). Kekuatan khusus PyTorch ada pada matmul berbatch — bila operand berdimensi lebih tinggi, dua sumbu terakhir diperlakukan sebagai matriks dan sumbu di depannya menunjuk batch. Berkas matmul_batch.py membuktikannya:

python
"""Perkalian matriks 2D, batch 3D, dan penggabungan cat/stack."""

import torch

A = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0]])
B = torch.tensor([[10.0, 20.0, 30.0],
                  [40.0, 50.0, 60.0]])

C = A @ B
print("2D  (2,2) @ (2,3)     ->", tuple(C.shape))
print(C)

v = torch.tensor([1.0, 2.0])
print("1D  (2,) @ (2,3)      ->", tuple((v @ B).shape), "=", (v @ B).tolist())

b1 = torch.ones(2, 3, 4)
b2 = torch.ones(2, 4, 5)
print("3D  (2,3,4) @ (2,4,5) ->", tuple((b1 @ b2).shape))
print("3D  (2,3,4) @ (4,5)   ->", tuple((b1 @ torch.ones(4, 5)).shape))

P = torch.ones(2, 3)
Q = torch.zeros(2, 3)
print("cat  dim=0             ->", tuple(torch.cat([P, Q], dim=0).shape))
print("cat  dim=1             ->", tuple(torch.cat([P, Q], dim=1).shape))
print("stack dim=0            ->", tuple(torch.stack([P, Q], dim=0).shape))

Output yang Dihasilkan:

text
2D  (2,2) @ (2,3)     -> (2, 3)
tensor([[ 90., 120., 150.],
        [190., 260., 330.]])
1D  (2,) @ (2,3)      -> (3,) = [90.0, 120.0, 150.0]
3D  (2,3,4) @ (2,4,5) -> (2, 3, 5)
3D  (2,3,4) @ (4,5)   -> (2, 3, 5)
cat  dim=0             -> (4, 3)
cat  dim=1             -> (2, 6)
stack dim=0            -> (2, 2, 3)

Mari kita telaah kode di atas baris per baris:

  1. Baris 10-12 (matmul 2D): (2,2) @ (2,3) menghasilkan (2,3); elemen C[0, 0] = 1×10 + 2×40 = 90 bisa diverifikasi manual.
  2. Baris 14-15 (vektor @ matriks): vektor (2,) dipromosikan otomatis — hasilnya vektor (3,), bukan matriks baris; nilai [90, 120, 150] sama dengan baris pertama C karena isinya memang identik.
  3. Baris 17-20 (batch 3D): (2,3,4) @ (2,4,5) menjalankan dua matmul (3,4)@(4,5) secara paralel dan menghasilkan (2,3,5); operand 2D biasa juga diterima dan disiarkan ke seluruh batch.
  4. Baris 22-26 (cat versus stack): cat memperpanjang sumbu yang sudah ada — dua tensor (2,3) menjadi (4,3) atau (2,6) — sedangkan stack menyisipkan sumbu baru sehingga hasilnya (2,2,3): tensor pertama dan kedua menjadi dua elemen batch.

Tabel Rumus Shape #

OperasiSyarat operandShape hasil
A + B, A * Bsama, atau kompatibel broadcastingbentuk setelah broadcasting
A @ B (2D)(n, k) dan (k, m)(n, m)
v @ B(k,) dan (k, m)(m,)
A @ B (batch)(..., n, k) dan (..., k, m)(..., n, m)
A.sum(dim=d)d validsumbu d dihapus
A.sum(dim=d, keepdim=True)idemsumbu d jadi panjang 1
A.max(dim=d)idemdua hasil: values dan indices
torch.cat(list, dim=d)shape sama kecuali sumbu dpanjang sumbu d dijumlah
torch.stack(list, dim=d)semua shape samasumbu d baru sepanjang jumlah tensor

Katalog operasi tensor lengkap tersedia di docs.pytorch.org/docs/stable/torch.html . Dua keluarga tadi mensyaratkan shape yang sama; kenyataannya model sering menjumlahkan tensor dengan bentuk berbeda — bias (C,) ke aktivasi (N, C). Aturan yang mengizinkannya, broadcasting versi PyTorch, adalah topik chapter berikutnya.