Data yang masuk ke sebuah operasi jarang memiliki bentuk yang persis dituntut, sehingga pengubahan bentuk menjadi keterampilan harian di PyTorch. Topik ini membahas lima operasinya, yaitu view dan reshape untuk menata ulang elemen, squeeze dan unsqueeze untuk menghapus serta menambah dimensi berukuran satu, dan permute untuk menukar urutan dimensi. Setiap contoh kembali menggunakan anotasi bentuk dengan gaya # (6,) -> (2, 3) yang menyatakan bentuk sebelum dan sesudah operasi.
view dan reshape#
Metode .view() menyusun ulang elemen tensor ke dalam bentuk baru tanpa menyalin data, dengan syarat jumlah elemennya tetap sama. Tensor (6,) dapat ditata menjadi (2, 3) maupun (3, 2) karena keduanya menampung enam elemen. Metode .reshape() melakukan hal yang sama, tetapi lebih toleran: bila penataan memungkinkan tanpa salinan, hasilnya berupa view, sedangkan bila tidak, salinan baru dibuat secara diam-diam. Contoh berikut disimpan sebagai file ubah_bentuk.py.
import torch
x = torch.arange(6) # (6,)
y = x.view(2, 3) # (6,) -> (2, 3)
print(y)
z = x.reshape(3, 2) # (6,) -> (3, 2)
print(z)
y[0, 0] = 99 # (2, 3), perubahan pada view
print(x) # (6,), x ikut berubah: memori yang samatensor([[0, 1, 2],
[3, 4, 5]])
tensor([[0, 1],
[2, 3],
[4, 5]])
tensor([99, 1, 2, 3, 4, 5])Keluaran baris ketiga memperlihatkan konsekuensi view: mengubah y juga mengubah x, karena view menunjuk memori yang sama dengan tensor asalnya. Sifat ini berlaku untuk view dan untuk reshape pada kasus yang tidak menyalin. Aturan praktisnya, reshape digunakan sebagai pilihan bawaan karena selalu berhasil, sedangkan view dipilih saat kemampuan membaca kode lebih diutamakan, karena kegagalannya berarti memang ada penataan yang tidak dapat dilakukan tanpa salinan.
view membutuhkan memori kontigu#
Memori tensor dikatakan kontigu (contiguous), yakni berurutan rapi, bila elemen-elemennya tersusun berurutan tanpa lompatan sesuai urutan dimensinya. Operasi seperti permute tidak memindahkan data sama sekali, melainkan hanya mengubah cara PyTorch melangkahi memori, sehingga hasilnya sering kali tidak kontigu. Pada tensor yang tidak kontigu, view menolak bekerja karena penataan barunya tidak dapat dipetakan ke memori tanpa salinan, sedangkan reshape menangani kasus tersebut dengan membuat salinan.
import torch
a = torch.arange(6).view(2, 3) # (2, 3)
b = a.permute(1, 0) # (2, 3) -> (3, 2), memori tidak kontigu
try:
b.view(6) # (3, 2) -> (6,), memicu error
except RuntimeError as e:
print("RuntimeError:", e)
print(b.reshape(6)) # (3, 2) -> (6,), reshape menyalin bila perluRuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
tensor([0, 3, 1, 4, 2, 5])Pesan error tersebut bahkan menyarankan solusinya, yaitu menggunakan reshape. Perhatikan pula isi hasil reshape: barisannya 0, 3, 1, 4, 2, 5 mengikuti susunan memori tensor hasil permute, bukan 0, 1, 2, 3, 4, 5. Pengetahuan ini penting saat membaca error pada kode model, karena error view semacam ini paling sering muncul setelah permute atau transpose tanpa .contiguous().
squeeze dan unsqueeze#
Dimensi berukuran satu, misalnya dimensi kedua pada bentuk (2, 1, 3), tidak menambah isi data tetapi mengubah tata letaknya, dan operasi tertentu menuntut kehadiran atau ketiadaannya. Metode .squeeze() menghapus seluruh dimensi berukuran satu, sedangkan .squeeze(i) hanya menghapus dimensi ke-i bila ukurannya memang satu. Kebalikannya, .unsqueeze(i) menyisipkan dimensi berukuran satu pada posisi i, dan indeks -1 berarti posisi paling akhir.
import torch
x = torch.zeros(2, 1, 3) # (2, 1, 3)
print(x.squeeze().shape) # (2, 1, 3) -> (2, 3), seluruh dimensi 1 dihapus
print(x.squeeze(1).shape) # (2, 1, 3) -> (2, 3), hanya dimensi 1
print(x.squeeze(0).shape) # (2, 1, 3) -> (2, 1, 3), dimensi 0 berukuran 2
y = torch.zeros(2, 3) # (2, 3)
print(y.unsqueeze(0).shape) # (2, 3) -> (1, 2, 3), dimensi batch baru
print(y.unsqueeze(-1).shape) # (2, 3) -> (2, 3, 1), dimensi terakhir barutorch.Size([2, 3])
torch.Size([2, 3])
torch.Size([2, 1, 3])
torch.Size([1, 2, 3])
torch.Size([2, 3, 1])Panggilan squeeze(0) tidak mengubah apa pun karena dimensi 0 berukuran dua, dan PyTorch membiarkannya tanpa error. Pasangan ini paling sering digunakan di sekitar dimensi batch: tensor tunggal (2, 3) dinaikkan menjadi (1, 2, 3) dengan unsqueeze(0) agar dapat masuk ke layer yang menuntut dimensi batch, lalu hasil (1, 2, 3) diturunkan kembali menjadi (2, 3) dengan squeeze(0) saat keluar.
permute#
Metode .permute() menukar urutan dimensi dengan menerima urutan baru dari indeks dimensi lama, tanpa menyalin data. Untuk tensor (2, 3, 4), pemanggilan permute(2, 0, 1) menghasilkan bentuk (4, 2, 3) karena dimensi lama ke-2 menjadi yang pertama. Pada tensor dua dimensi, properti .T adalah singkatan penukarannya, yaitu transpose matriks.
import torch
torch.manual_seed(0)
x = torch.rand(2, 3, 4) # (2, 3, 4)
y = x.permute(2, 0, 1) # (2, 3, 4) -> (4, 2, 3)
print(y.shape)
a = torch.rand(3, 4) # (3, 4)
b = a.T # (3, 4) -> (4, 3), transpose dua dimensi
print(b.shape)
print(b.is_contiguous())torch.Size([4, 2, 3])
torch.Size([4, 3])
FalseNilai False pada baris terakhir menegaskan bahwa hasil permute tidak kontigu, sebagaimana dibahas pada bagian sebelumnya. Persoalan klasik yang diselesaikan permute adalah perbedaan konvensi susunan data citra: PyTorch menuntut (C, H, W) untuk satu citra, yaitu kanal, tinggi, lalu lebar, sedangkan banyak pustaka pengolah citra menyusun (H, W, C). Perpindahan antara konvensi itu dilakukan dengan satu permute, tanpa satu byte data pun berpindah.
Ringkasan#
viewmenata ulang elemen tanpa salinan dan berbagi memori dengan tensor asal, sedangkanreshapemelakukan hal yang sama dengan izin menyalin bila penataan menuntutnya.viewmenolak tensor yang tidak kontigu dengan RuntimeError, dan hasilpermuteatautransposeumumnya tidak kontigu.squeezemenghapus dimensi berukuran satu, sedangkanunsqueezemenyisipkannya, dan keduanya digunakan di sekitar dimensi batch.permutemenukar urutan dimensi tanpa memindahkan data, dengan.Tsebagai singkatan transpose pada tensor dua dimensi.
Referensi#
- PyTorch documentation — PyTorch
— Rujukan API untuk
view,reshape,squeeze,unsqueeze,permute, dantorch.Tensor.contiguous. - Learn the Basics — PyTorch — Tutorial resmi yang bagian Tensors memperkenalkan pengubahan bentuk dan dimensi tensor.