TensorFlow 1.x ve TensorFlow 2 - Davranışlar ve API'ler

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın GitHub'da görüntüle Not defterini indir

Kaputun altında, TensorFlow 2, TF1.x'ten temelde farklı bir programlama paradigmasını takip eder.

Bu kılavuz, davranışlar ve API'ler açısından TF1.x ve TF2 arasındaki temel farklılıkları ve bunların geçiş yolculuğunuzla nasıl ilişkili olduğunu açıklamaktadır.

Büyük değişikliklerin üst düzey özeti

Temel olarak, TF1.x ve TF2, yürütme (TF2'de istekli), değişkenler, kontrol akışı, tensör şekilleri ve tensör eşitlik karşılaştırmaları etrafında farklı bir çalışma zamanı davranışı seti kullanır. TF2 uyumlu olması için kodunuzun TF2 davranışlarının tamamıyla uyumlu olması gerekir. Taşıma sırasında, tf.compat.v1.enable_* veya tf.compat.v1.disable_* API'leri aracılığıyla bu davranışların çoğunu tek tek etkinleştirebilir veya devre dışı bırakabilirsiniz. Tek istisna, istekli yürütmeyi etkinleştirmenin/devre dışı bırakmanın bir yan etkisi olan koleksiyonların kaldırılmasıdır.

Yüksek düzeyde, TensorFlow 2:

  • Gereksiz API'leri kaldırır.
  • API'leri daha tutarlı hale getirir - örneğin, Birleşik RNN'ler ve Birleşik Optimize Ediciler .
  • İşlevleri oturumlara tercih eder ve grafikler ve derleme için otomatik kontrol bağımlılıkları sağlayan tf.function ile birlikte Eager yürütmenin varsayılan olarak etkinleştirildiği Python çalışma zamanı ile daha iyi bütünleşir.
  • Genel grafik koleksiyonlarını kullanımdan kaldırır.
  • ReferenceVariables üzerinde ResourceVariables kullanarak Değişken eşzamanlılık semantiğini değiştirir.
  • Fonksiyon tabanlı ve türevlenebilir kontrol akışını destekler (Kontrol Akışı v2).
  • TensorShape API'sini tf.compat.v1.Dimension nesneleri yerine int s tutacak şekilde basitleştirir.
  • Tensör eşitliği mekaniğini günceller. TF1.x'te tensörler ve değişkenler üzerindeki == operatörü, nesne referans eşitliğini kontrol eder. TF2'de değer eşitliğini kontrol eder. Ek olarak, tensörler/değişkenler artık hash edilebilir değildir, ancak setlerde veya dict anahtarları olarak kullanmanız gerekiyorsa var.ref() aracılığıyla bunlara hashable nesne referansları alabilirsiniz.

Aşağıdaki bölümler, TF1.x ve TF2 arasındaki farklar hakkında biraz daha bağlam sağlar. TF2'nin arkasındaki tasarım süreci hakkında daha fazla bilgi edinmek için RFC'leri ve tasarım belgelerini okuyun.

API temizleme

Birçok API, TF2'de ya gitti ya da taşındı . Önemli değişikliklerden bazıları, şimdi açık kaynaklı absl-py lehine tf.app , tf.flags ve tf.logging öğelerinin kaldırılmasını, tf.contrib içinde yaşayan projelerin yeniden barındırılmasını ve ana tf.* ad alanını şu şekilde temizlemeyi tf.contrib : daha az kullanılan işlevleri tf.math gibi alt paketlere taşımak. Bazı API'ler TF2 eşdeğerleriyle değiştirildi - tf.summary , tf.keras.metrics ve tf.keras.optimizers .

tf.compat.v1 : Eski ve Uyumluluk API Uç Noktaları

tf.compat ve tf.compat.v1 ad alanlarının altındaki semboller, TF2 API'leri olarak kabul edilmez. Bu ad alanları, TF 1.x'ten eski API uç noktalarının yanı sıra uyumluluk sembollerinin bir karışımını ortaya çıkarır. Bunlar, TF1.x'ten TF2'ye geçişe yardımcı olmayı amaçlamaktadır. Ancak, bu compat.v1 API'lerinin hiçbiri deyimsel TF2 API'leri olmadığından, bunları yepyeni TF2 kodu yazmak için kullanmayın.

Bireysel tf.compat.v1 sembolleri TF2 uyumlu olabilir, çünkü TF2 davranışları etkinken bile çalışmaya devam ederler ( tf.compat.v1.losses.mean_squared_error gibi), diğerleri ise TF2 ile uyumsuzdur ( tf.compat.v1.metrics.accuracy ). Çoğu compat.v1 sembolü (tümü olmasa da), belgelerinde TF2 davranışlarıyla uyumluluk derecelerini ve bunların TF2 API'lerine nasıl taşınacağını açıklayan özel geçiş bilgileri içerir.

TF2 yükseltme komut dosyası , birçok compat.v1 API sembolünü, takma ad olmaları veya aynı bağımsız değişkenlere sahip olmaları ancak farklı bir sıralamaya sahip olmaları durumunda eşdeğer TF2 API'leriyle eşleyebilir. TF1.x API'lerini otomatik olarak yeniden adlandırmak için yükseltme komut dosyasını da kullanabilirsiniz.

Yanlış arkadaş API'leri

TF2 tf ad alanında ( compat.v1 altında değil) bulunan bir dizi "sahte arkadaş" sembolü bulunur, bunlar aslında TF2 davranışlarının altında yatan ve/veya TF2 davranışlarının tamamıyla tam olarak uyumlu değildir. Bu nedenle, bu API'lerin potansiyel olarak sessiz yollarla TF2 koduyla hatalı davranması muhtemeldir.

  • tf.estimator.* : Tahminciler, başlık altında grafikler ve oturumlar oluşturur ve kullanır. Bu nedenle, bunlar TF2 uyumlu olarak kabul edilmemelidir. Kodunuz tahmin edicileri çalıştırıyorsa, TF2 davranışlarını kullanmıyor.
  • keras.Model.model_to_estimator(...) : Bu, başlık altında yukarıda belirtildiği gibi TF2 uyumlu olmayan bir Tahminci oluşturur.
  • tf.Graph().as_default() : Bu, TF1.x grafik davranışlarını girer ve standart TF2 uyumlu tf.function davranışlarını izlemez. Bunun gibi grafiklere giren kodlar genellikle onları Oturumlar aracılığıyla çalıştırır ve TF2 uyumlu olarak kabul edilmemelidir.
  • tf.feature_column.* Özellik sütunu API'leri genellikle TF1 stili tf.compat.v1.get_variable değişken oluşturmaya dayanır ve oluşturulan değişkenlere genel koleksiyonlar aracılığıyla erişileceğini varsayar. TF2, koleksiyonları desteklemediğinden, API'ler, TF2 davranışları etkinken çalıştırıldığında düzgün çalışmayabilir.

Diğer API değişiklikleri

  • TF2, tf.colocate_with kullanımını gereksiz kılan cihaz yerleştirme algoritmalarında önemli iyileştirmeler sunar. Kaldırılması performansın düşmesine neden oluyorsa lütfen bir hata bildirin .

  • tf.v1.ConfigProto'nun tüm kullanımlarını tf.v1.ConfigProto eşdeğer işlevlerle tf.config .

hevesli yürütme

TF1.x, tf.* API çağrıları yaparak soyut bir sözdizimi ağacını (grafik) manuel olarak birleştirmenizi ve ardından bir dizi çıktı tensörü ve giriş tensörünü bir session.run çağrısına geçirerek soyut sözdizimi ağacını manuel olarak derlemenizi gerektiriyordu. TF2 hevesle çalışır (normalde Python'un yaptığı gibi) ve grafikleri ve oturumları uygulama ayrıntıları gibi hissettirir.

İstekli yürütmenin dikkate değer bir yan ürünü, tüm kod satırları sırayla yürütüldüğü için tf.control_dependencies artık gerekli olmamasıdır (bir tf.function içinde, yan etkileri olan kod, yazılan sırayla yürütülür).

Artık küresel yok

TF1.x, büyük ölçüde örtük global ad alanlarına ve koleksiyonlara güveniyordu. tf.Variable , varsayılan grafikte bir koleksiyona konur ve ona işaret eden Python değişkeninin izini kaybetseniz bile orada kalır. Daha sonra bu tf.Variable kurtarabilirsiniz, ancak yalnızca oluşturulduğu adı biliyorsanız. Değişkenin yaratılmasının kontrolü sizde değilse bunu yapmak zordu. Sonuç olarak, değişkenlerinizi yeniden bulmanıza ve çerçevelerin kullanıcı tarafından oluşturulan değişkenleri bulmasına yardımcı olmak için her türlü mekanizma çoğaldı. Bunlardan bazıları şunlardır: değişken kapsamlar, genel koleksiyonlar, tf.get_global_step ve tf.global_variables_initializer gibi yardımcı yöntemler, optimize ediciler, tüm eğitilebilir değişkenler üzerinde gradyanları dolaylı olarak hesaplar, vb. TF2, tüm bu mekanizmaları ( Değişkenler 2.0 RFC ) varsayılan mekanizma lehine ortadan kaldırır - değişkenlerinizi takip edersiniz. Bir tf.Variable izini kaybederseniz, çöp toplanır.

Değişkenleri izleme gereksinimi bazı ekstra işler yaratır, ancak modelleme şimleri ve tf.Module s ve tf.keras.layers.Layer s'deki örtük nesne yönelimli değişken koleksiyonları gibi davranışlar gibi araçlarla yük en aza indirilir.

Oturumlar değil, işlevler

Bir session.run çağrısı neredeyse bir işlev çağrısı gibidir: girişleri ve çağrılacak işlevi belirlersiniz ve bir dizi çıktıyı geri alırsınız. TF2'de, TensorFlow'un onu tek bir grafik olarak çalıştırması için JIT derlemesi için işaretlemek üzere tf.function kullanarak bir Python işlevini dekore edebilirsiniz ( Functions 2.0 RFC ). Bu mekanizma, TF2'nin grafik modunun tüm avantajlarını elde etmesini sağlar:

  • Performans: İşlev optimize edilebilir (düğüm budama, çekirdek birleştirme vb.)
  • Taşınabilirlik: İşlev dışa aktarılabilir/yeniden içe aktarılabilir ( SavedModel 2.0 RFC ), modüler TensorFlow işlevlerini yeniden kullanmanıza ve paylaşmanıza olanak tanır.
# TF1.x
outputs = session.run(f(placeholder), feed_dict={placeholder: input})
# TF2
outputs = f(input)

Python ve TensorFlow kodunu serbestçe serpiştirme gücüyle Python'un etkileyiciliğinden yararlanabilirsiniz. Ancak taşınabilir TensorFlow, mobil, C++ ve JavaScript gibi bir Python yorumlayıcısı olmayan bağlamlarda yürütülür. tf.function eklerken kodunuzu yeniden yazmaktan kaçınmaya yardımcı olmak için, Python yapılarının bir alt kümesini TensorFlow eşdeğerlerine dönüştürmek için AutoGraph kullanın:

  • for / while -> tf.while_loop ( break ve continue etme desteklenir)
  • if -> tf.cond
  • for _ in dataset -> dataset.reduce

AutoGraph, sekans modelleri, pekiştirmeli öğrenme, özel eğitim döngüleri ve daha fazlası gibi birçok karmaşık ML programının performanslı ve özlü bir şekilde uygulanmasını mümkün kılan, kontrol akışının rastgele iç içe yerleştirilmesini destekler.

TF 2.x Davranış Değişikliklerine Uyum Sağlamak

TF2'ye geçişiniz, ancak TF2 davranışlarının tamamına geçiş yaptığınızda tamamlanır. Davranışların tamamı, tf.compat.v1.enable_v2_behaviors ve tf.compat.v1.disable_v2_behaviors aracılığıyla etkinleştirilebilir veya devre dışı bırakılabilir. Aşağıdaki bölümlerde her bir önemli davranış değişikliği ayrıntılı olarak tartışılmaktadır.

tf.function s kullanma

Geçiş sırasında programlarınızdaki en büyük değişikliklerin, grafiklerden ve oturumlardan istekli yürütmeye ve tf.function olan temel programlama modeli paradigma kaymasından kaynaklanması muhtemeldir. İstekli yürütme ve tf.function ile uyumlu olmayan API'lerden bunlarla uyumlu API'lere geçiş hakkında daha fazla bilgi edinmek için TF2 geçiş kılavuzlarına bakın.

Aşağıda, herhangi bir API'ye bağlı olmayan ve tf.Graph s ve tf.compat.v1.Session s'den tf.function s ile istekli yürütmeye geçerken sorunlara neden olabilecek bazı yaygın program kalıpları verilmiştir.

Model 1: Python nesne manipülasyonu ve yalnızca bir kez yapılması amaçlanan değişken oluşturma, birden çok kez çalıştırılır

Grafiklere ve oturumlara dayanan TF1.x programlarında, genellikle programınızdaki tüm Python mantığının yalnızca bir kez çalışması beklenir. Ancak, istekli yürütme ve tf.function ile Python mantığınızın en az bir kez, ancak muhtemelen daha fazla kez (ya birden çok kez hevesle, ya da farklı tf.function izlerinde birden çok kez) çalıştırılmasını beklemek adil olur. Bazen, tf.function aynı girdi üzerinde iki kez izlenerek beklenmeyen davranışlara neden olur (bkz. Örnek 1 ve 2). Daha fazla ayrıntı için tf.function kılavuzuna bakın.

Örnek 1: Değişken oluşturma

Fonksiyonun çağrıldığında bir değişken oluşturduğu aşağıdaki örneği düşünün:

def f():
  v = tf.Variable(1.0)
  return v

with tf.Graph().as_default():
  with tf.compat.v1.Session() as sess:
    res = f()
    sess.run(tf.compat.v1.global_variables_initializer())
    sess.run(res)

Ancak, değişken oluşturmayı içeren yukarıdaki işlevin tf.function ile saf olarak kaydırılmasına izin verilmez. tf.function yalnızca ilk çağrıda tek değişken oluşturmayı destekler. Bunu uygulamak için, tf.function ilk çağrıda değişken oluşturma algıladığında, tekrar izlemeyi dener ve ikinci izlemede değişken oluşturma varsa bir hata verir.

@tf.function
def f():
  print("trace") # This will print twice because the python body is run twice
  v = tf.Variable(1.0)
  return v

try:
  f()
except ValueError as e:
  print(e)

Geçici bir çözüm, değişkeni ilk çağrıda oluşturulduktan sonra önbelleğe almak ve yeniden kullanmaktır.

class Model(tf.Module):
  def __init__(self):
    self.v = None

  @tf.function
  def __call__(self):
    print("trace") # This will print twice because the python body is run twice
    if self.v is None:
      self.v = tf.Variable(0)
    return self.v

m = Model()
m()

Örnek 2: tf.function retracing nedeniyle kapsam dışı Tensörler

Örnek 1'de gösterildiği gibi, tf.function ilk çağrıda Değişken oluşturma algıladığında yeniden izlenecektir. Bu, fazladan kafa karışıklığına neden olabilir, çünkü iki izleme iki grafik oluşturacaktır. Geri izlemeden gelen ikinci grafik, ilk izleme sırasında oluşturulan grafikten bir Tensöre erişmeye çalıştığında, Tensorflow, Tensörün kapsam dışında olduğundan şikayet eden bir hata verir. Senaryoyu göstermek için aşağıdaki kod, ilk tf.function çağrısında bir veri kümesi oluşturur. Bu beklendiği gibi çalışır.

class Model(tf.Module):
  def __init__(self):
    self.dataset = None

  @tf.function
  def __call__(self):
    print("trace") # This will print once: only traced once
    if self.dataset is None:
      self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])
    it = iter(self.dataset)
    return next(it)

