Transformasi

Compose, ToTensor, dan normalisasi pada pipeline torchvision.

Daftar isi

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 (H,W,C)(H, W, C) dengan piksel 0 sampai 255 menjadi tensor float32 berbentuk (C,H,W)(C, H, W) 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.

python transformasi_dasar.py
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())
text Keluaran
Mode citra asli : L
Ukuran citra    : (28, 28)
Shape tensor    : (1, 28, 28)
dtype           : torch.float32
Rentang nilai   : 0.0 sampai 1.0

Keluaran menegaskan tiga perubahan sekaligus: tipe data berpindah dari citra PIL mode L ke torch.float32, dimensi berpindah dari ukuran (28, 28) ke shape (1,28,28)(1, 28, 28), 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 (x−μ)/σ (x - \mu) / \sigma , dengan μ \mu menyatakan mean dan σ \sigma 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.

python Contoh: rentang nilai sebelum dan sesudah Normalize
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())
text Keluaran
Tanpa normalisasi: 0.0 sampai 1.0
Dengan normalisasi: -1.0 sampai 1.0

Untuk 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.

python Contoh: Compose dengan Resize dan ToTensor
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))
text Keluaran
Ukuran awal : (64, 64)
Shape tensor: (3, 32, 32)

Keluaran memperlihatkan efek kedua langkah sekaligus: Resize memperkecil ukuran menjadi 32x32, kemudian ToTensor menghasilkan shape (3,32,32)(3, 32, 32) 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#

  • ToTensor mengubah citra PIL atau array NumPy (H,W,C)(H, W, C) menjadi tensor float32 (C,H,W)(C, H, W) dengan rentang 0 sampai 1, tanpa mengubah ukurannya.
  • Normalize menerapkan (x−μ)/σ (x - \mu) / \sigma per channel. Pasangan (0.5,), (0.5,) menghasilkan rentang -1 sampai 1, sedangkan angka ImageNet digunakan untuk model pretrained.
  • Compose merangkai transformasi menjadi satu pipeline berurutan yang diserahkan ke parameter transform milik dataset.

Referensi#