TF Profiler ile tf.data performansını analiz edin

Genel Bakış

Bu kılavuz, TensorFlow Profiler ve tf.data aşina olduğunuzu varsayar. 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.

TensorFlow Trace Viewer

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şı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ın tf_data_iterator_get_next iş parçacıklarında bulunması gerekir.

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.

image

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.

image

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

image

İşlem hattınızın sonunda bir prefetch getirmeniz 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 hesaplamanın 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 aynı zamanda 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.

image

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

image

Ö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ırlar. 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 giriş 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

image

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

image

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 çözebilirsiniz. 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