m = Model()
m()

Ancak, ilk tf.function çağrısında da bir değişken oluşturmaya çalışırsak, kod, veri kümesinin kapsam dışında olduğundan şikayet eden bir hata verecektir. Bunun nedeni, veri kümesinin birinci grafikte olması ve ikinci grafiğin de ona erişmeye çalışmasıdır.

class Model(tf.Module):
  def __init__(self):
    self.v = None
    self.dataset = None

  @tf.function
  def __call__(self):
    print("trace") # This will print twice because the python body is run twice
    if self.v is None:
      self.v = tf.Variable(0)
    if self.dataset is None:
      self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])
    it = iter(self.dataset)
    return [self.v, next(it)]

m = Model()
try:
  m()
except TypeError as e:
  print(e) # <tf.Tensor ...> is out of scope and cannot be used here.

En basit çözüm, hem değişken oluşturmanın hem de veri kümesi oluşturmanın tf.funciton çağrısının dışında olmasını sağlamaktır. Örneğin:

class Model(tf.Module):
  def __init__(self):
    self.v = None
    self.dataset = None

  def initialize(self):
    if self.dataset is None:
      self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])
    if self.v is None:
      self.v = tf.Variable(0)

  @tf.function
  def __call__(self):
    it = iter(self.dataset)
    return [self.v, next(it)]

