Dataset.map, Dataset.prefetch ve Dataset.shuffle'daki buffer_size'nin anlamı


101

TensorFlow belgelerine göre , sınıfın prefetchve mapyöntemlerinin tf.contrib.data.Datasether ikisinin de adında bir parametre vardır buffer_size.

İçin prefetchbir yöntem, bir parametre olarak bilinir buffer_sizeve belgelerine göre:

buffer_size: tf.int64 skaler tf.Tensor, ön getirme sırasında arabelleğe alınacak maksimum sayıda öğeyi temsil eder.

İçin mapbir yöntem, bir parametre olarak bilinir output_buffer_sizeve belgelerine göre:

output_buffer_size: (İsteğe bağlı.) Arabelleğe alınacak maksimum işlenmiş öğe sayısını temsil eden tf.int64 skaler tf.Tensor.

shuffleYöntem için benzer şekilde , aynı miktar görünür ve belgelere göre:

buffer_size: Yeni veri kümesinin örnekleyeceği bu veri kümesinden öğe sayısını temsil eden tf.int64 skalar tf.Tensor.

Bu parametreler arasındaki ilişki nedir?

DatasetAşağıdaki gibi bir nesne oluşturduğumu varsayalım :

 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)

bufferYukarıdaki kod parçacığındaki parametreler nasıl bir rol oynuyor ?


1
404 "dokümantasyon" bağlantısı bulunamadı.
Pradeep Singh

Yanıtlar:


151

TL; DR Benzer isimlerine rağmen, bu argümanların oldukça farklı anlamları vardır. buffer_sizeİçinde Dataset.shuffle()dolayısıyla da veri kümesi rastlantısallığını ve elemanlarının üretildiği sırası etkileyebilir. Giriş buffer_size, Dataset.prefetch()yalnızca bir sonraki öğeyi üretmek için gereken süreyi etkiler.


buffer_sizeİçinde argüman tf.data.Dataset.prefetch()ve output_buffer_sizeiçinde argüman tf.contrib.data.Dataset.map()ayarlamak için bir yol sağlar performansı hem argümanlar en fazla bir tampon oluşturmak için TensorFlow söyle: Giriş boru hattının buffer_sizeelemanları, ve bir arka plan iş parçacığı arka planda o tampon doldurmak için. (Bu kaldırdığımıza Not output_buffer_sizegelen argüman Dataset.map()o taşındığında tf.contrib.dataiçin tf.data. Yeni kod kullanmalıdır Dataset.prefetch()sonra map()aynı davranışı elde etmek.)

Bir önceden getirme arabelleği eklemek, verilerin ön işlemesini aşağı akış hesaplamasıyla örtüştürerek performansı artırabilir. Tipik olarak, boru hattının en sonuna küçük bir ön getirme tamponu (belki sadece tek bir elemanla) eklemek en yararlıdır, ancak daha karmaşık boru hatları, özellikle tek bir eleman üretme zamanı değişebildiği zaman, ilave ön getirmeden faydalanabilir.

Buna karşılık, buffer_sizeargüman dönüşümün rastgeleliğinitf.data.Dataset.shuffle() etkiler . Belleğe sığmayacak kadar büyük veri kümelerini işlemek için dönüşümü ( yerini aldığı işlev gibi) tasarladık . Tüm veri kümesini karıştırmak yerine, bir öğe arabelleği tutar ve bu arabellekten rastgele bir sonraki öğeyi seçer (varsa, onu sonraki giriş öğesiyle değiştirir). Değerinin değiştirilmesi, karıştırmanın ne kadar tek tip olduğunu etkiler: veri kümesindeki öğelerin sayısından büyükse, tek tip bir karıştırma elde edersiniz; ÖyleyseDataset.shuffle()tf.train.shuffle_batch()buffer_sizebuffer_sizebuffer_size1o zaman hiç karıştırmazsınız. Çok büyük veri kümeleri için, tipik bir "yeterince iyi" yaklaşım, eğitimden önce verileri rastgele birden çok dosyaya bölmek, ardından dosya adlarını tek tip olarak karıştırmak ve ardından daha küçük bir karıştırma arabelleği kullanmaktır. Bununla birlikte, uygun seçim, eğitim işinizin tam yapısına bağlı olacaktır.



