Google I/O'yu ayarladığınız için teşekkür ederiz. İsteğe bağlı olarak tüm oturumları görüntüleyin İsteğe bağlı olarak izleyin

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

genel bakış

Bu kılavuz, TensorFlow Profiler ve tf.data ile aşina olduğunuzu varsayar. Kullanıcıların giriş boru hattı performans sorunlarını teşhis etmesine ve düzeltmesine yardımcı olmak için örneklerle adım adım talimatlar sağlamayı amaçlar.

Başlamak için, TensorFlow işinizin profilini toplayın. Bunun nasıl yapılacağına ilişkin talimatlar, CPU'lar/GPU'lar ve Cloud TPU'lar için mevcuttur.

TensorFlow Trace Viewer

Aşağıda ayrıntıları verilen analiz iş akışı, Profil Oluşturucu'daki iz görüntüleyici aracına odaklanır. Bu araç, TensorFlow programınız tarafından gerçekleştirilen 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ü belirlemenizi sağlar. İz görüntüleyici hakkında daha fazla bilgi için TF Profiler kılavuzunun bu bölümüne bakın . Genel olarak, ana bilgisayar CPU zaman çizelgesinde tf.data olayları görünecektir.

Analiz İş Akışı

Lütfen aşağıdaki iş akışını takip edin. Geliştirmemize yardımcı olacak geri bildiriminiz varsa, lütfen "comp:data" etiketiyle bir github sorunu oluşturun .

1. tf.data hattınız yeterince hızlı veri üretiyor mu?

Giriş boru hattının TensorFlow programınız için darboğaz olup olmadığını belirleyerek başlayın.

Bunu yapmak için izleme görüntüleyicide IteratorGetNext::DoCompute işlemlerini arayın. Genel olarak, bunları bir adımın başında görmeyi beklersiniz. Bu dilimler, giriş boru hattınızın istendiğinde bir grup öğeyi vermesi için geçen süreyi temsil eder. Bir tf.function içinde keras kullanıyorsanız veya veri kümeniz üzerinde yineleme tf.function , bunlar tf_data_iterator_get_next dizilerinde bulunmalıdır.

Bir dağıtım stratejisi kullanıyorsanız, IteratorGetNext::DoCompute ::DoCompute yerine (TF 2.3'ten itibaren) IteratorGetNextAsOptional::DoCompute olaylarını görebileceğinizi unutmayın.

image

Aramalar hızlı bir şekilde geri dönerse (<= 50 us), bu, talep edildiğinde verilerinizin mevcut olduğu anlamına gelir. Giriş boru hattı sizin darboğazınız değildir; daha genel performans analizi ipuçları için Profil oluşturucu kılavuzuna bakın.

image

Aramalar yavaş dönerse, tf.data tüketicinin isteklerine yetişemez. Bir sonraki bölüme devam edin.

2. Verileri önceden mi getiriyorsunuz?

Girdi işlem hattı performansı için en iyi uygulama, tf.data işlem hattınızın sonuna bir tf.data.Dataset.prefetch dönüşümü tf.data . Bu dönüşüm, girdi ardışık düzeninin ön işleme hesaplamasıyla model hesaplamanın sonraki adımıyla çakışır ve modelinizi eğitirken optimum girdi ardışık düzeni performansı için gereklidir. Verileri önceden getiriyorsanız, IteratorGetNext::DoCompute aynı iş parçacığında bir Iterator::Prefetch dilimi görmelisiniz.

image

Ardışık düzeninizin sonunda bir prefetch yoksa, bir tane eklemelisiniz. tf.data performans önerileri hakkında daha fazla bilgi için tf.data performans kılavuzuna bakın.

Halihazırda verileri önceden getiriyorsanız ve giriş ardışık düzeni sizin için hâlâ darboğazsa, performansı daha fazla analiz etmek için bir sonraki bölüme geçin.

3. Yüksek CPU kullanımına ulaşıyor musunuz?

tf.data , mevcut kaynakları mümkün olan en iyi şekilde kullanmaya çalışarak yüksek verim elde eder. Genel olarak, modelinizi GPU veya TPU gibi bir hızlandırıcı üzerinde çalıştırırken bile tf.data ardışık düzenleri CPU üzerinde çalıştırılır. Kullanımınızı sar ve htop gibi araçlarla veya GCP'de çalışıyorsanız bulut izleme konsolunda kontrol edebilirsiniz.

Kullanımınız düşükse bu, girdi boru 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 ile verim düşük kalmaya devam ediyorsa, aşağıdaki Darboğaz analizine devam edin.

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 hesaplamadan kaçınma) ya da hesaplamayı boşaltmanız gerekir.

tf.data gereksiz hesaplamalardan kaçınarak girdi işlem hattınızın verimliliğini artırabilirsiniz. Bunu yapmanın bir yolu, verileriniz belleğe sığarsa, hesaplama açısından yoğun bir ç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 işlem içi paralelliğin devre dışı bırakılması verimliliği > %10 artırma potansiyeline sahiptir ve giriş boru hattınızda aşağıdaki seçeneği ayarlayarak 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üm, darboğazın nerede olduğunu ve olası hafifletme stratejilerini anlamak için iz görüntüleyicide tf.data olaylarının nasıl okunacağını açıklamaktadır.