m = Model()
m.initialize()
m()

Bununla birlikte, bazen tf.function içinde değişkenler yaratmaktan kaçınılamaz (bazı TF keras optimizerlerindeki slot değişkenleri gibi). Yine de, veri kümesi oluşturmayı tf.function çağrısının dışına kolayca taşıyabiliriz. Buna güvenebilmemizin nedeni, tf.function veri kümesini örtük bir girdi olarak alması ve her iki grafiğin de buna düzgün şekilde erişebilmesidir.

class Model(tf.Module):
  def __init__(self):
    self.v = None
    self.dataset = None

  def initialize(self):
    if self.dataset is None:
      self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])

  @tf.function
  def __call__(self):
    if self.v is None:
      self.v = tf.Variable(0)
    it = iter(self.dataset)
    return [self.v, next(it)]

m = Model()
m.initialize()
m()

Örnek 3: Dikt kullanımı nedeniyle beklenmeyen Tensorflow nesnesi yeniden oluşturma

tf.function , bir listeye ekleme veya bir sözlüğe kontrol etme/ekleme gibi python yan etkileri için çok zayıf bir desteğe sahiptir. Daha fazla ayrıntı "tf.function ile daha iyi performans" bölümündedir . Aşağıdaki örnekte kod, veri kümelerini ve yineleyicileri önbelleğe almak için sözlükleri kullanır. Aynı anahtar için, modele yapılan her çağrı, veri kümesinin aynı yineleyicisini döndürür.

