Migracja użycia tf.summary do TF 2.x

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik
import tensorflow as tf

TensorFlow 2.x zawiera znaczące zmiany w tf.summary API używane do zapisu danych sumarycznych do wizualizacji w TensorBoard.

Co się zmieniło

Jest to użyteczne myśleć o tf.summary API jako dwie podgrupy API:

W TF 1.x

Obie połówki musiał ręcznie połączone ze sobą - pobierając wyjścia Podsumowanie op poprzez Session.run() i wywołanie FileWriter.add_summary(output, step) . v1.summary.merge_all() op wykonane to łatwiejsze przy użyciu kolekcji wykres agregować wszystkie wyjścia podsumowanie op, ale takie podejście nadal pracował źle dla chętnych realizacji i kontroli przepływu, co czyni go szczególnie źle nadają się do TF 2.x.

W TF 2.X

Obie połówki są ściśle zintegrowane, a teraz poszczególne tf.summary ops napisać swoje dane natychmiast po uruchomieniu. Korzystanie z interfejsu API z kodu modelu powinno nadal wyglądać znajomo, ale jest teraz przyjazne dla chętnego wykonania, zachowując jednocześnie zgodność z trybem wykresu. Integracja obu połówek pomocą API summary.FileWriter jest obecnie częścią kontekście realizacji TensorFlow i dostaje dostępnych bezpośrednio tf.summary ops, więc konfigurowanie pisarzy jest głównym elementem, który wygląda inaczej.

Przykładowe użycie z gorliwym wykonaniem, domyślne w TF 2.x:

writer = tf.summary.create_file_writer("/tmp/mylogs/eager")

with writer.as_default():
  for step in range(100):
    # other model code would go here
    tf.summary.scalar("my_metric", 0.5, step=step)
    writer.flush()
ls /tmp/mylogs/eager
events.out.tfevents.1633086727.kokoro-gcp-ubuntu-prod-1386032077.31590.0.v2

Przykładowe użycie z wykonaniem wykresu tf.function:

writer = tf.summary.create_file_writer("/tmp/mylogs/tf_function")

@tf.function
def my_func(step):
  with writer.as_default():
    # other model code would go here
    tf.summary.scalar("my_metric", 0.5, step=step)

for step in tf.range(100, dtype=tf.int64):
  my_func(step)
  writer.flush()
ls /tmp/mylogs/tf_function
events.out.tfevents.1633086728.kokoro-gcp-ubuntu-prod-1386032077.31590.1.v2

Przykładowe użycie ze starszym wykonaniem wykresu TF 1.x:

g = tf.compat.v1.Graph()
with g.as_default():
  step = tf.Variable(0, dtype=tf.int64)
  step_update = step.assign_add(1)
  writer = tf.summary.create_file_writer("/tmp/mylogs/session")
  with writer.as_default():
    tf.summary.scalar("my_metric", 0.5, step=step)
  all_summary_ops = tf.compat.v1.summary.all_v2_summary_ops()
  writer_flush = writer.flush()


with tf.compat.v1.Session(graph=g) as sess:
  sess.run([writer.init(), step.initializer])

  for i in range(100):
    sess.run(all_summary_ops)
    sess.run(step_update)
    sess.run(writer_flush)
ls /tmp/mylogs/session
events.out.tfevents.1633086728.kokoro-gcp-ubuntu-prod-1386032077.31590.2.v2

Konwersja kodu

