Data sekuens adalah data yang urutan elemennya membawa informasi, sehingga tata letaknya tidak boleh diacak sebelum diproses model. Topik ini membahas cara mewakili teks dan time series (deret waktu) sebagai tensor, konvensi bentuk (batch, panjang_sekuens, fitur) yang digunakan sepanjang jalur ini, serta perlakuan untuk sekuens dengan panjang yang tidak seragam. Setelah topik ini, tensor input untuk layer embedding maupun jaringan rekuren dapat disiapkan dengan bentuk yang benar.
Urutan yang membawa makna#
Ciri pembeda data sekuens terletak pada hubungan antara elemennya. Pada data tabular, baris-baris biasanya saling bebas, sehingga menukar urutan baris tidak mengubah informasi yang tersimpan. Pada data sekuens, urutan justru bagian dari informasi itu sendiri. Kalimat “perpustakaan tutup sore ini” menyampaikan hal yang berbeda apabila katanya diacak, dan catatan suhu harian kehilangan arti tren apabila urutan hariannya diacak.
PyTorch tidak menuntut tipe data khusus untuk menyimpan sekuens, karena semua bermuara pada tensor dan bentuknya. Yang membedakan hanyalah kesepakatan tentang arti setiap dimensi. Konvensi yang digunakan pada jalur ini menempatkan batch pada dimensi pertama, panjang sekuens pada dimensi kedua, dan fitur pada dimensi ketiga, sehingga satu batch sekuens berbentuk (batch, panjang_sekuens, fitur).
Konsekuensi urutan yang membawa makna itu terasa saat data disiapkan. Mengacak urutan contoh dalam satu batch tetap sah, karena yang ditukar hanyalah identitas contoh dan bukan elemen di dalamnya. Yang tidak boleh adalah mengacak elemen di dalam satu sekuens, misalnya menomori hari secara acak pada catatan suhu, karena deret yang dihasilkan tidak lagi mewakili kejadian yang sesungguhnya.
Konvensi bentuk (batch, panjang_sekuens, fitur)#
Setiap dimensi pada konvensi tersebut membawa arti yang berbeda. Batch adalah banyaknya contoh yang diproses sekaligus, panjang sekuens adalah banyaknya langkah waktu atau token pada setiap contoh, sedangkan fitur adalah banyaknya angka yang mewakili satu langkah. Diagram berikut memperlihatkan kedua bentuk yang paling sering ditemui pada topik ini.
Untuk teks, dimensi fitur tidak muncul secara eksplisit karena satu langkah direpresentasikan sebagai satu indeks token, sehingga bentuknya cukup (batch, panjang_sekuens) bertipe bilangan bulat. Indeks tersebut adalah penunjuk baris pada tabel kosakata (vocabulary), bukan besaran fisik yang siap dihitung. Untuk time series satu variabel seperti suhu, fiturnya berjumlah satu sehingga bentuknya (batch, panjang_sekuens, 1), dan apabila sensor mencatat suhu serta kelembapan sekaligus, fiturnya menjadi dua.
Layer sekuens di PyTorch seperti nn.RNN dan nn.LSTM secara default menerima bentuk (panjang_sekuens, batch, fitur). Opsi batch_first=True mengubah kesepakatan itu menjadi (batch, panjang_sekuens, fitur) sebagaimana konvensi di atas, dan seluruh contoh pada jalur ini menggunakan opsi tersebut agar bentuknya konsisten dengan cara data disusun.
Time series menjadi jendela#
Deret angka yang panjang perlu dipotong menjadi contoh-contoh berukuran sama sebelum diberikan ke model. Teknik yang lazim adalah jendela geser (sliding window), yaitu mengambil potongan lima hari berturut-turut, menggeser satu hari, lalu mengambil potongan berikutnya. Setiap jendela menjadi satu contoh pada dimensi batch, sehingga deret panjang berubah menjadi kumpulan sekuens pendek yang bentuknya seragam.
import torch
# suhu harian satu sensor selama 14 hari, dalam derajat Celsius
suhu = [26.5, 27.0, 26.8, 27.4, 28.1, 28.6, 27.9,
27.2, 26.9, 27.5, 28.0, 28.4, 29.0, 28.3]
# setiap jendela lima hari berturut-turut menjadi satu contoh sekuens
panjang_jendela = 5
jendela = [suhu[i:i + panjang_jendela] for i in range(len(suhu) - panjang_jendela + 1)]
# batch: (10, 5, 1) = (jumlah_jendela, panjang_sekuens, fitur)
batch = torch.tensor(jendela, dtype=torch.float32).unsqueeze(-1)
print("Jumlah jendela :", batch.shape[0])
print("Bentuk batch :", tuple(batch.shape))
print("Jendela pertama :", [round(v, 1) for v in batch[0].squeeze(-1).tolist()])
print("Jendela terakhir:", [round(v, 1) for v in batch[-1].squeeze(-1).tolist()])Jumlah jendela : 10
Bentuk batch : (10, 5, 1)
Jendela pertama : [26.5, 27.0, 26.8, 27.4, 28.1]
Jendela terakhir: [27.5, 28.0, 28.4, 29.0, 28.3]Jumlah jendela dihitung dari , yaitu panjang deret dikurangi lebar jendela lalu ditambah satu. Setiap jendela memuat lima langkah dengan satu fitur, sehingga method unsqueeze(-1) menambah dimensi ketiga dan menghasilkan bentuk (10, 5, 1). Pada pelatihan sesungguhnya, jendela-jendela ini dipasangkan dengan nilai yang ingin diprediksi, misalnya suhu hari setelah jendela berakhir.
Jendela yang saling bertumpuk seperti di atas lazim digunakan untuk prediksi langkah berikutnya, sehingga data yang sama digunakan berkali-kali dengan sudut pandang berbeda. Apabila tumpang-tindih tidak diinginkan, langkah geser diperbesar hingga sama dengan lebar jendela sehingga potongan-potongannya saling bersambung tanpa pengulangan.
Teks menjadi indeks token#
Teks masuk ke model melalui dua tahap, yaitu pemenggalan menjadi token dan pemetaan setiap token menjadi indeks bilangan bulat. Pemenggalan paling sederhana memisahkan kata menurut spasi, sedangkan pemetaannya disimpan dalam tabel kosakata yang dibangun dari data pelatihan. Indeks 0 lazim dicadangkan untuk token pad, yaitu token isian (padding) yang menyeragamkan panjang sekuens dalam satu batch.
import torch
# kosakata kecil; indeks 0 dicadangkan untuk token <pad>
kosakata = {"<pad>": 0, "pengunjung": 1, "perpustakaan": 2,
"naik": 3, "sore": 4, "ini": 5}
def ke_indeks(kalimat):
return [kosakata[kata] for kata in kalimat.split()]
# dua kalimat dengan panjang berbeda
kalimat_a = ke_indeks("pengunjung perpustakaan naik") # (3,)
kalimat_b = ke_indeks("pengunjung perpustakaan naik sore ini") # (5,)
# kalimat yang lebih pendek dilapisi <pad> (indeks 0) di ujungnya
kalimat_a = kalimat_a + [kosakata["<pad>"]] * (5 - len(kalimat_a))
# batch: (2, 5) = (jumlah_kalimat, panjang_sekuens), nilai berupa indeks token
batch = torch.tensor([kalimat_a, kalimat_b], dtype=torch.long)
print(batch)
print("Bentuk batch:", tuple(batch.shape))tensor([[1, 2, 3, 0, 0],
[1, 2, 3, 4, 5]])
Bentuk batch: (2, 5)Kalimat yang lebih pendek dilapisi token pad di ujungnya sehingga kedua baris berisi lima token, dan tensor batch memperoleh bentuk (2, 5). Nilai di dalamnya hanyalah penunjuk baris pada tabel kosakata, sehingga operasi aritmetika seperti penjumlahan atau pengurutan nilainya tidak bermakna. Konversi indeks menjadi vektor yang dapat dipelajari dikerjakan layer embedding.
Kesalahan umum#
Kesalahan pertama adalah menukar dimensi panjang sekuens dengan dimensi fitur. Tensor suhu berbentuk (10, 5) yang langsung diberikan tanpa unsqueeze(-1) akan dibaca sebagai batch berukuran 10 dengan panjang sekuens 5 dan tanpa dimensi fitur, sehingga layer sekuens menolaknya karena jumlah fitur tidak cocok. Perbaikannya adalah memeriksa tensor.shape sebelum data masuk ke layer dan memastikan dimensi terakhir benar-benar memuat fitur.
Kesalahan kedua adalah mencampur sekuens dengan panjang berbeda dalam satu tensor tanpa padding. Pemanggilan torch.tensor([kalimat_a, kalimat_b]) pada list yang panjangnya tidak sama tidak dapat membentuk tensor persegi dan langsung gagal. Perbaikannya adalah melapisi token pad seperti contoh di atas, atau menggunakan torch.nn.utils.rnn.pad_sequence yang melakukannya secara otomatis.
Kesalahan ketiga adalah memperlakukan indeks token sebagai angka, misalnya menskalakannya seperti fitur numerik biasa. Indeks hanyalah penunjuk baris pada tabel kosakata, sehingga jarak antara indeksnya tidak menyatakan jarak makna. Nilai tersebut sebaiknya dibiarkan bertipe bilangan bulat sampai dikonversi menjadi vektor embedding.
Ringkasan#
- Data sekuens menyimpan informasi pada urutan elemennya, sehingga urutan itu tidak boleh diacak sebelum diproses.
- Konvensi bentuk pada jalur ini adalah
(batch, panjang_sekuens, fitur), dan teks yang sudah menjadi indeks token cukup berbentuk(batch, panjang_sekuens). - Time series dipotong dengan jendela geser sehingga deret panjang menjadi batch sekuens yang seragam, misalnya
(10, 5, 1). - Sekuens dengan panjang berbeda dilapisi token pad, lazimnya indeks 0, agar satu batch membentuk tensor persegi.
Referensi#
- torch.nn — PyTorch
— Rujukan modul
torch.nnyang memuat konvensi bentuk masukan layer sekuens, termasuk opsibatch_first. - torch.nn.utils.rnn.pad_sequence — PyTorch — Fungsi bawaan untuk melapisi daftar sekuens dengan panjang berbeda menjadi satu tensor batch.
- Learn the Basics — PyTorch — Pengantar resmi tensor PyTorch, termasuk bentuk dan operasi dasarnya.