Dataset citra jarang digunakan dalam bentuk mentahnya: piksel biasanya dikonversi menjadi tensor dan diskalakan sebelum masuk model. Topik ini membahas modul torchvision.transforms: mengubah citra menjadi tensor dengan ToTensor, menormalkan rentang nilainya dengan Normalize, dan merangkai keduanya dengan Compose. Setelah topik ini dipelajari, pipeline (rantai pemrosesan) citra dapat dirangkai menjadi satu objek yang diserahkan kepada dataset.
ToTensor: dari citra ke tensor#
ToTensor mengubah citra PIL atau array NumPy berbentuk dengan piksel 0 sampai 255 menjadi tensor float32 berbentuk dengan nilai 0 sampai 1. Urutan dimensi diputar karena konvolusi pada modul CNN mengharapkan channel lebih dahulu, sedangkan penskalaan ke 0 sampai 1 menjaga nilai masukan dalam rentang yang seragam untuk semua dataset. Contoh berikut menggunakan citra yang digambar di memori dengan Pillow, sehingga tidak ada unduhan sama sekali.
import torch
from PIL import Image, ImageDraw
from torchvision import transforms
# citra keabuan 28x28 dibuat di memori, tanpa unduhan apa pun
citra = Image.new("L", (28, 28), color=0)
gambar = ImageDraw.Draw(citra)
gambar.rectangle([6, 6, 21, 21], fill=255)
ke_tensor = transforms.ToTensor()
tensor_citra = ke_tensor(citra)
print("Mode citra asli :", citra.mode)
print("Ukuran citra :", citra.size)
print("Shape tensor :", tuple(tensor_citra.shape))
print("dtype :", tensor_citra.dtype)
print("Rentang nilai :", tensor_citra.min().item(), "sampai", tensor_citra.max().item())Mode citra asli : L
Ukuran citra : (28, 28)
Shape tensor : (1, 28, 28)
dtype : torch.float32
Rentang nilai : 0.0 sampai 1.0Keluaran menegaskan tiga perubahan sekaligus: tipe data berpindah dari citra PIL mode L ke torch.float32, dimensi berpindah dari ukuran (28, 28) ke shape , dan rentang nilai menyempit dari 0 sampai 255 menjadi 0 sampai 1. Perlu diingat bahwa transformasi dipanggil pada objeknya, yaitu ke_tensor(citra), bukan pada kelasnya.
Normalisasi dengan Normalize#
Normalize menggeser dan menskalakan setiap channel dengan rumus , dengan menyatakan mean dan menyatakan simpangan baku per channel. Untuk citra keabuan, pasangan (0.5,), (0.5,) digunakan agar rumusnya sederhana: piksel hitam bernilai 0 menjadi -1 dan piksel putih bernilai 1 tetap bernilai 1, sehingga seluruh rentang bergeser menjadi -1 sampai 1. Contoh berikut menghitung rentang sebelum dan sesudah normalisasi pada citra yang sama.
import torch
from PIL import Image, ImageDraw
from torchvision import transforms
citra = Image.new("L", (28, 28), color=0)
ImageDraw.Draw(citra).rectangle([6, 6, 21, 21], fill=255)
tanpa_normalisasi = transforms.ToTensor()(citra)
dengan_normalisasi = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,)),
])(citra)
print("Tanpa normalisasi:", tanpa_normalisasi.min().item(), "sampai", tanpa_normalisasi.max().item())
print("Dengan normalisasi:", dengan_normalisasi.min().item(), "sampai", dengan_normalisasi.max().item())Tanpa normalisasi: 0.0 sampai 1.0
Dengan normalisasi: -1.0 sampai 1.0Untuk citra berwarna, angka yang lazim berasal dari statistik dataset ImageNet, yaitu mean [0.485, 0.456, 0.406] dan simpangan baku [0.229, 0.224, 0.225] per channel RGB. Model pretrained torchvision yang dibahas pada modul Transfer Learning dilatih dengan normalisasi tersebut, sehingga inferensinya menuntut normalisasi yang sama persis.
Merangkai dengan Compose#
Compose merangkai beberapa transformasi menjadi satu objek yang dijalankan berurutan, sehingga dataset cukup menyimpan satu pipeline alih-alih beberapa transformasi terpisah. Urutannya penting: transformasi yang menuntut citra PIL dipasang lebih dahulu, kemudian ToTensor, dan Normalize selalu ditempatkan di belakang karena menuntut tensor. Contoh berikut memperkecil citra berwarna 64x64 menjadi 32x32 lalu mengubahnya menjadi tensor.
from PIL import Image, ImageDraw
from torchvision import transforms
citra = Image.new("RGB", (64, 64), color=(20, 60, 120))
ImageDraw.Draw(citra).ellipse([16, 16, 48, 48], fill=(240, 240, 240))
pipeline = transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor(),
])
tensor_citra = pipeline(citra)
print("Ukuran awal :", citra.size)
print("Shape tensor:", tuple(tensor_citra.shape))Ukuran awal : (64, 64)
Shape tensor: (3, 32, 32)Keluaran memperlihatkan efek kedua langkah sekaligus: Resize memperkecil ukuran menjadi 32x32, kemudian ToTensor menghasilkan shape untuk citra tiga channel. Bila urutan dirangkai dengan asal, error muncul pada langkah yang menuntut tipe data yang belum siap, atau nilai keliru dihasilkan tanpa pesan apa pun.
Diserahkan ke parameter transform#
Dataset torchvision menerima parameter transform pada konstruktornya, dan setiap item yang diambil __getitem__ melewati pipeline itu sebelum dikembalikan. Dengan cara ini, transformasi tidak perlu dipanggil secara manual di luar dataset. Pola yang sama sudah digunakan pada topik dataset bawaan, misalnya datasets.MNIST(root='./data', train=True, download=True, transform=transforms.ToTensor()), yang dapat dirangkai menjadi transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) saat normalisasi juga diinginkan.
Transformasi tambahan seperti pembalikan horizontal dan pemotongan acak dipasang dengan cara yang persis sama. Transformasi semacam itu mengubah isi citra, sehingga hanya dipasang pada data pelatihan dan tidak pada data validasi atau uji. Penerapan lengkapnya sebagai data augmentation dibahas pada modul CNN.
Kesalahan umum#
Normalize dipasang sebelum ToTensor. Normalize menuntut tensor, sedangkan item dataset masih berupa citra PIL, sehingga pemanggilannya melempar TypeError karena masukannya bukan tensor. Perbaikannya dengan selalu menempatkan ToTensor sebelum Normalize di dalam Compose.
Normalisasi berbeda antara pelatihan dan inferensi. Model yang dilatih dengan data ternormalisasi ImageNet tetapi disajikan data mentah saat inferensi akan mengalami penurunan performa tanpa pesan error apa pun. Perbaikannya dengan menyimpan nilai mean dan std sebagai bagian dari konfigurasi eksperimen dan menggunakan satu pipeline yang sama di kedua tempat.
Menganggap ToTensor mengubah ukuran. Ukuran citra tetap sama setelah ToTensor, dan shape yang lebih kecil hanya terjadi bila Resize dipasang sebelumnya. Perbaikannya dengan memeriksa shape satu item dataset sebelum pelatihan dimulai, kebiasaan yang juga menangkap kesalahan pipeline lain.
Ringkasan#
ToTensormengubah citra PIL atau array NumPy menjadi tensor float32 dengan rentang 0 sampai 1, tanpa mengubah ukurannya.Normalizemenerapkan per channel. Pasangan(0.5,), (0.5,)menghasilkan rentang -1 sampai 1, sedangkan angka ImageNet digunakan untuk model pretrained.Composemerangkai transformasi menjadi satu pipeline berurutan yang diserahkan ke parametertransformmilik dataset.
Referensi#
- torch.utils.data — PyTorch Docs
— dokumentasi pustaka data yang menerima transformasi melalui parameter
transform. - Learn the Basics — PyTorch Tutorials
— seri tutorial dasar resmi yang mendemonstrasikan
ToTensordanNormalizepada dataset torchvision.