Kelas Dataset

Kontrak kelas Dataset: method __len__ dan __getitem__.

Daftar isi

Kelas Dataset adalah kontrak paling dasar dalam pemuatan data PyTorch, yaitu pemetaan dari indeks menjadi satu item data. Topik ini membahas dua method yang membentuk kontrak tersebut, kelas TensorDataset yang sudah disediakan PyTorch, dan sebuah dataset kecil yang ditulis di atas daftar pasangan fitur dan label. Setelah topik ini dipahami, dataset apa pun dapat diserahkan ke DataLoader untuk dikemas menjadi batch.

Kontrak __len__ dan __getitem__#

Dataset adalah abstraksi indeks: kelas dari modul torch.utils.data ini menetapkan dua method wajib yang menghubungkan sebuah bilangan dengan satu item data. Method __len__() mengembalikan jumlah item dalam bentuk bilangan bulat, sedangkan method __getitem__(idx) mengembalikan satu item untuk indeks tertentu. Keduanya menggunakan nama dengan dua garis bawah di awal dan di akhir, yaitu konvensi dunder (double underscore) Python untuk method yang dipanggil secara implisit. Pemanggilan len(dataset) sesungguhnya menjalankan __len__(), dan dataset[5] menjalankan __getitem__(5).

Kontrak ini sengaja dibuat tipis. Dataset tidak menentukan letak data: item boleh berupa baris tensor yang sudah dimuat di memori, file citra yang dibaca saat diminta, atau hasil perhitungan yang disusun lebih dahulu. Yang dituntut hanyalah pemetaan indeks ke item yang konsisten, sehingga len() dan pengindeksan [i] selalu bekerja dengan benar. Diagram berikut merangkum kontrak tersebut.

Koleksi dataindeks 0, 1, 2, 3,4, …, N-1indeks iKelas Dataset__len__() → jumlah item__getitem__(i) → satu itemitemSatu item(fitur, label)tuple tensor
Kontrak kelas Dataset: dua method memetakan indeks menjadi satu item

Ketipasan kontrak itulah yang membuat seluruh ekosistem PyTorch dapat bekerja pada data apa pun. DataLoader, pemisahan data latih dan data uji berdasarkan indeks, hingga pengacakan urutan item, semuanya hanya menuntut dua method tersebut. Selama kontraknya dipenuhi, satu dataset yang sama dapat digunakan lintas topik tanpa perubahan apa pun pada kode pelatihannya.

TensorDataset siap pakai#

Untuk data yang sudah berbentuk tensor, PyTorch menyediakan TensorDataset sehingga kelas tidak perlu ditulis sendiri. Kelas ini menerima satu atau lebih tensor dengan jumlah baris yang sama dan menyandingkan baris ke-i dari setiap tensor menjadi satu item. Dataset pasangan fitur dan label adalah penggunaan yang paling umum: tensor fitur berbentuk (jumlah item, jumlah fitur) dan tensor label berbentuk (jumlah item,) menghasilkan item berupa pasangan baris fitur dan labelnya.

python Contoh: TensorDataset dari pasangan fitur dan label
import torch
from torch.utils.data import TensorDataset

torch.manual_seed(0)

fitur = torch.randn(20, 4)          # shape: (20, 4)
label = torch.randint(0, 3, (20,))  # shape: (20,)

dataset = TensorDataset(fitur, label)

print("Jumlah item:", len(dataset))
x, y = dataset[7]
print("Shape fitur item ke-7:", tuple(x.shape))
print("Label item ke-7      :", y.item())
print("Sama dengan baris 7  :", torch.equal(x, fitur[7]))
text Keluaran
Jumlah item: 20
Shape fitur item ke-7: (4,)
Label item ke-7      : 1
Sama dengan baris 7  : True

Item ke-7 yang diambil sama persis dengan baris ke-7 tensor aslinya. Properti ini penting saat sebuah sampel perlu dilacak kembali ke sumbernya, misalnya saat memeriksa sampel yang gagal dipelajari model. Batasannya juga jelas: TensorDataset hanya cocok untuk data yang muat utuh di memori sebagai tensor. Data yang dibaca dari file memerlukan dataset buatan sendiri seperti pada bagian berikut.