class Model(tf.Module):
  def __init__(self):
    self.datasets = {}
    self.iterators = {}

  def __call__(self, key):
    if key not in self.datasets:
      self.datasets[key] = tf.compat.v1.data.Dataset.from_tensor_slices([1, 2, 3])
      self.iterators[key] = self.datasets[key].make_initializable_iterator()
    return self.iterators[key]

with tf.Graph().as_default():
  with tf.compat.v1.Session() as sess:
    m = Model()
    it = m('a')
    sess.run(it.initializer)
    for _ in range(3):
      print(sess.run(it.get_next())) # prints 1, 2, 3

Ancak, yukarıdaki kalıp tf.function içinde beklendiği gibi çalışmayacaktır. İzleme sırasında tf.function , sözlüklere eklemenin python yan etkisini yok sayar. Bunun yerine, yalnızca yeni bir veri kümesinin ve yineleyicinin oluşturulmasını hatırlar. Sonuç olarak, modele yapılan her çağrı her zaman yeni bir yineleyici döndürür. Sayısal sonuçlar veya performans yeterince önemli olmadıkça bu sorunu fark etmek zordur. Bu nedenle, kullanıcıların tf.function saf bir şekilde python koduna sarmadan önce kodu dikkatlice düşünmelerini öneririz.

class Model(tf.Module):
  def __init__(self):
    self.datasets = {}
    self.iterators = {}

  @tf.function
  def __call__(self, key):
    if key not in self.datasets:
      self.datasets[key] = tf.data.Dataset.from_tensor_slices([1, 2, 3])
      self.iterators[key] = iter(self.datasets[key])
    return self.iterators[key]

m = Model()
for _ in range(3):
  print(next(m('a'))) # prints 1, 1, 1

Beklenen davranışı elde etmek için veri kümesini ve yineleyici oluşturmayı grafiğin dışına çıkarmak için tf.init_scope kullanabiliriz:

class Model(tf.Module):
  def __init__(self):
    self.datasets = {}
    self.iterators = {}

  @tf.function
  def __call__(self, key):
    if key not in self.datasets:
      # Lifts ops out of function-building graphs
      with tf.init_scope():
        self.datasets[key] = tf.data.Dataset.from_tensor_slices([1, 2, 3])
        self.iterators[key] = iter(self.datasets[key])
    return self.iterators[key]

m = Model()
for _ in range(3):
  print(next(m('a'))) # prints 1, 2, 3

Genel kural, mantığınızdaki Python yan etkilerine güvenmekten kaçınmak ve bunları yalnızca izlemelerinizde hata ayıklamak için kullanmaktır.

Örnek 4: Genel bir Python listesini işlemek

Aşağıdaki TF1.x kodu, yalnızca geçerli eğitim adımı tarafından oluşturulan kayıpların listesini korumak için kullandığı genel bir kayıp listesi kullanır. Kayıpları listeye ekleyen Python mantığının, oturumun kaç eğitim adımı için çalıştırıldığına bakılmaksızın yalnızca bir kez çağrılacağını unutmayın.

all_losses = []

class Model():
  def __call__(...):
    ...
    all_losses.append(regularization_loss)
    all_losses.append(label_loss_a)
    all_losses.append(label_loss_b)
    ...

