Saya telah melalui contoh model bahasa LSTM ini di github (tautan) . Apa yang dilakukannya secara umum cukup jelas bagi saya. Tapi saya masih berjuang untuk memahami apa contiguous()
fungsi panggilan , yang terjadi beberapa kali dalam kode.
Misalnya pada baris 74/75 dari input kode dan urutan target LSTM dibuat. Data (disimpan dalam ids
) adalah 2 dimensi dimana dimensi pertama adalah ukuran tumpukan.
for i in range(0, ids.size(1) - seq_length, seq_length):
# Get batch inputs and targets
inputs = Variable(ids[:, i:i+seq_length])
targets = Variable(ids[:, (i+1):(i+1)+seq_length].contiguous())
Jadi sebagai contoh sederhana, saat menggunakan ukuran batch 1 dan seq_length
10 inputs
dan targets
terlihat seperti ini:
inputs Variable containing:
0 1 2 3 4 5 6 7 8 9
[torch.LongTensor of size 1x10]
targets Variable containing:
1 2 3 4 5 6 7 8 9 10
[torch.LongTensor of size 1x10]
Jadi secara umum pertanyaan saya adalah, apa contiguous()
dan mengapa saya membutuhkannya?
Lebih lanjut saya tidak mengerti mengapa metode ini dipanggil untuk urutan target dan tetapi bukan urutan input karena kedua variabel terdiri dari data yang sama.
Bagaimana bisa targets
tidak bersebelahan dan inputs
masih bersebelahan?
EDIT:
Saya mencoba untuk tidak menelepon contiguous()
, tetapi ini mengarah ke pesan kesalahan saat menghitung kerugian.
RuntimeError: invalid argument 1: input is not contiguous at .../src/torch/lib/TH/generic/THTensor.c:231
Jadi jelas memanggil contiguous()
dalam contoh ini diperlukan.
(Agar ini tetap dapat dibaca, saya menghindari memposting kode lengkap di sini, itu dapat ditemukan dengan menggunakan tautan GitHub di atas.)
Terima kasih sebelumnya!
tldr; to the point summary
dengan ringkasan yang ringkas.Jawaban:
Ada beberapa operasi pada Tensor di PyTorch yang tidak benar-benar mengubah konten tensor, tetapi hanya cara mengonversi indeks ke lokasi tensor ke byte. Operasi ini meliputi:
Misalnya: ketika Anda memanggil
transpose()
, PyTorch tidak menghasilkan tensor baru dengan tata letak baru, PyTorch hanya mengubah informasi meta di objek Tensor sehingga offset dan langkah untuk bentuk baru. Tensor yang ditransposisikan dan tensor asli memang berbagi memori!x = torch.randn(3,2) y = torch.transpose(x, 0, 1) x[0, 0] = 42 print(y[0,0]) # prints 42
Di sinilah konsep contiguous masuk. Di atas
x
bersebelahan tetapiy
bukan karena tata letak memorinya berbeda dari tensor berbentuk sama yang dibuat dari awal. Perhatikan bahwa kata "contiguous" agak menyesatkan karena bukan konten tensor yang tersebar di sekitar blok memori yang terputus. Di sini byte masih dialokasikan dalam satu blok memori tetapi urutan elemennya berbeda!Saat Anda memanggil
contiguous()
, itu sebenarnya membuat salinan tensor sehingga urutan elemen akan sama seperti jika tensor dengan bentuk yang sama dibuat dari awal.Biasanya Anda tidak perlu khawatir tentang ini. Jika PyTorch mengharapkan tensor yang berdekatan tetapi jika tidak, Anda akan mendapatkan
RuntimeError: input is not contiguous
dan menambahkan panggilan kecontiguous()
.sumber
contiguous()
sendiri?permute
, yang juga dapat mengembalikan tensor non- "bersebelahan".Dari [dokumentasi pytorch] [1]:
Dimana di
contiguous
sini berarti tidak hanya bersebelahan dalam memori, tetapi juga dalam urutan yang sama dalam memori dengan urutan indeks: misalnya melakukan transposisi tidak mengubah data dalam memori, itu hanya mengubah peta dari indeks ke penunjuk memori, jika Anda kemudian Menerapkannyacontiguous()
akan mengubah data dalam memori sehingga peta dari indeks ke lokasi memori adalah peta kanonik. [1]: http://pytorch.org/docs/master/tensors.htmlsumber
tensor.contiguous () akan membuat salinan tensor, dan elemen dalam salinan akan disimpan dalam memori dengan cara yang berdekatan. Fungsi contiguous () biasanya diperlukan ketika kita pertama kali mentransposisi () tensor dan kemudian membentuk kembali (view). Pertama, mari buat tensor bersebelahan:
The stride () return (3,1) berarti bahwa: saat bergerak di sepanjang dimensi pertama dengan setiap langkah (baris demi baris), kita perlu memindahkan 3 langkah dalam memori. Saat bergerak sepanjang dimensi kedua (kolom demi kolom), kita perlu memindahkan 1 langkah dalam memori. Ini menunjukkan bahwa elemen dalam tensor disimpan secara berdekatan.
Sekarang kami mencoba menerapkan fungsi come ke tensor:
Ok, kita bisa menemukan bahwa transpose (), narrow () dan tensor slicing, serta expand () akan membuat tensor yang dihasilkan tidak bersebelahan. Menariknya, repeat () dan view () tidak membuatnya rancu. Jadi sekarang pertanyaannya adalah: apa yang terjadi jika saya menggunakan tensor yang tidak bersebelahan?
Jawabannya adalah fungsi view () tidak dapat diterapkan ke tensor yang tidak bersebelahan. Ini mungkin karena view () mengharuskan tensor disimpan secara berdekatan sehingga dapat melakukan pembentukan ulang cepat dalam memori. misalnya:
kami akan mendapatkan kesalahan:
Untuk mengatasi ini, cukup tambahkan contiguous () ke tensor tidak bersebelahan, untuk membuat salinan berdekatan lalu terapkan view ()
sumber
Seperti dalam jawaban sebelumnya contigous () mengalokasikan potongan memori contigous , akan sangat membantu ketika kita meneruskan tensor ke kode backend c atau c ++ di mana tensor diteruskan sebagai pointer
sumber
Jawaban yang diterima sangat bagus, dan saya mencoba menipu
transpose()
efek fungsi. Saya membuat dua fungsi yang dapat memeriksasamestorage()
dancontiguous
.def samestorage(x,y): if x.storage().data_ptr()==y.storage().data_ptr(): print("same storage") else: print("different storage") def contiguous(y): if True==y.is_contiguous(): print("contiguous") else: print("non contiguous")
Saya memeriksa dan mendapatkan hasil ini sebagai tabel:
Anda dapat meninjau kode pemeriksa di bawah, tetapi mari kita berikan satu contoh ketika tensor tidak bersebelahan . Kita tidak bisa begitu saja memanggil
view()
tensor itu, kita akan membutuhkannyareshape()
atau kita juga bisa memanggilnya.contiguous().view()
.Selanjutnya yang perlu diperhatikan ada metode yang membuat tensor bersebelahan dan tidak bersebelahan pada akhirnya. Ada metode yang dapat beroperasi pada penyimpanan yang sama , dan beberapa metode seperti
flip()
itu akan membuat penyimpanan baru (baca: kloning tensor) sebelum dikembalikan.Kode pemeriksa:
sumber
Dari apa yang saya pahami, ini jawaban yang lebih ringkas:
Menurut pendapat saya, kata bersebelahan adalah istilah yang membingungkan / menyesatkan karena dalam konteks normal itu berarti ketika memori tidak tersebar di blok yang terputus (yaitu "bersebelahan / terhubung / kontinu").
Beberapa operasi mungkin memerlukan properti bersebelahan ini karena beberapa alasan (kemungkinan besar efisiensi dalam GPU, dll).
Perhatikan bahwa
.view
operasi lain yang mungkin menyebabkan masalah ini. Lihatlah kode berikut yang saya perbaiki hanya dengan memanggil bersebelahan (bukan masalah transpose khas yang menyebabkannya di sini adalah contoh yang menyebabkan ketika RNN tidak senang dengan inputnya):Kesalahan yang biasa saya dapatkan:
Sumber / Sumber:
sumber