Genel Bakış
Bu kılavuz, TensorFlow Profiler ve tf.data
aşina olduğunuzu varsaymaktadır. Kullanıcıların giriş hattı performans sorunlarını teşhis etmesine ve düzeltmesine yardımcı olacak örneklerle adım adım talimatlar sağlamayı amaçlamaktadır.
Başlamak için TensorFlow işinizin bir profilini toplayın. Bunun nasıl yapılacağına ilişkin talimatlar CPU'lar/GPU'lar ve Bulut TPU'lar için mevcuttur.
Aşağıda ayrıntıları verilen analiz iş akışı, Profiler'daki izleme görüntüleme aracına odaklanır. Bu araç, TensorFlow programınız tarafından yürütülen operasyonların süresini gösteren bir zaman çizelgesi görüntüler ve hangi operasyonların yürütülmesinin en uzun sürdüğünü belirlemenize olanak tanır. İzleme görüntüleyici hakkında daha fazla bilgi için TF Profiler kılavuzunun bu bölümüne bakın. Genel olarak, tf.data
olayları ana bilgisayarın CPU zaman çizelgesinde görünecektir.
Analiz İş Akışı
Lütfen aşağıdaki iş akışını takip edin. Geliştirmemize yardımcı olacak geri bildirimleriniz varsa lütfen "comp:data" etiketiyle bir github sorunu oluşturun .
1. tf.data
hattınız yeterince hızlı veri üretiyor mu?
Giriş hattının TensorFlow programınız için bir darboğaz olup olmadığını tespit ederek başlayın.
Bunu yapmak için izleme görüntüleyicide IteratorGetNext::DoCompute
ops'u arayın. Genel olarak bunları bir adımın başlangıcında görmeyi beklersiniz. Bu dilimler, giriş hattınızın istendiğinde bir grup öğe üretmesi için gereken süreyi temsil eder. Bir tf.function
dosyasında keras kullanıyorsanız veya veri kümeniz üzerinde yineleme yapıyorsanız, bunlar tf_data_iterator_get_next
iş parçacıklarında bulunmalıdır.
Bir dağıtım stratejisi kullanıyorsanız IteratorGetNext:: IteratorGetNext::DoCompute
(TF 2.3'ten itibaren) yerine IteratorGetNextAsOptional::DoCompute
olaylarını görebileceğinizi unutmayın.
Aramalar hızlı bir şekilde geri dönüyorsa (<= 50 kişi), bu, verilerinizin istendiğinde kullanılabilir olduğu anlamına gelir. Giriş boru hattı sizin darboğazınız değildir; Daha genel performans analizi ipuçları için Profiler kılavuzuna bakın.
Çağrılar yavaş geri dönerse tf.data
tüketicinin isteklerine yetişemez. Bir sonraki bölüme geçin.
2. Verileri önceden mi getiriyorsunuz?
Giriş hattı performansına yönelik en iyi uygulama, tf.data
ardışık düzeninizin sonuna bir tf.data.Dataset.prefetch
dönüşümü eklemektir. Bu dönüşüm, giriş hattının ön işleme hesaplamasını model hesaplamanın bir sonraki adımıyla örtüştürür ve modelinizi eğitirken optimum giriş hattı performansı için gereklidir. Verileri önceden getiriyorsanız, IteratorGetNext::DoCompute
işlemiyle aynı iş parçacığında bir Iterator::Prefetch
dilimi görmelisiniz.
İşlem hattınızın sonunda bir prefetch
işleminiz yoksa bir tane eklemelisiniz. tf.data
performans önerileri hakkında daha fazla bilgi için tf.data performans kılavuzuna bakın.
Zaten verileri önceden getiriyorsanız ve giriş hattı hala darboğazınızsa, performansı daha ayrıntılı analiz etmek için sonraki bölüme geçin.
3. Yüksek CPU kullanımına ulaşıyor musunuz?
tf.data
mevcut kaynakların mümkün olan en iyi şekilde kullanılmasını sağlamaya çalışarak yüksek verim elde eder. Genel olarak modelinizi GPU veya TPU gibi bir hızlandırıcıda çalıştırırken bile tf.data
işlem hatları CPU üzerinde çalıştırılır. Kullanımınızı sar ve htop gibi araçlarla veya GCP üzerinde çalışıyorsanız bulut izleme konsolundan kontrol edebilirsiniz.
Kullanımınız düşükse bu, giriş hattınızın ana bilgisayar CPU'sundan tam olarak yararlanamayabileceğini gösterir. En iyi uygulamalar için tf.data performans kılavuzuna başvurmalısınız. En iyi uygulamaları uyguladıysanız ve kullanım ve aktarım hızı düşük kalıyorsa aşağıdaki Darboğaz analizine geçin.
Kullanımınız kaynak sınırına yaklaşıyorsa performansı daha da artırmak için ya girdi hattınızın verimliliğini artırmanız (örneğin, gereksiz hesaplamalardan kaçınma) ya da hesaplama yükünü boşaltmanız gerekir.
tf.data
gereksiz hesaplamalardan kaçınarak giriş hattınızın verimliliğini artırabilirsiniz. Bunu yapmanın bir yolu, verileriniz belleğe sığıyorsa yoğun hesaplamalı çalışmanın ardından bir tf.data.Dataset.cache
dönüşümü eklemektir; bu, artan bellek kullanımı pahasına hesaplamayı azaltır. Ek olarak, tf.data
operasyon içi paralelliğin devre dışı bırakılması, verimliliği %10'dan fazla artırma potansiyeline sahiptir ve giriş hattınızda aşağıdaki seçeneği ayarlayarak bu yapılabilir:
dataset = ...
options = tf.data.Options()
options.experimental_threading.max_intra_op_parallelism = 1
dataset = dataset.with_options(options)
4. Darboğaz Analizi
Aşağıdaki bölümde, darboğazın nerede olduğunu ve olası azaltma stratejilerini anlamak için izleme görüntüleyicide tf.data
olaylarının nasıl okunacağı açıklanmaktadır.
Profiler'da tf.data
olaylarını anlama
Profiler'daki her tf.data
olayı Iterator::<Dataset>
adına sahiptir; burada <Dataset>
, veri kümesi kaynağının veya dönüşümün adıdır. Her olay ayrıca Iterator::<Dataset_1>::...::<Dataset_n>
uzun adına sahiptir ve bunu tf.data
olayına tıklayarak görebilirsiniz. Uzun adda, <Dataset_n>
, (kısa) addaki <Dataset>
ile eşleşir ve uzun addaki diğer veri kümeleri, aşağı akış dönüşümlerini temsil eder.
Örneğin, yukarıdaki ekran görüntüsü aşağıdaki koddan oluşturulmuştur:
dataset = tf.data.Dataset.range(10)
dataset = dataset.map(lambda x: x)
dataset = dataset.repeat(2)
dataset = dataset.batch(5)
Burada, Iterator::Map
olayının uzun adı Iterator::BatchV2::FiniteRepeat::Map
. Veri kümesi adının python API'sinden biraz farklı olabileceğini (örneğin, Tekrar yerine FiniteRepeat) ancak ayrıştırma için yeterince sezgisel olması gerektiğini unutmayın.
Senkron ve asenkron dönüşümler
Eşzamanlı tf.data
dönüşümleri için ( Batch
ve Map
gibi), aynı iş parçacığı üzerindeki yukarı akış dönüşümlerinden gelen olayları göreceksiniz. Yukarıdaki örnekte kullanılan tüm dönüşümler eşzamanlı olduğundan tüm olaylar aynı iş parçacığında görünür.
Eşzamansız dönüşümler için ( Prefetch
, ParallelMap
, ParallelInterleave
ve MapAndBatch
gibi) yukarı akış dönüşümlerindeki olaylar farklı bir iş parçacığında olacaktır. Bu gibi durumlarda "uzun ad", bir olayın ardışık düzende hangi dönüşüme karşılık geldiğini belirlemenize yardımcı olabilir.
Örneğin, yukarıdaki ekran görüntüsü aşağıdaki koddan oluşturulmuştur:
dataset = tf.data.Dataset.range(10)
dataset = dataset.map(lambda x: x)
dataset = dataset.repeat(2)
dataset = dataset.batch(5)
dataset = dataset.prefetch(1)
Burada Iterator::Prefetch
olayları tf_data_iterator_get_next
iş parçacıklarındadır. Prefetch
eşzamansız olduğundan, giriş olayları ( BatchV2
) farklı bir iş parçacığında olacaktır ve Iterator::Prefetch::BatchV2
uzun adı aranarak bulunabilir. Bu durumda, bunlar tf_data_iterator_resource
iş parçacığındadır. Uzun isminden BatchV2
Prefetch
yukarı akışında olduğu sonucunu çıkarabilirsiniz. Ayrıca BatchV2
olayının parent_id
Prefetch
olayının kimliğiyle eşleşecektir.
Darboğazın belirlenmesi
Genel olarak, giriş hattınızdaki darboğazı tanımlamak için giriş hattını en dıştaki dönüşümden kaynağa kadar yürütün. İşlem hattınızdaki son dönüşümden başlayarak, yavaş bir dönüşüm bulana veya TFRecord
gibi bir kaynak veri kümesine ulaşana kadar yukarı akış dönüşümlerine tekrar devam edin. Yukarıdaki örnekte Prefetch
başlayacak, ardından BatchV2
, FiniteRepeat
, Map
ve son olarak Range
doğru yukarı doğru yürüyeceksiniz.
Genel olarak yavaş bir dönüşüm, olayları uzun ancak girdi olayları kısa olan dönüşüme karşılık gelir. Aşağıda bazı örnekler verilmiştir.
Çoğu ana bilgisayar giriş hattındaki son (en dıştaki) dönüşümün Iterator::Model
olayı olduğunu unutmayın. Model dönüşümü, tf.data
çalışma zamanı tarafından otomatik olarak tanıtılır ve giriş hattı performansının enstrümantasyonu ve otomatik ayarlanması için kullanılır.
İşiniz bir dağıtım stratejisi kullanıyorsa iz görüntüleyici, cihazın giriş hattına karşılık gelen ek olaylar içerecektir. Cihaz hattının en dıştaki dönüşümü ( IteratorGetNextOp::DoCompute
veya IteratorGetNextAsOptionalOp::DoCompute
altında yuvalanmıştır), yukarı akış Iterator::Generator
olayıyla bir Iterator::Prefetch
olayı olacaktır. Iterator::Model
olaylarını arayarak karşılık gelen ana bilgisayar işlem hattını bulabilirsiniz.
Örnek 1
Yukarıdaki ekran görüntüsü aşağıdaki giriş hattından oluşturulmuştur:
dataset = tf.data.TFRecordDataset(filename)
dataset = dataset.map(parse_record)
dataset = dataset.batch(32)
dataset = dataset.repeat()
Ekran görüntüsünde, (1) Iterator::Map
olaylarının uzun olduğunu, ancak (2) giriş olaylarının ( Iterator::FlatMap
) hızla geri döndüğünü gözlemleyin. Bu, sıralı Harita dönüşümünün darboğaz olduğunu göstermektedir.
Ekran görüntüsünde InstantiatedCapturedFunction::Run
olayının, harita işlevini yürütmek için gereken süreye karşılık geldiğini unutmayın.
Örnek 2
Yukarıdaki ekran görüntüsü aşağıdaki giriş hattından oluşturulmuştur:
dataset = tf.data.TFRecordDataset(filename)
dataset = dataset.map(parse_record, num_parallel_calls=2)
dataset = dataset.batch(32)
dataset = dataset.repeat()
Bu örnek yukarıdakine benzer ancak Harita yerine ParallelMap kullanır. Burada (1) Iterator::ParallelMap
olaylarının uzun olduğunu, ancak (2) Iterator::FlatMap
giriş olaylarının (ParallelMap eşzamansız olduğundan farklı bir iş parçacığındadırlar) kısa olduğunu fark ettik. Bu, ParallelMap dönüşümünün darboğaz olduğunu gösteriyor.
Darboğazın ele alınması
Kaynak veri kümeleri
TFRecord dosyalarından okuma gibi bir veri kümesi kaynağını darboğaz olarak belirlediyseniz, veri ayıklamayı paralelleştirerek performansı artırabilirsiniz. Bunu yapmak için verilerinizin birden fazla dosyaya bölündüğünden emin olun ve tf.data.Dataset.interleave
num_parallel_calls
parametresi tf.data.AUTOTUNE
olarak ayarlanmış şekilde kullanın. Eğer determinizm programınız için önemli değilse, TF 2.2'den itibaren tf.data.Dataset.interleave
üzerinde deterministic=False
işaretini ayarlayarak performansı daha da artırabilirsiniz. Örneğin, TFRecords'tan okuyorsanız aşağıdakileri yapabilirsiniz:
dataset = tf.data.Dataset.from_tensor_slices(filenames)
dataset = dataset.interleave(tf.data.TFRecordDataset,
num_parallel_calls=tf.data.AUTOTUNE,
deterministic=False)
Parçalanmış dosyaların, dosya açma yükünü azaltmak için makul derecede büyük olması gerektiğini unutmayın. Paralel veri çıkarma hakkında daha fazla ayrıntı için tf.data
performans kılavuzunun bu bölümüne bakın.
Dönüşüm veri kümeleri
Darboğaz olarak bir ara tf.data
dönüşümü belirlediyseniz, verileriniz belleğe sığıyorsa ve uygunsa, dönüşümü paralelleştirerek veya hesaplamayı önbelleğe alarak bu sorunu giderebilirsiniz. Map
gibi bazı dönüşümlerin paralel karşılıkları vardır; tf.data
performans kılavuzu bunların nasıl paralelleştirileceğini gösterir . Filter
, Unbatch
ve Batch
gibi diğer dönüşümler doğası gereği sıralıdır; “dış paralellik” getirerek bunları paralelleştirebilirsiniz. Örneğin, girdi hattınızın başlangıçta Batch
darboğaz olduğu aşağıdaki gibi göründüğünü varsayalım:
filenames = tf.data.Dataset.list_files(file_path, shuffle=is_training)
dataset = filenames_to_dataset(filenames)
dataset = dataset.batch(batch_size)
Giriş hattının birden çok kopyasını parçalanmış girişler üzerinde çalıştırıp sonuçları birleştirerek "dış paralellik" sağlayabilirsiniz:
filenames = tf.data.Dataset.list_files(file_path, shuffle=is_training)
def make_dataset(shard_index):
filenames = filenames.shard(NUM_SHARDS, shard_index)
dataset = filenames_to_dataset(filenames)
Return dataset.batch(batch_size)
indices = tf.data.Dataset.range(NUM_SHARDS)
dataset = indices.interleave(make_dataset,
num_parallel_calls=tf.data.AUTOTUNE)
dataset = dataset.prefetch(tf.data.AUTOTUNE)
Ek kaynaklar
- Performans
tf.data
giriş ardışık düzenlerinin nasıl yazılacağına ilişkin tf.data performans kılavuzu - TensorFlow videosu:
tf.data
en iyi uygulamaları - Profil oluşturucu kılavuzu
- Colab ile profil oluşturma eğitimi