g = tf.Graph()
with g.as_default():
  ...
  # initialize all objects
  model = Model()
  optimizer = ...
  ...
  # train step
  model(...)
  total_loss = tf.reduce_sum(all_losses)
  optimizer.minimize(total_loss)
  ...
...
sess = tf.compat.v1.Session(graph=g)
sess.run(...)  

Bununla birlikte, bu Python mantığı, istekli yürütme ile saf bir şekilde TF2 ile eşlenirse, küresel kayıp listesi, her eğitim adımında kendisine eklenen yeni değerlere sahip olacaktır. Bu, daha önce listenin yalnızca mevcut eğitim adımındaki kayıpları içermesini bekleyen eğitim adımı kodunun, şimdi o ana kadar çalıştırılan tüm eğitim adımlarındaki kayıpların listesini gördüğü anlamına gelir. Bu, istenmeyen bir davranış değişikliğidir ve listenin her adımın başında temizlenmesi veya eğitim adımına yerel hale getirilmesi gerekir.

all_losses = []

class Model():
  def __call__(...):
    ...
    all_losses.append(regularization_loss)
    all_losses.append(label_loss_a)
    all_losses.append(label_loss_b)
    ...

# initialize all objects
model = Model()
optimizer = ...

def train_step(...)
  ...
  model(...)
  total_loss = tf.reduce_sum(all_losses) # global list is never cleared,
  # Accidentally accumulates sum loss across all training steps
  optimizer.minimize(total_loss)
  ...

Model 2: TF1.x'teki her adımda yeniden hesaplanması amaçlanan bir sembolik tensör, istekli duruma geçerken yanlışlıkla ilk değerle önbelleğe alınır.

Bu model genellikle kodunuzun tf.functions dışında hevesle yürütülürken sessizce hatalı davranmasına neden olur, ancak ilk değer önbelleğe alma bir tf.function içinde gerçekleşirse bir InaccessibleTensorError oluşturur. Ancak, yukarıdaki Model 1'den kaçınmak için, genellikle yanlışlıkla kodunuzu, bu ilk değer önbelleğe alma, bir hata oluşturabilecek herhangi bir tf.function dışında gerçekleşecek şekilde yapılandıracağınızı unutmayın. Bu nedenle, programınızın bu kalıba duyarlı olabileceğini biliyorsanız, ekstra özen gösterin.

Bu kalıbın genel çözümü, değeri yanlışlıkla önbelleğe almak yerine her seferinde yeniden hesaplandığından emin olmak için kodu yeniden yapılandırmak veya gerekirse Python çağrılabilirlerini kullanmaktır.

Örnek 1: Öğrenme hızı/hiperparametre/vb. küresel adıma bağlı programlar

Aşağıdaki kod parçacığında beklenti, oturum her çalıştırıldığında en son global_step değerinin okunacağı ve yeni bir öğrenme oranının hesaplanacağı yönündedir.

g = tf.Graph()
with g.as_default():
  ...
  global_step = tf.Variable(0)
  learning_rate = 1.0 / global_step
  opt = tf.compat.v1.train.GradientDescentOptimizer(learning_rate)
  ...
  global_step.assign_add(1)
...
sess = tf.compat.v1.Session(graph=g)
sess.run(...)

Ancak, istekli olmaya geçiş yapmaya çalışırken, amaçlanan programı takip etmek yerine, öğrenme oranının yalnızca bir kez hesaplanıp sonra yeniden kullanılmasıyla sonuçlanmamaya dikkat edin:

global_step = tf.Variable(0)
learning_rate = 1.0 / global_step # Wrong! Only computed once!
opt = tf.keras.optimizers.SGD(learning_rate)

def train_step(...):
  ...
  opt.apply_gradients(...)
  global_step.assign_add(1)
  ...

Bu özel örnek yaygın bir model olduğundan ve optimize ediciler her eğitim adımından ziyade yalnızca bir kez başlatılmalıdır, TF2 optimize edicileri öğrenme hızı ve diğer hiperparametreler için argümanlar olarak tf.keras.optimizers.schedules.LearningRateSchedule programlarını veya Python çağrılabilirlerini destekler.

Örnek 2: Nesne öznitelikleri olarak atanan ve ardından işaretçi aracılığıyla yeniden kullanılan sembolik rasgele sayı başlatmaları, istekliye geçerken yanlışlıkla önbelleğe alınır

Aşağıdaki NoiseAdder modülünü göz önünde bulundurun:

class NoiseAdder(tf.Module):
  def __init__(shape, mean):
    self.noise_distribution = tf.random.normal(shape=shape, mean=mean)
    self.trainable_scale = tf.Variable(1.0, trainable=True)

  def add_noise(input):
    return (self.noise_distribution + input) * self.trainable_scale

TF1.x'te aşağıdaki gibi kullanmak, oturum her çalıştırıldığında yeni bir rastgele gürültü tensörü hesaplayacaktır:

g = tf.Graph()
with g.as_default():
  ...
  # initialize all variable-containing objects
  noise_adder = NoiseAdder(shape, mean)
  ...
  # computation pass
  x_with_noise = noise_adder.add_noise(x)
  ...
...
sess = tf.compat.v1.Session(graph=g)
sess.run(...)

Ancak, noise_adder başlangıçta başlatılması, noise_distribution yalnızca bir kez hesaplanmasına ve tüm eğitim adımları için donmasına neden olacaktır:

...
# initialize all variable-containing objects
noise_adder = NoiseAdder(shape, mean) # Freezes `self.noise_distribution`!
...
# computation pass
x_with_noise = noise_adder.add_noise(x)
...