Bu açıklama için hala bazı kafa karışıklıklarım var tf.data.Dataset.shuffle(). Karıştırma sürecini tam olarak bilmek istiyorum. Diyelim ki, ilk batch_sizeörnekler ilk buffer_sizeöğelerden rastgele seçilir ve bu böyle devam eder.
Bs He

1
@mrry IIUC dosya adlarını karıştırmak önemlidir, çünkü aksi takdirde her epoch aynı öğeyi gruplar 0 ... 999; ve 1000.1999 partiler halinde; vb, burada 1 dosya = 1000 grup olduğunu varsayıyorum. Dosya adlarının karıştırılmasında bile bazı rasgelelik yoktur: Bunun nedeni, #k dosyasındaki örneklerin her çağda birbirine yakın olmasıdır. #K dosyasının kendisi rastgele olduğundan bu çok kötü olmayabilir; yine de bazı durumlarda, bu bile eğitimi bozabilir. Mükemmel karıştırma elde etmenin tek yolu buffer_size, dosya boyutuna eşit olacak şekilde ayarlamaktır (ve tabii ki dosyaları karıştırmaktır).
en fazla

Tensorflow rc 15.0. İle dataset.shuffle(buffer_size=1)karıştırma devam eder. Düşüncesi olan var mı?
Sergey Bushmanov

@SergeyBushmanov, shuffle'ınızdan önceki dönüşüme bağlı olabilir, örneğin her çağın başında dosya adlarını varsayılan olarak karıştıran list_files ().
Xiaolong

130

Önemi buffer_sizeiçindeshuffle()

Ben vurgulamak @mrry önceki Yanıta yanıtlamak istedik önem arasında buffer_sizeyer tf.data.Dataset.shuffle().

Bir düşük seviyeye sahip olmak , bazı durumlarda buffer_sizesize sadece daha az karıştırmakla kalmaz : tüm eğitiminizi bozabilir.


Pratik bir örnek: kedi sınıflandırıcı

Örneğin, resimler üzerinde bir kedi sınıflandırıcısı eğittiğinizi ve verilerinizin aşağıdaki şekilde organize edildiğini ( 10000her kategorideki resimlerle) varsayalım :

train/
    cat/
        filename_00001.jpg
        filename_00002.jpg
        ...
    not_cat/
        filename_10001.jpg
        filename_10002.jpg
        ...

Verileri girmenin standart bir yolu tf.data, bir dosya adları listesine ve karşılık gelen etiketlerin bir listesine sahip olmak ve tf.data.Dataset.from_tensor_slices()veri kümesini oluşturmak için kullanmak olabilir:

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

Yukarıdaki kodla ilgili en büyük sorun , veri kümesinin aslında doğru şekilde karıştırılmamasıdır. Bir dönemin yaklaşık ilk yarısı için yalnızca kedi resimlerini ve ikinci yarısında yalnızca kedi olmayan resimleri göreceğiz. Bu eğitime çok zarar verecek.
Eğitimin başlangıcında, veri kümesi ilk 1000dosya adlarını alacak ve bunları arabelleğine koyacak, ardından bunlar arasından rastgele birini seçecektir. İlk 1000resimlerin tamamı kedi resimleri olduğu için, başlangıçta yalnızca kedi resimleri seçeceğiz.

Buradaki düzeltme buffer_size, daha büyük olduğundan emin olmak 20000veya önceden karıştırmak filenamesve labels(tabii ki aynı indekslerle).

Tüm dosya adlarını ve etiketleri bellekte saklamak bir sorun olmadığından, aslında buffer_size = len(filenames)her şeyin birlikte karıştırılacağından emin olmak için kullanabiliriz . tf.data.Dataset.shuffle()Ağır dönüşümleri uygulamadan önce çağırdığınızdan emin olun (görüntüleri okumak, işlemek, gruplamak gibi ...).

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

Paket servisi, karıştırmanın ne yapacağını her zaman iki kez kontrol etmektir. Bu hataları yakalamanın iyi bir yolu, partilerin zaman içindeki dağılımını çizmek olabilir (partilerin eğitim setiyle yaklaşık aynı dağıtımı içerdiğinden emin olun, örneğimizdeki yarı kedi ve yarısı kedi dışı).


