Sejauh ini kita memproses data yang tersedia sekaligus: sebuah gambar masuk ke CNN sebagai satu tensor utuh, dan satu baris tabel masuk ke MLP sebagai satu vektor fitur. Namun sebagian besar data di dunia justru datang berurutan: teks terbaca kata demi kata, harga saham tercatat hari demi hari, sinyal audio tersampel titik demi titik. Pada data semacam itu, urutan elemen membawa makna sendiri — kalimat “Budi membeli jeruk” dan “jeruk membeli Budi” memuat kata yang sama, tetapi maknanya berlawanan karena urutannya berbeda. Section ini membahas keluarga model yang dirancang untuk memproses urutan, dan kita mulai dari bahan bakarnya: cara merepresentasikan data sekuens sebagai dataset yang siap dilatih.

Sekuens dan Dimensinya #

Sekuens adalah daftar nilai yang urutannya bermakna: x = (x_1, x_2, …, x_T) dengan T menyatakan panjang sekuens. Setiap langkah waktu x_t dapat berupa satu nilai sederhana atau sebuah vektor fitur berdimensi D. Dua angka ini — panjang T dan dimensi fitur D — menentukan bentuk tensor yang dipakai model. Tabel berikut memetakan sekuens dari beberapa domain ke ukurannya:

DomainContoh dataSatu langkahFitur per langkah (D)
Tekskalimat 12 kata1 kata (token)dimensi embedding
Deret waktuharga penutupan 30 hari1 hari1 nilai numerik
Audiorekaman 1 detik1 sampel (16.000 per detik)1 amplitudo
Sensor gerakjejak 10 langkah robot1 langkahposisi + kecepatan (2)

Satu perbedaan penting dari data tabular: panjang T boleh berbeda antar sampel — kalimat satu sama kalimat lain tidak sama jumlah katanya. Tensor batch di NumPy maupun PyTorch menuntut dimensi seragam, sehingga kita perlu dua alat tambahan: windowing untuk deret panjang yang ingin dijadikan sampel supervised, dan padding serta truncation untuk menyamakan panjang antar sampel.

Windowing: Deret Waktu menjadi Dataset Supervised #

Model supervised membutuhkan pasangan (X, y). Untuk deret waktu, cara standarnya adalah sliding window: potong jendela sepanjang W langkah sebagai masukan X, lalu gunakan nilai pada langkah ke-W+1 sebagai target y. Jendela digeser satu langkah setiap kali, sehingga satu deret panjang menghasilkan banyak sampel yang saling bertetangga.

Program berikut membangun deret sinus sintetis sepanjang 120 titik, menerapkan jendela W = 8, lalu menunjukkan hasilnya beserta demo padding dan truncation pada sekuens token dengan panjang berbeda:

python
"""Membentuk dataset supervised dari deret waktu: windowing, padding, batch.

Deret sintetis sinyal sinus dipotong menjadi jendela geser (sliding window)
sepanjang 8 langkah; jendela menjadi fitur X dan nilai sesudahnya menjadi
target y. Ditambah demo padding + truncation untuk sekuens panjang beda.
"""

import numpy as np

np.random.seed(42)

# ---------- 1. Deret waktu sintetis: sinus + noise kecil ----------
t = np.arange(120)
deret = np.sin(0.25 * t) + 0.05 * np.random.randn(t.size)

print("Panjang deret         :", deret.shape[0])
print("5 nilai pertama       :", np.round(deret[:5], 3))
print()

# ---------- 2. Windowing: jendela 8 langkah -> dataset supervised ----------
W = 8  # panjang jendela (jumlah langkah waktu masukan)

X, y = [], []
for i in range(len(deret) - W):
    X.append(deret[i : i + W])   # jendela: t .. t+W-1
    y.append(deret[i + W])       # target: nilai pada t+W

X = np.array(X).reshape(len(X), W, 1)  # (N, T, D)
y = np.array(y)                        # (N,)

print("Ukuran jendela W      :", W)
print("Banyak sampel N       :", X.shape[0])
print("Shape X               :", X.shape)
print("Shape y               :", y.shape)
print()
print("X[0] (8 langkah)      :", np.round(X[0, :, 0], 3))
print("y[0] (nilai t+W)      :", round(float(y[0]), 3))
print("X[1] (geser 1 langkah):", np.round(X[1, :, 0], 3))
print("y[1]                  :", round(float(y[1]), 3))
print()

# ---------- 3. Batch: potong 4 sampel pertama ----------
batch_X = X[:4]
batch_y = y[:4]
print("Shape satu batch      :", batch_X.shape, "-> (N, T, D) = (4, 8, 1)")
print("Shape target batch    :", batch_y.shape)
print()

# ---------- 4. Padding & truncation untuk sekuens beda panjang ----------
kalimat = [
    [5, 2, 7, 3, 9],        # panjang 5
    [4, 8, 1],              # panjang 3
    [6, 0, 2, 5, 3, 7, 9],  # panjang 7
]
T_MAX = 6
PAD = 0