Bunu düzeltmek için, her seferinde aynı tensör nesnesine başvurmak yerine, her yeni bir rastgele tensör gerektiğinde tf.random.normal öğesini çağırmak için NoiseAdder refactor.

class NoiseAdder(tf.Module):
  def __init__(shape, mean):
    self.noise_distribution = lambda: tf.random.normal(shape=shape, mean=mean)
    self.trainable_scale = tf.Variable(1.0, trainable=True)

  def add_noise(input):
    return (self.noise_distribution() + input) * self.trainable_scale

Model 3: TF1.x kodu doğrudan dayanır ve tensörleri ada göre arar

TF1.x kod testlerinin bir grafikte hangi tensörlerin veya işlemlerin bulunduğunu kontrol etmeye dayanması yaygındır. Bazı ender durumlarda, modelleme kodu da bu aramaları ada göre kullanır.

tf.function dışında hevesle yürütülürken tensör adları oluşturulmaz, bu nedenle tf.Tensor.name öğesinin tüm kullanımları bir tf.Tensor.name içinde tf.function . Gerçek oluşturulan adların, aynı tf.function içinde bile TF1.x ve TF2 arasında büyük olasılıkla farklılık gösterebileceğini ve API garantilerinin, oluşturulan adların TF sürümlerinde kararlılığını sağlamadığını unutmayın.

Model 4: TF1.x oturumu, oluşturulan grafiğin yalnızca bir kısmını seçerek çalıştırır

TF1.x'te, bir grafik oluşturabilir ve ardından grafikteki her işlemin çalıştırılmasını gerektirmeyen bir dizi girdi ve çıktı seçerek bunun yalnızca bir alt kümesini seçerek bir oturumla çalıştırmayı seçebilirsiniz.

Örneğin, tek bir grafiğin içinde hem bir oluşturucu hem de bir ayırıcı olabilir ve yalnızca ayırıcıyı eğitmek veya yalnızca oluşturucuyu eğitmek arasında geçiş yapmak için ayrı tf.compat.v1.Session.run çağrıları kullanabilirsiniz.

TF2'de, tf.function otomatik kontrol bağımlılıkları ve istekli yürütme nedeniyle, tf.function izlerinin seçici budaması yoktur. Tüm değişken güncellemelerini içeren tam bir grafik, örneğin yalnızca ayırıcının veya üretecin çıktısı tf.function .

Bu nedenle, ya programın farklı bölümlerini içeren birden çok tf.function s kullanmanız ya da yalnızca gerçekten çalıştırmak istediğiniz şeyleri yürütmek için tf.function için koşullu bir argüman kullanmanız gerekir.

Koleksiyon Kaldırma

İstekli yürütme etkinleştirildiğinde, grafik koleksiyonuyla ilgili compat.v1 API'leri ( tf.compat.v1.trainable_variables gibi başlık altındaki koleksiyonlara okuyan veya bunlara yazanlar dahil) artık kullanılamaz. Bazıları ValueError s'yi yükseltebilirken, diğerleri sessizce boş listeler döndürebilir.

TF1.x'teki koleksiyonların en standart kullanımı, başlatıcıları, genel adımı, ağırlıkları, düzenlileştirme kayıplarını, model çıktı kayıplarını ve BatchNormalization katmanlarından çalıştırılması gereken değişken güncellemelerini korumaktır.

Bu standart kullanımların her birini işlemek için:

  1. Başlatıcılar - Yoksay. İstekli yürütme etkinken manuel değişken başlatma gerekli değildir.
  2. Genel adım - Taşıma talimatları için tf.compat.v1.train.get_or_create_global_step belgelerine bakın.
  3. Ağırlıklar - Model eşleme kılavuzundaki yönergeleri izleyerek modellerinizi tf.Module s/ tf.keras.layers.Layer s/ tf.keras.Model s ile eşleştirin ve ardından tf.module.trainable_variables .
  4. Düzenlileştirme kayıpları - Model haritalama kılavuzundaki yönergeleri izleyerek modellerinizi tf.Module s/ tf.keras.layers.Layer s/ tf.keras.Model s ile eşleştirin ve ardından tf.keras.losses kullanın. Alternatif olarak, düzenlileştirme kayıplarınızı manuel olarak da takip edebilirsiniz.
  5. Model çıktı kayıpları - tf.keras.Model kayıp yönetimi mekanizmalarını kullanın veya koleksiyon kullanmadan kayıplarınızı ayrı olarak izleyin.
  6. Ağırlık güncellemeleri - Bu koleksiyonu yoksay. İstekli yürütme ve tf.function (imza ve otomatik kontrol bağımlılıklarıyla birlikte), tüm değişken güncellemelerin otomatik olarak çalıştırılacağı anlamına gelir. Bu nedenle, tüm ağırlık güncellemelerini en sonunda açıkça çalıştırmanız gerekmeyecek, ancak bunun, ağırlık güncellemelerinin, kontrol bağımlılıklarını nasıl kullandığınıza bağlı olarak, TF1.x kodunuzda olduğundan farklı bir zamanda gerçekleşebileceği anlamına geldiğini unutmayın.
  7. Özetler - Taşıma özeti API kılavuzuna bakın.

Daha karmaşık koleksiyon kullanımı (özel koleksiyonlar kullanmak gibi), kendi global mağazalarınızı sürdürmek veya global mağazalara hiç güvenmemek için kodunuzu yeniden düzenlemenizi gerektirebilir.

ReferenceVariables yerine ResourceVariables