1
Bir sonraki numune her zaman tampondan seçilir (burada 1000 boyutunda). Böylece ilk örnek, ilk 1000 dosya adından alınır. Arabellek 999 boyutuna düşer, bu nedenle sonraki girişi ( filename_01001) alır ve ekler. İkinci örnek bu 1000 dosya adından rastgele alınır (1001 ilk dosya adı eksi ilk örnek).
Olivier Moindrot

1
Bu düşük arabellek boyutuyla ilgili sorun, ilk gruplarınızda yalnızca kedilere sahip olmanızdır. Böylece model önemsiz bir şekilde sadece "kedi" yi tahmin etmeyi öğrenecek. Ağı eğitmenin en iyi yolu, aynı miktarda "kedi" ve "kedi olmayan" gruplara sahip olmaktır.
Olivier Moindrot

1
tf.summary.histogramEtiketlerin zaman içindeki dağılımını çizmek için kullanabilirsiniz .
Olivier Moindrot

3
Yazım hatası değil :) Veri kümesinde her sınıfın 10k görüntüsü vardır, bu nedenle toplam arabellek boyutu 20k'nin üzerinde olmalıdır. Ancak yukarıdaki örnekte, çok düşük olan 1k'lik bir tampon boyutu aldım.
Olivier Moindrot

1
Evet, arabellek boyutunu veri kümesi boyutuna ayarlamak genellikle iyidir. Veri kümesi boyutunun üzerindeki herhangi bir şey yine de işe yaramaz (ve karıştırmadan önce veri kümenizi tekrarlamadığınız sürece, arabellek veri kümesinden daha büyük olamaz).
Olivier Moindrot

7

Kod

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()

Çıktı

[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]


2
Bu, yineleyici tarafından sağlanan her öğe için, arabelleğin daha önce arabellekte olmayan ilgili bir sonraki veri kümesiyle doldurulduğunu gösterir.
Alex

2

Aslında @ olivier-moindrot'un cevabı doğru değil.

O bahsettikçe dosya adları ve etiketler oluşturarak ve karıştırma değerlerini yazdırarak doğrulayabilirsiniz.

Her karıştırma prosedürünün, veri kümesindeki tampon boyutuna eşit boyutta rastgele örnek oluşturacağını göreceksiniz.

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))

2

@ Olivier-moindrot'un gerçekten doğru olduğunu buldum, @Houtarou Oreki tarafından sağlanan kodu @max ile gösterilen değişiklikleri kullanarak denedim. Kullandığım kod şuydu:

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())

Kod çıktısı aslında 1 ile (buffer_size + (i * batch_size)) arasında değişen bir sayıydı, burada i , next_element'i kaç kez çalıştırdığınızdır . Sanırım çalışma şekli şudur. İlk olarak, BUFFER_SIZE numuneleri gelen sırayla toplanır fake_data . Daha sonra batch_size numuneleri tek tek tampondan alınır. Tampondan bir parti numunesi her alındığında, sahte_veriden sırayla alınan yenisiyle değiştirilir . Bu son şeyi aşağıdaki kodu kullanarak test ettim:

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)

Kod tarafından üretilen maksimum değer 109'du . Bu nedenle , eğitim sırasında tek tip bir örnekleme sağlamak için batch_size içinde dengeli bir örnek sağlamanız gerekir.

@Mrry'nin performans hakkında söylediklerini de test ettim, batch_size'nin bu miktarda örneği belleğe önceden getireceğini buldum . Bunu aşağıdaki kodu kullanarak test ettim:

dataset = dataset.shuffle(buffer_size=20)
dataset = dataset.prefetch(10)
dataset = dataset.batch(batch_size=5)

Değiştirme dataset.prefetch (10) miktarı, hafıza (RAM) içerisinde bir değişikliğe neden olmamıştır kullanılır. Verileriniz RAM'e sığmadığında bu önemlidir. Bence en iyi yol, verilerinizi / dosya_ adlarınızı tf.dataset'e beslemeden önce karıştırmak ve ardından buffer_size kullanarak arabellek boyutunu kontrol etmektir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.