Konwersja istniejącego tf.summary wykorzystanie API TF 2.x nie można wiarygodnie zautomatyzowany, więc tf_upgrade_v2 skrypt po prostu przepisuje to wszystko tf.compat.v1.summary . Aby przeprowadzić migrację do TF 2.x, musisz dostosować swój kod w następujący sposób:

  1. Zestaw domyślny pisarz poprzez .as_default() musi być obecny w użyciu ops podsumowania

    • Oznacza to gorliwe wykonywanie operacji lub używanie operacji do tworzenia grafów
    • Bez domyślnego programu piszącego operacje podsumowania stają się cichymi operacjami bez operacji
    • Domyślne pisarze nie (jeszcze) propaguje się przez @tf.function granicy wykonania - są wykrywane tylko wtedy, gdy funkcja jest drogi - więc najlepszym rozwiązaniem jest zadzwonić writer.as_default() wewnątrz ciała funkcji, oraz w celu zapewnienia, że przedmiot pisarz nadal istnieje tak długo, jak @tf.function jest używany
  2. Wartość „krok” muszą być przekazywane do każdego op poprzez step argumentu

    • TensorBoard wymaga wartości kroku, aby renderować dane jako szereg czasowy
    • Jawne przekazywanie jest konieczne, ponieważ globalny krok z TF 1.x został usunięty, więc każda operacja musi znać żądaną zmienną kroku do odczytu
    • Aby zmniejszyć boilerplate, eksperymentalne wsparcie dla rejestracji wartość domyślna krokiem jest dostępny jako tf.summary.experimental.set_step() , ale jest to tymczasowe funkcje, które mogą ulec zmianie bez uprzedzenia
  3. Zmieniły się sygnatury funkcji poszczególnych operacji podsumowujących

    • Wartość zwracana jest teraz wartością logiczną (wskazującą, czy podsumowanie zostało rzeczywiście napisane)
    • Druga nazwa parametru (jeśli jest używany) został zmieniony z tensor do data
    • collections parametr został usunięty; kolekcje są tylko TF 1.x
    • family parametrów zostały usunięte; Wystarczy użyć tf.name_scope()
  4. [Tylko dla starszych użytkowników trybu wykresu / wykonywania sesji]

    • Najpierw zainicjować pisarz v1.Session.run(writer.init())

    • Stosować v1.summary.all_v2_summary_ops() , aby uzyskać wszystkie ops podsumowania TF 2.x dla bieżącego wykresu, na przykład, aby wykonać je za pomocą Session.run()

    • Przepłukać pisarz v1.Session.run(writer.flush()) , a także do close()

Jeśli TF Kod 1.x został zamiast korzystania tf.contrib.summary API, jest dużo bardziej podobny do interfejsu API TF 2.x, więc tf_upgrade_v2 skrypt zautomatyzować większość kroków migracji (i emituje ostrzeżenia lub błędy do dowolnego użytku, które nie mogą być w pełni migrowane). W większości przypadków po prostu przepisuje wywołań API do tf.compat.v2.summary ; Jeśli potrzebujesz tylko zgodność z TF 2.x można upuścić compat.v2 i po prostu odwołać go jako tf.summary .

Dodatkowe wskazówki

Oprócz krytycznych obszarów powyżej, niektóre aspekty pomocnicze również uległy zmianie:

  • Nagrywanie warunkowe (np. „rejestruj co 100 kroków”) ma nowy wygląd

    • Do OPS kontrolnych oraz towarzyszącym kodu, zawinąć je w regularne if (który działa w trybie chętny iw @tf.function poprzez autografem ) lub tf.cond
    • Aby sterować tylko podsumowania, należy użyć nowego tf.summary.record_if() menedżera kontekstowe, i przekazać mu logiczną stan swojego wyboru
    • Zastępują one wzorzec TF 1.x:

      if condition:
        writer.add_summary()
      
  • Brak bezpośredniego pisanie tf.compat.v1.Graph - zamiast funkcje użycie śladowych

  • Nie bardziej globalny pisarz buforowanie za logdir z tf.summary.FileWriterCache

    • Użytkownicy powinni też wdrożyć własne buforowanie / dzielenie obiektów pisarz, lub po prostu używać osobnych pisarzy (wsparcie TensorBoard za ten ostatni jest w toku )
  • Zmieniła się binarna reprezentacja pliku zdarzeń

    • TensorBoard 1.x obsługuje już nowy format; ta różnica dotyczy tylko użytkowników, którzy ręcznie analizują dane podsumowujące z plików zdarzeń
    • Dane podsumowujące są teraz przechowywane jako bajty tensorowe; można użyć tf.make_ndarray(event.summary.value[0].tensor) , aby przekształcić go numpy