ResourceVariables , ReferenceVariables daha güçlü okuma-yazma tutarlılığı garantilerine sahiptir. Bu, değişkenlerinizi kullanırken önceki bir yazmanın sonucunu gözlemleyip gözlemlemeyeceğiniz konusunda anlambilim hakkında daha öngörülebilir, daha kolay akıl yürütmeye yol açar. Bu değişikliğin, mevcut kodun hata vermesine veya sessizce kırılmasına neden olması pek olası değildir.

Ancak, bu daha güçlü tutarlılık garantilerinin belirli programınızın bellek kullanımını artırması pek olası olmasa da mümkündür. Durumun böyle olduğunu düşünüyorsanız lütfen bir sorun bildirin. Ek olarak, değişken okumalarına karşılık gelen bir grafikte operatör adlarıyla tam dize karşılaştırmalarına dayanan birim testleriniz varsa, kaynak değişkenlerini etkinleştirmenin bu operatörlerin adını biraz değiştirebileceğini unutmayın.

Bu davranış değişikliğinin kodunuz üzerindeki etkisini izole etmek için, istekli yürütme devre dışı bırakılırsa, bu davranış değişikliğini genel olarak devre dışı bırakmak veya etkinleştirmek için tf.compat.v1.disable_resource_variables() ve tf.compat.v1.enable_resource_variables() kullanabilirsiniz. İstekli yürütme etkinleştirilirse, ResourceVariables her zaman kullanılacaktır.

Kontrol akışı v2

TF1.x'te, tf.cond ve tf.while_loop gibi kontrol akışı operasyonları Switch , Merge vb. gibi satır içi düşük seviyeli operasyonlar. TF2, her dal ve destek için ayrı tf.function izlemeleriyle uygulanan gelişmiş fonksiyonel kontrol akışı operasyonları sağlar. üst düzey farklılaşma

Bu davranış değişikliğinin kodunuz üzerindeki etkisini izole etmek için, istekli yürütme devre dışı bırakılırsa, bu davranış değişikliğini genel olarak devre dışı bırakmak veya etkinleştirmek için tf.compat.v1.disable_control_flow_v2() ve tf.compat.v1.enable_control_flow_v2() kullanabilirsiniz. Ancak, kontrol akışı v2'yi yalnızca istekli yürütme de devre dışı bırakılmışsa devre dışı bırakabilirsiniz. Etkinleştirilirse, kontrol akışı v2 her zaman kullanılacaktır.

Bu davranış değişikliği, bir düz grafik yerine birkaç iç içe işlev izi içereceğinden, kontrol akışını kullanan oluşturulan TF programlarının yapısını önemli ölçüde değiştirebilir. Bu nedenle, üretilen izlerin tam semantiğine yüksek oranda bağımlı olan herhangi bir kod, bazı değişiklikler gerektirebilir. Bu içerir:

  • Operatör ve tensör adlarına dayanan kod
  • Bir TensorFlow kontrol akışı dalında o dalın dışından oluşturulan tensörlere atıfta bulunan kod. Bunun bir InaccessibleTensorError üretmesi muhtemeldir

Bu davranış değişikliğinin performans açısından nötrden pozitife doğru olması amaçlanmıştır, ancak v2 kontrol akışının sizin için TF1.x kontrol akışından daha kötü performans gösterdiği bir sorunla karşılaşırsanız, lütfen çoğaltma adımlarıyla ilgili bir sorun bildirin.

TensorShape API davranış değişiklikleri

TensorShape sınıfı, tf.compat.v1.Dimension nesneleri yerine int s'yi tutacak şekilde basitleştirildi. Yani int almak için .value çağırmaya gerek yoktur.

Bireysel tf.compat.v1.Dimension nesnelerine tf.TensorShape.dims erişilebilir.

Bu davranış değişikliğinin kodunuz üzerindeki etkisini izole etmek için, bu davranış değişikliğini genel olarak devre dışı bırakmak veya etkinleştirmek için tf.compat.v1.disable_v2_tensorshape() ve tf.compat.v1.enable_v2_tensorshape() kullanabilirsiniz.

Aşağıdakiler, TF1.x ve TF2 arasındaki farkları göstermektedir.

import tensorflow as tf
# Create a shape and choose an index
i = 0
shape = tf.TensorShape([16, None, 256])
shape
-yer tutucu21 l10n-yer
TensorShape([16, None, 256])

TF1.x'te buna sahip olsaydınız:

value = shape[i].value

Ardından bunu TF2'de yapın:

value = shape[i]
value
tutucu24 l10n-yer
16

TF1.x'te buna sahip olsaydınız:

for dim in shape:
    value = dim.value
    print(value)

Ardından, bunu TF2'de yapın:

for value in shape:
  print(value)
tutucu27 l10n-yer
16
None
256

Bunu TF1.x'te aldıysanız (veya başka bir boyut yöntemi kullandıysanız):

dim = shape[i]
dim.assert_is_compatible_with(other_dim)

Ardından bunu TF2'de yapın:

other_dim = 16
Dimension = tf.compat.v1.Dimension

if shape.rank is None:
  dim = Dimension(None)
else:
  dim = shape.dims[i]
dim.is_compatible_with(other_dim) # or any other dimension method
True
-yer tutucu31 l10n-yer
shape = tf.TensorShape(None)

if shape:
  dim = shape.dims[i]
  dim.is_compatible_with(other_dim) # or any other dimension method

Bir tf.TensorShape boole değeri, sıralama biliniyorsa True , değilse False olur.