Profil tf.data olaylarını anlama

Profil Oluşturucu'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ın ayrıca tf.data olayına tıklayarak görebileceğiniz Iterator::<Dataset_1>::...::<Dataset_n> uzun adı vardır. Uzun adda <Dataset_n> , (kısa) addan <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ı, Iterator::BatchV2::FiniteRepeat::Map uzun adına sahiptir. Veri kümelerinin adının python API'sinden biraz farklı olabileceğini (örneğin, Tekrar yerine FiniteRepeat) ancak ayrıştırmak için yeterince sezgisel olması gerektiğini unutmayın.

Eşzamanlı ve eşzamansız dönüşümler

Eşzamanlı tf.data dönüşümleri için ( Batch ve Map gibi), aynı iş parçacığında yukarı akış dönüşümlerinden gelen olayları göreceksiniz. Yukarıdaki örnekte, kullanılan tüm dönüşümler senkronize olduğundan, tüm olaylar aynı iş parçacığında görünür.

Zaman uyumsuz dönüşümler için (örneğin Prefetch , ParallelMap , ParallelInterleave ve MapAndBatch ) yukarı akış dönüşümlerinden gelen 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ığındadır. Prefetch eşzamansız olduğundan, girdi olayları ( BatchV2 ) farklı bir iş parçacığında olacaktır ve Iterator::Prefetch::BatchV2 uzun adı aranarak bulunabilir. Bu durumda, tf_data_iterator_resource dizisinde bulunurlar. Uzun adından, BatchV2 Prefetch yukarı akışı olduğunu anlayabilirsiniz. Ayrıca, parent_id olayının BatchV2 değeri, Prefetch olayının kimliği ile eşleşecektir.

Darboğazın belirlenmesi

Genel olarak, giriş boru hattınızdaki darboğazı belirlemek için, giriş boru 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 yineleyin. Yukarıdaki örnekte, Prefetch başlayacak, ardından BatchV2 , FiniteRepeat , Map ve son olarak Range doğru ilerleyeceksiniz.

Genel olarak, yavaş bir dönüşüm, olayları uzun, ancak girdi olayları kısa olan bir dönüşüme karşılık gelir. Bazı örnekler aşağıdadır.

Çoğu ana bilgisayar giriş işlem 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 sunulur ve girdi ardışık düzeni performansının enstrümantasyonu ve otomatik olarak ayarlanması için kullanılır.

İşiniz bir dağıtım stratejisi kullanıyorsa iz görüntüleyici, cihaz giriş hattına karşılık gelen ek olaylar içerecektir. Cihaz boru hattının en dıştaki dönüşümü ( IteratorGetNextOp::DoCompute veya IteratorGetNextAsOptionalOp::DoCompute altında iç içe geçmiş), yukarı akış Iterator::Generator olayı ile bir Iterator::Prefetch olayı olacaktır. Iterator::Model olaylarını arayarak karşılık gelen ana bilgisayar ardışık düzenini bulabilirsiniz.

örnek 1

image

Yukarıdaki ekran görüntüsü, aşağıdaki giriş boru hattından oluşturulur:

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ızlı bir şekilde geri döndüğünü gözlemleyin. Bu, sıralı Harita dönüşümünün darboğaz olduğunu gösterir.

Ekran görüntüsünde, InstantiatedCapturedFunction::Run olayının harita işlevini yürütmek için geçen süreye karşılık geldiğini unutmayın.

Örnek 2

image

Yukarıdaki ekran görüntüsü, aşağıdaki giriş boru hattından oluşturulur:

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'i 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ığında olan) kısa olduğunu fark ettik. Bu, ParallelMap dönüşümünün darboğaz olduğunu gösterir.

Darboğazın ele alınması

Kaynak veri kümeleri

Darboğaz olarak bir veri kümesi kaynağı belirlediyseniz (TFRecord dosyalarından okuma gibi), veri ayıklamayı paralel hale getirerek performansı artırabilirsiniz. Bunu yapmak için, verilerinizin birden çok dosya arasında paylaştırıldığından emin olun ve tf.data.Dataset.interleave num_parallel_calls parametresi tf.data.AUTOTUNE olarak ayarlanmış şekilde kullanın. Determinizm programınız için önemli değilse, TF tf.data.Dataset.interleave itibaren tf.data.Dataset.interleave'de deterministic=False bayrağını 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, bir dosyayı açma yükünün amortismana tabi tutulması için makul ölçüde 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üştürmeler doğası gereği sıralıdır; “dış paralellik” getirerek bunları paralelleştirebilirsiniz. Örneğin, giriş boru hattınızın başlangıçta aşağıdaki gibi göründüğünü varsayalım, darboğaz Batch ile:

filenames = tf.data.Dataset.list_files(file_path, shuffle=is_training)
dataset = filenames_to_dataset(filenames)
dataset = dataset.batch(batch_size)

Parçalanmış girdiler üzerinde girdi hattının birden çok kopyasını çalıştırarak ve sonuçları birleştirerek "dış paralelliği" tanıtabilirsiniz:

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