Sesuai dokumentasi TensorFlow , metode prefetch
dan kelas, keduanya memiliki parameter yang dipanggil .map
tf.contrib.data.Dataset
buffer_size
Untuk prefetch
metode, parameternya dikenal sebagai buffer_size
dan menurut dokumentasi:
buffer_size: Skalar tf.int64 tf.Tensor, mewakili jumlah elemen maksimum yang akan di-buffer ketika melakukan prefetching.
Untuk map
metode ini, parameternya dikenal sebagai output_buffer_size
dan menurut dokumentasi:
output_buffer_size: (Opsional.) Skalar tf.int64 tf.Tensor, mewakili jumlah maksimum elemen yang diproses yang akan di-buffer.
Demikian pula untuk shuffle
metode, kuantitas yang sama muncul dan menurut dokumentasi:
buffer_size: Skalar tf.int64 tf.Tensor, yang mewakili jumlah elemen dari kumpulan data ini yang akan dijadikan sampel untuk kumpulan data baru.
Apa hubungan antara parameter ini?
Misalkan saya membuat Dataset
objek sebagai berikut:
tr_data = TFRecordDataset(trainfilenames)
tr_data = tr_data.map(providefortraining, output_buffer_size=10 * trainbatchsize, num_parallel_calls\
=5)
tr_data = tr_data.shuffle(buffer_size= 100 * trainbatchsize)
tr_data = tr_data.prefetch(buffer_size = 10 * trainbatchsize)
tr_data = tr_data.batch(trainbatchsize)
Peran apa yang dimainkan oleh buffer
parameter dalam cuplikan di atas?
sumber
Jawaban:
TL; DR Meskipun namanya mirip, argumen ini memiliki arti yang cukup berbeda. The
buffer_size
diDataset.shuffle()
dapat mempengaruhi keacakan dataset Anda, dan karenanya urutan elemen diproduksi. Thebuffer_size
dalamDataset.prefetch()
hanya mempengaruhi waktu yang dibutuhkan untuk menghasilkan elemen berikutnya.The
buffer_size
argumen dalamtf.data.Dataset.prefetch()
danoutput_buffer_size
argumen dalamtf.contrib.data.Dataset.map()
memberikan cara untuk menyesuaikan kinerja dari pipa masukan Anda: kedua argumen memberitahu TensorFlow untuk membuat penyangga palingbuffer_size
unsur, dan latar belakang thread untuk mengisi buffer di latar belakang. (Perhatikan bahwa kami menghapusoutput_buffer_size
argumen dariDataset.map()
saat dipindahkan daritf.contrib.data
ketf.data
. Kode baru harus digunakanDataset.prefetch()
setelahmap()
untuk mendapatkan perilaku yang sama.)Menambahkan buffer prefetch dapat meningkatkan kinerja dengan tumpang tindih preprocessing data dengan komputasi hilir. Biasanya akan sangat berguna untuk menambahkan buffer prefetch kecil (mungkin hanya dengan satu elemen) di bagian paling akhir dari pipeline, tetapi pipeline yang lebih kompleks dapat memanfaatkan prapengambilan tambahan, terutama ketika waktu untuk menghasilkan satu elemen dapat bervariasi.
Sebaliknya,
buffer_size
argumen untuktf.data.Dataset.shuffle()
mempengaruhi keacakan transformasi. Kami merancangDataset.shuffle()
transformasi (sepertitf.train.shuffle_batch()
fungsi yang digantikannya) untuk menangani kumpulan data yang terlalu besar untuk muat dalam memori. Alih-alih mengacak seluruh kumpulan data, ia mempertahankan bufferbuffer_size
elemen, dan secara acak memilih elemen berikutnya dari buffer itu (menggantinya dengan elemen input berikutnya, jika tersedia). Mengubah nilaibuffer_size
mempengaruhi seberapa seragam pengacakan: jikabuffer_size
lebih besar dari jumlah elemen dalam kumpulan data, Anda mendapatkan pengocokan seragam; jika memang1
maka Anda tidak perlu menyeret sama sekali. Untuk kumpulan data yang sangat besar, pendekatan "cukup baik" yang umum adalah memecah data secara acak menjadi beberapa file satu kali sebelum pelatihan, lalu mengacak nama file secara seragam, lalu menggunakan buffer acak yang lebih kecil. Namun, pilihan yang tepat akan bergantung pada sifat pekerjaan pelatihan Anda.sumber
tf.data.Dataset.shuffle()
. Saya ingin tahu proses pengocokan yang tepat. Misalnya,batch_size
sampel pertama dipilih secara acak daribuffer_size
elemen pertama , dan seterusnya.buffer_size
ukuran file yang sama (dan mengacak file tentunya).dataset.shuffle(buffer_size=1)
pengocokan masih terjadi. Ada pemikiran?Pentingnya
buffer_size
dalamshuffle()
Saya ingin menindaklanjuti jawaban sebelumnya dari @mrry untuk menekankan pentingnya dari
buffer_size
dalamtf.data.Dataset.shuffle()
.Memiliki nada rendah
buffer_size
tidak hanya akan membuat Anda kalah dalam beberapa kasus: hal itu dapat mengacaukan seluruh latihan Anda.Contoh praktis: pengklasifikasi kucing
Misalkan Anda melatih pengklasifikasi kucing pada gambar, dan data Anda diatur dengan cara berikut (dengan
10000
gambar di setiap kategori):Cara standar untuk memasukkan data dengan
tf.data
can adalah memiliki daftar nama file dan daftar label yang sesuai, dan gunakantf.data.Dataset.from_tensor_slices()
untuk membuat dataset:filenames = ["filename_00001.jpg", "filename_00002.jpg", ..., "filename_10001.jpg", "filename_10002.jpg", ...] labels = [1, 1, ..., 0, 0...] # 1 for cat, 0 for not_cat dataset = tf.data.Dataset.from_tensor_slices((filenames, labels)) dataset = dataset.shuffle(buffer_size=1000) # 1000 should be enough right? dataset = dataset.map(...) # transform to images, preprocess, repeat, batch...
Masalah besar dengan kode di atas adalah bahwa dataset sebenarnya tidak akan dikocok dengan cara yang benar. Untuk sekitar paruh pertama zaman, kita hanya akan melihat gambar kucing, dan untuk paruh kedua hanya gambar bukan kucing. Ini akan sangat melukai latihan.
Pada awal pelatihan, kumpulan data akan mengambil
1000
nama file pertama dan meletakkannya di buffernya, lalu memilih satu di antaranya secara acak. Karena semua1000
gambar pertama adalah gambar kucing, kami hanya akan memilih gambar kucing di awal.Perbaikan di sini adalah untuk memastikan bahwa
buffer_size
lebih besar dari20000
, atau untuk mengocok terlebih dahulufilenames
danlabels
(dengan indeks yang sama jelas).Karena menyimpan semua nama file dan label dalam memori bukanlah masalah, kami sebenarnya dapat menggunakan
buffer_size = len(filenames)
untuk memastikan bahwa semuanya akan dikocok bersama. Pastikan untuk memanggiltf.data.Dataset.shuffle()
sebelum menerapkan transformasi berat (seperti membaca gambar, memprosesnya, menumpuk ...).dataset = tf.data.Dataset.from_tensor_slices((filenames, labels)) dataset = dataset.shuffle(buffer_size=len(filenames)) dataset = dataset.map(...) # transform to images, preprocess, repeat, batch...
Kesimpulannya adalah selalu memeriksa ulang apa yang akan dilakukan pengacakan. Cara yang baik untuk menangkap kesalahan ini mungkin dengan memplot distribusi batch dari waktu ke waktu (pastikan bahwa batch berisi distribusi yang kira-kira sama dengan set pelatihan, setengah kucing dan setengah bukan kucing dalam contoh kita).
sumber
filename_01001
) dan menambahkannya. Sampel kedua diambil secara acak dari 1000 nama file ini (1001 nama file pertama dikurangi sampel pertama).tf.summary.histogram
untuk merencanakan distribusi label dari waktu ke waktu.Kode
import tensorflow as tf def shuffle(): ds = list(range(0,1000)) dataset = tf.data.Dataset.from_tensor_slices(ds) dataset=dataset.shuffle(buffer_size=500) dataset = dataset.batch(batch_size=1) iterator = dataset.make_initializable_iterator() next_element=iterator.get_next() init_op = iterator.initializer with tf.Session() as sess: sess.run(init_op) for i in range(100): print(sess.run(next_element), end='') shuffle()
Keluaran
[298] [326] [2] [351] [92] [398] [72] [134] [404] [378] [238] [131] [369] [324] [35] [182] [441 ] [370] [372] [144] [77] [11] [199] [65] [346] [418] [493] [343] [444] [470] [222] [83] [61] [ 81] [366] [49] [295] [399] [177] [507] [288] [524] [401] [386] [89] [371] [181] [489] [172] [159] [195] [232] [160] [352] [495] [241] [435] [127] [268] [429] [382] [479] [519] [116] [395] [165] [233 ] [37] [486] [553] [111] [525] [170] [571] [215] [530] [47] [291] [558] [21] [245] [514] [103] [ 45] [545] [219] [468] [338] [392] [54] [139] [339] [448] [471] [589] [321] [223] [311] [234] [314]
sumber
Sebenarnya jawaban @ olivier-moindrot salah.
Anda dapat memverifikasinya dengan membuat nama file dan label saat dia menyebutkan dan mencetak nilai acak.
Anda akan melihat setiap prosedur shuffle akan menghasilkan sampel secara acak dengan ukuran sama dengan ukuran buffer dari dataset.
dataset = dataset.shuffle(buffer_size=1000) iterator = dataset.make_one_shot_iterator() next_element = iterator.get_next() with tf.Session() as sess: for i in range(1000): print(sess.run(next_element))
sumber
Saya menemukan bahwa @ olivier-moindrot memang benar, saya mencoba kode yang disediakan oleh @Houtarou Oreki, menggunakan modifikasi yang ditunjukkan oleh @max. Kode yang saya gunakan adalah sebagai berikut:
fake_data = np.concatenate((np.arange(1,500,1),np.zeros(500))) dataset = tf.data.Dataset.from_tensor_slices(fake_data) dataset=dataset.shuffle(buffer_size=100) dataset = dataset.batch(batch_size=10) iterator = dataset.make_initializable_iterator() next_element=iterator.get_next() init_op = iterator.initializer with tf.Session() as sess: sess.run(init_op) for i in range(50): print(i) salida = np.array(sess.run(next_element)) print(salida) print(salida.max())
Output kode memang berupa angka mulai dari 1 hingga (buffer_size + (i * batch_size)), di mana i adalah berapa kali Anda menjalankan next_element . Saya pikir cara kerjanya adalah sebagai berikut. Pertama, sampel buffer_size dipilih secara berurutan dari fake_data . Kemudian satu per satu sampel batch_size diambil dari buffer. Setiap kali sampel batch diambil dari buffer, sampel tersebut akan diganti dengan yang baru, diambil secara berurutan dari fake_data . Saya menguji hal terakhir ini menggunakan kode berikut:
aux = 0 for j in range (10000): with tf.Session() as sess: sess.run(init_op) salida = np.array(sess.run(next_element)) if salida.max() > aux: aux = salida.max() print(aux)
Nilai maksimum yang dihasilkan oleh kode adalah 109. Jadi, Anda perlu memastikan sampel yang seimbang dalam batch_size Anda untuk memastikan pengambilan sampel yang seragam selama pelatihan.
Saya juga menguji apa yang dikatakan @mrry tentang kinerja, saya menemukan bahwa batch_size akan mengambil sampel sebanyak itu ke dalam memori. Saya menguji ini menggunakan kode berikut:
dataset = dataset.shuffle(buffer_size=20) dataset = dataset.prefetch(10) dataset = dataset.batch(batch_size=5)
Mengubah jumlah dataset.prefetch (10) mengakibatkan tidak ada perubahan pada memori (RAM) yang digunakan. Ini penting ketika data Anda tidak cocok dengan RAM. Saya pikir cara terbaik adalah mengocok data / nama_file Anda sebelum memberi mereka makan ke tf.dataset, dan kemudian mengontrol ukuran buffer menggunakan buffer_size .
sumber