print(bool(tf.TensorShape([])))      # Scalar
print(bool(tf.TensorShape([0])))     # 0-length vector
print(bool(tf.TensorShape([1])))     # 1-length vector
print(bool(tf.TensorShape([None])))  # Unknown-length vector
print(bool(tf.TensorShape([1, 10, 100])))       # 3D tensor
print(bool(tf.TensorShape([None, None, None]))) # 3D tensor with no known dimensions
print()
print(bool(tf.TensorShape(None)))  # A tensor with unknown rank.
tutucu33 l10n-yer
True
True
True
True
True
True

False

TensorShape değişikliklerinden kaynaklanan olası hatalar

TensorShape davranış değişikliklerinin kodunuzu sessizce bozması pek olası değildir. Ancak, şekille ilgili kodun AttributeError s'yi int s olarak yükseltmeye başladığını görebilirsiniz ve None s, tf.compat.v1.Dimension s ile aynı özelliklere sahip değildir. Aşağıda bu AttributeError 'lara ilişkin bazı örnekler verilmiştir:

try:
  # Create a shape and choose an index
  shape = tf.TensorShape([16, None, 256])
  value = shape[0].value
except AttributeError as e:
  # 'int' object has no attribute 'value'
  print(e)
'int' object has no attribute 'value'
yer tutucu36 l10n-yer
try:
  # Create a shape and choose an index
  shape = tf.TensorShape([16, None, 256])
  dim = shape[1]
  other_dim = shape[2]
  dim.assert_is_compatible_with(other_dim)
except AttributeError as e:
  # 'NoneType' object has no attribute 'assert_is_compatible_with'
  print(e)
'NoneType' object has no attribute 'assert_is_compatible_with'

Değere Göre Tensör Eşitliği

Değişkenler ve tensörler üzerindeki ikili == ve != operatörleri, TF1.x'teki gibi nesne referansıyla karşılaştırmak yerine TF2'deki değere göre karşılaştırılacak şekilde değiştirildi. Ek olarak, tensörler ve değişkenler artık doğrudan hash edilemez veya setlerde veya dict anahtarlarında kullanılamaz, çünkü onları değere göre hash etmek mümkün olmayabilir. Bunun yerine, tensör veya değişkene kolay erişilebilir bir başvuru elde etmek için kullanabileceğiniz bir .ref() yöntemini gösterirler.

Bu davranış değişikliğinin etkisini izole etmek için, bu davranış değişikliğini genel olarak devre dışı bırakmak veya etkinleştirmek için tf.compat.v1.disable_tensor_equality() ve tf.compat.v1.enable_tensor_equality() kullanabilirsiniz.

Örneğin, TF1.x'te, == operatörünü kullandığınızda aynı değere sahip iki değişken false döndürür:

tf.compat.v1.disable_tensor_equality()
x = tf.Variable(0.0)
y = tf.Variable(0.0)

x == y
tutucu39 l10n-yer
False

TF2'de tensör eşitlik kontrolleri etkinken, x == y True değerini döndürür.

tf.compat.v1.enable_tensor_equality()
x = tf.Variable(0.0)
y = tf.Variable(0.0)

x == y
tutucu41 l10n-yer
<tf.Tensor: shape=(), dtype=bool, numpy=True>

Bu nedenle, TF2'de, nesne referansına göre karşılaştırmanız gerekiyorsa, is ve is not not'u kullandığınızdan emin olun.

tf.compat.v1.enable_tensor_equality()
x = tf.Variable(0.0)
y = tf.Variable(0.0)

x is y
tutucu43 l10n-yer
False

Karma tensörler ve değişkenler

TF1.x davranışlarıyla, set ve dict tuşları gibi karma gerektiren veri yapılarına doğrudan değişkenler ve tensörler ekleyebiliyordunuz.

tf.compat.v1.disable_tensor_equality()
x = tf.Variable(0.0)
set([x, tf.constant(2.0)])
tutucu45 l10n-yer
{<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=2.0>}

Bununla birlikte, tensör eşitliği etkinleştirilmiş TF2'de, == ve != operatör anlambiliminin değer eşitlik kontrollerine dönüşmesi nedeniyle tensörler ve değişkenler hash edilemez hale getirilir.

tf.compat.v1.enable_tensor_equality()
x = tf.Variable(0.0)

try:
  set([x, tf.constant(2.0)])
except TypeError as e:
  # TypeError: Variable is unhashable. Instead, use tensor.ref() as the key.
  print(e)
tutucu47 l10n-yer
Variable is unhashable. Instead, use tensor.ref() as the key.

Bu nedenle, TF2'de anahtar veya set içeriği olarak tensör veya değişken nesneler kullanmanız gerekiyorsa, anahtar olarak kullanılabilecek bir hashable referansı almak için tensor.ref() kullanabilirsiniz:

tf.compat.v1.enable_tensor_equality()
x = tf.Variable(0.0)

tensor_set = set([x.ref(), tf.constant(2.0).ref()])
assert x.ref() in tensor_set

tensor_set
tutucu49 l10n-yer
{<Reference wrapping <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.0>>,
 <Reference wrapping <tf.Tensor: shape=(), dtype=float32, numpy=2.0>>}

Gerekirse, referanstan tensörü veya değişkeni reference.deref() kullanarak da alabilirsiniz:

referenced_var = x.ref().deref()
assert referenced_var is x
referenced_var
tutucu51 l10n-yer
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.0>

Kaynaklar ve daha fazla okuma

  • TF1.x'ten TF2'ye geçiş hakkında daha fazla bilgi için TF2'ye Geçiş bölümünü ziyaret edin.
  • TF1.x modellerinizi doğrudan TF2'de çalışacak şekilde eşleştirme hakkında daha fazla bilgi edinmek için model eşleştirme kılavuzunu okuyun.