Dataset buatan sendiri#

Dataset buatan sendiri dibuat dengan mewarisi Dataset dan mengisi kedua method kontraknya. Polanya tetap sama untuk data apa pun: __init__ menyiapkan data atau daftar penunjuk ke data, __len__ menghitung jumlahnya, dan __getitem__ mengambil satu item. File berikut mendefinisikan DatasetSensor, yaitu dataset kecil berisi dua belas pasangan fitur dan label. Seed dipasang di awal agar hasilnya dapat direproduksi.

python dataset_sederhana.py
import torch
from torch.utils.data import Dataset

torch.manual_seed(0)


class DatasetSensor(Dataset):
    """Dataset kecil di atas daftar pasangan (fitur, label)."""

    def __init__(self):
        self.data = []
        for i in range(12):
            fitur = torch.randn(4)  # vektor fitur, shape: (4,)
            label = i % 3           # kelas 0, 1, atau 2
            self.data.append((fitur, label))

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        fitur, label = self.data[idx]
        return fitur, torch.tensor(label)


dataset = DatasetSensor()

print("Jumlah item:", len(dataset))
print("Tipe satu item:", type(dataset[0]).__name__)
for i in range(3):
    fitur, label = dataset[i]
    print(f"item {i}: shape fitur {tuple(fitur.shape)}, label {label.item()}")
text Keluaran
Jumlah item: 12
Tipe satu item: tuple
item 0: shape fitur (4,), label 0
item 1: shape fitur (4,), label 1
item 2: shape fitur (4,), label 2

Keluarannya memperlihatkan kontrak yang bekerja: len() mengembalikan 12, dan setiap item berupa tuple berisi tensor fitur berbentuk (4,) serta tensor skalar label. Bentuk tuple ini boleh berbeda dari satu dataset ke dataset lain, misalnya (citra, label) atau (citra, kotak pembatas, label). Pengguna dataset, seperti DataLoader, menyesuaikan diri dengan apa pun yang dikembalikan __getitem__.

Kesalahan umum#

Method __len__ tidak ditulis. Penyebabnya sering berupa asumsi bahwa __getitem__ saja sudah cukup. Akibatnya len(dataset) melempar TypeError tentang objek tanpa len(), dan DataLoader juga gagal karena menyusun sampler dari jumlah item. Perbaikannya dengan mengisi kedua method kontrak, meskipun __len__ hanya berisi satu baris pengembalian nilai.

Tipe item berbeda antara indeks. Satu cabang yang terlewat di dalam __getitem__ membuat indeks tertentu mengembalikan tuple, sementara indeks lain mengembalikan tensor tunggal. Penyusunan batch kemudian gagal dengan pesan tentang bentuk yang tidak cocok. Perbaikannya dengan memastikan satu jalur keluar yang sama untuk semua indeks, biasanya dengan menata data lebih dahulu di __init__.

Seluruh data dimuat ke memori pada __init__. Pola list di atas memang nyaman untuk data kecil, tetapi bila dibawa mentah-mentah ke dataset besar, pola ini membuat program kehabisan memori sebelum pelatihan dimulai. Perbaikannya dengan menyimpan daftar penunjuk data, misalnya path file, lalu membaca isinya di __getitem__ setiap kali item diminta. Pola lazy ini dipraktikkan pada topik dataset kustom.

Ringkasan#

  • Dataset adalah kontrak dua method: __len__() mengembalikan jumlah item dan __getitem__(idx) mengembalikan satu item untuk satu indeks.
  • TensorDataset menyediakan implementasi bawaan untuk tensor yang sudah ada, dan item ke-i berisi baris ke-i dari setiap tensor penyusunnya.
  • Dataset buatan sendiri cukup mewarisi Dataset, menyiapkan data di __init__, dan mengembalikan tuple tensor yang seragam dari __getitem__.

Referensi#