print("Padding ke T =", T_MAX, "(token PAD =", PAD, ")")
for k, seq in enumerate(kalimat):
    dipotong = seq[:T_MAX]                    # truncation bila lebih panjang
    diisi = dipotong + [PAD] * (T_MAX - len(dipotong))
    mask = [1] * len(dipotong) + [0] * (T_MAX - len(dipotong))
    print(f"  sekuens {k}: panjang {len(seq):2d} -> {diisi}  mask {mask}")

Output yang Dihasilkan:

text
Panjang deret         : 120
5 nilai pertama       : [0.025 0.24  0.512 0.758 0.83 ]

Ukuran jendela W      : 8
Banyak sampel N       : 112
Shape X               : (112, 8, 1)
Shape y               : (112,)

X[0] (8 langkah)      : [0.025 0.24  0.512 0.758 0.83  0.937 1.076 1.022]
y[0] (nilai t+W)      : 0.886
X[1] (geser 1 langkah): [0.24  0.512 0.758 0.83  0.937 1.076 1.022 0.886]
y[1]                  : 0.805

Shape satu batch      : (4, 8, 1) -> (N, T, D) = (4, 8, 1)
Shape target batch    : (4,)

Padding ke T = 6 (token PAD = 0 )
  sekuens 0: panjang  5 -> [5, 2, 7, 3, 9, 0]  mask [1, 1, 1, 1, 1, 0]
  sekuens 1: panjang  3 -> [4, 8, 1, 0, 0, 0]  mask [1, 1, 1, 0, 0, 0]
  sekuens 2: panjang  7 -> [6, 0, 2, 5, 3, 7]  mask [1, 1, 1, 1, 1, 1]

Mari kita telaah kode di atas baris per baris:

  1. Baris 10-14: seed dikunci agar hasil deterministik, lalu deret waktu dibuat dari sinyal sinus frekuensi 0,25 radian per langkah ditambah noise Gaussian kecil berskala 0,05 — meniru pola halus yang tetap bisa dipelajari model.
  2. Baris 21-26: perulangan i memotong jendela deret[i : i+W] sebagai fitur dan deret[i+W] sebagai target. Jumlah jendela yang bisa dibentuk adalah len(deret) - W = 120 - 8 = 112.
  3. Baris 28-29: daftar jendela dikumpulkan menjadi array lalu di-reshape ke (112, 8, 1). Sumbu terakhir berukuran 1 adalah dimensi fitur D; tanpa reshape ini, NumPy akan menyimpan (112, 8) dan bentuknya tidak lagi konsisten dengan konvensi (N, T, D).
  4. Baris 36-39: X[1] dibandingkan dengan X[0] — keduanya berbagi 7 dari 8 nilai karena jendela hanya bergeser satu langkah. Nilai y[0] (0,886) muncul sebagai elemen terakhir X[1], persis hubungan “nilai target menjadi masukan” pada jendela berikutnya.
  5. Baris 43-46: empat sampel pertama diambil sebagai satu batch berbentuk (4, 8, 1) — inilah bentuk baku yang akan diterima layer rekuren pada chapter berikutnya.

Padding dan Truncation untuk Sekuens Berbeda Panjang #

Bagian keempat program (baris 49-63) menangani masalah panjang variabel. Tiga sekuens token dengan panjang 5, 3, dan 7 harus masuk ke satu tensor dengan T_MAX = 6: sekuens ke-7 token dipotong menjadi 6 (truncation, baris 60), sekuens ke-3 token ditambah tiga token PAD bernilai 0 (padding, baris 61), dan mask 0/1 dibuat menandai posisi mana yang berisi data asli (baris 62). Mask inilah yang nanti dipakai loss function agar posisi PAD tidak dihitung seolah-olah data sungguhan.

PyTorch menyediakan pad_sequence untuk mengotomatiskan langkah ini pada daftar tensor berbeda panjang.

Bentuk Batch (N, T, D) #

Seluruh contoh di atas memakai konvensi (N, T, D): N sampel, T langkah waktu, D fitur per langkah. Konvensi ini disebut batch-first karena sumbu batch berada di depan. Berikut ringkasannya:

KomponenBentukKeterangan
Deret mentah(120,)satu garis waktu
Dataset X(112, 8, 1)N=112, T=8, D=1
Target y(112,)satu nilai per sampel
Batch X(4, 8, 1)potongan 4 sampel
Sekuens setelah padding(3, 6)T diseragamkan ke 6

Kini data sekuens sudah berada dalam bentuk yang standar. Pertanyaan berikutnya adalah arsitektur apa yang memprosesnya: jaringan yang membaca langkah demi langkah sambil mengingat apa yang sudah dibacanya. Chapter berikutnya memperkenalkan recurrent neural network beserta mekanisme hidden state yang menjadi intinya.