Modern konferanslar, squeezenet, Xception, Keras ve TPU'lar

1. Genel Bakış

Bu laboratuvarda, modern konvolüsyon mimarisi hakkında bilgi edinecek ve "squeezenet" adlı basit ancak etkili bir konvolüsyon ağı uygulamak için edindiğiniz bilgileri kullanacaksınız.

Bu laboratuvar, derin öğrenme hakkında bilgi edinen geliştiriciler için iyi bir başlangıç noktası olan, konvolüsyonel sinir ağlarıyla ilgili gerekli teorik açıklamaları içerir.

Bu laboratuvar, "TPU'da Keres" serisinin 4. bölümüdür. Bunları aşağıdaki sırayla veya ayrı ayrı yapabilirsiniz.

ca8cc21f6838eccc.png

Neler öğreneceksiniz?

  • Keras'ın işlevsel tarzında ustalaşmak için
  • Squeezenet mimarisini kullanarak model oluşturmak için
  • Hızlı bir şekilde eğitmek ve mimarinizde iterasyon yapmak için TPU'lardan yararlanmak
  • Veri artırmayı tf.data.dataset ile uygulamak için
  • TPU'da önceden eğitilmiş büyük bir modelde (Xception) ince ayar yapmak için

Geri bildirim

Bu kod laboratuvarında bir sorun görürseniz lütfen bize bildirin. Geri bildirimlerinizi, GitHub sorunları [feedback link] sayfasında bulabilirsiniz.

2. Google Colaboratory hızlı başlangıç kılavuzu

Bu laboratuvarda Google Ortak Çalışması kullanılmaktadır ve sizin herhangi bir kurulum yapmanız gerekmez. Colaboratory, eğitim amaçlı bir online not defteri platformudur. Ücretsiz CPU, GPU ve TPU eğitimi sunar.

688858c21e3beff2.png

Bu örnek not defterini açıp Colaboratory hakkında bilgi edinmek için birkaç hücreyi inceleyebilirsiniz.

c3df49e90e5a654f.png Welcome to Colab.ipynb

TPU arka ucu seçin

8832c6208c99687d.png

Colab menüsünde Çalışma zamanı > Çalışma zamanı türünü değiştir'i ve ardından TPU'yu seçin. Bu kod laboratuvarında, donanım hızlandırmalı eğitim için desteklenen güçlü bir TPU (Tensor İşleme Birimi) kullanacaksınız. Çalışma zamanı bağlantısı ilk çalıştırma sırasında otomatik olarak kurulacaktır. İsterseniz sağ üst köşedeki "Bağlan" düğmesini de kullanabilirsiniz.

Not defteri yürütme

76d05caa8b4db6da.png

Bir hücreyi tıklayıp Üst Karakter-ENTER tuşlarını kullanarak hücreleri tek tek yürütün. Not defterinin tamamını Çalışma zamanı > Tümünü çalıştır'ı kullanarak da çalıştırabilirsiniz.

İçindekiler

429f106990037ec4.png

Tüm not defterlerinde bir içindekiler listesi bulunur. Soldaki siyah oku kullanarak açabilirsiniz.

Gizli hücreler

edc3dba45d26f12a.png

Bazı hücrelerde yalnızca başlık gösterilir. Bu, Colab'e özel bir not defteri özelliğidir. Üzerlerini çift tıklayarak içindeki kodu görebilirsiniz, ancak bu durum genellikle pek de ilgi çekici değildir. Genellikle destek veya görselleştirme işlevleridir. İçerideki işlevlerin tanımlanması için bu hücreleri çalıştırmanız gerekir.

Kimlik Doğrulama

cdd4b41413100543.png

Yetkili bir hesapla kimlik doğrulama yaptığınızda Colab'ın gizli Google Cloud Storage paketlerinize erişmesi mümkündür. Yukarıdaki kod snippet'i bir kimlik doğrulama sürecini tetikler.

3. [BİLGİ] Tensor İşleme Birimleri (TPU'lar) nedir?

Özet

f88cf6facfc70166.png

Keras'ta bir modeli TPU üzerinde eğitmek için kullanılan kod (ve TPU yoksa GPU veya CPU'ya geri dönme):

try: # detect TPUs
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
    strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines

# use TPUStrategy scope to define model
with strategy.scope():
  model = tf.keras.Sequential( ... )
  model.compile( ... )

# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)

Bu eğitimde, çiçek sınıflandırıcıyı etkileşimli hızlarda (eğitim çalıştırma başına dakika) oluşturmak ve optimize etmek için TPU'ları kullanacağız.

688858c21e3beff2.png

Neden TPU'ları kullanmalısınız?

Modern GPU'lar, programlanabilir "çekirdekler" etrafında düzenlenir. Bu çekirdekler, 3D oluşturma, derin öğrenme, fiziksel simülasyonlar gibi çeşitli görevlerin üstesinden gelmelerini sağlayan çok esnek bir mimaridir. Öte yandan TPU'lar, klasik bir vektör işlemcisini özel bir matris çarpma birimi ile eşleştirir ve büyük matris çarpmalarının hakim olduğu tüm görevlerde (ör. sinir ağları) mükemmel performans gösterir.

8eb3e718b8e2ed08.png

Çizim: Bir matris çarpımı olarak yoğun bir nöral ağ katmanı ve aynı anda nöral ağ üzerinden işlenen sekiz görüntüden oluşan bir katman. Bir resmin tüm piksel değerlerinin ağırlıklı toplamını gerçekten yaptığını doğrulamak için lütfen bir satır x sütun çarpımı yapın. Dalgalar katmanları, biraz daha karmaşık olsa da matris çarpımları olarak da temsil edilebilir ( 1. bölümde açıklama).

Donanım

MXU ve VPU

TPU v2 çekirdeği, matris çarpma işlemlerini yürüten bir Matris Çarpma Birimi (MXU) ve etkinleştirme, yumuşak maksimum vb. gibi diğer tüm görevler için bir Vektör İşleme Birimi'nden (VPU) oluşur. VPU, float32 ve int32 hesaplamalarını yönetir. Diğer yandan MXU, karma hassasiyetli 16-32 bit kayan nokta biçiminde çalışır.

7d68944718f76b18.png

Karma hassasiyetli kayan nokta ve bfloat16

MXU, bfloat16 girişlerini ve float32 çıkışlarını kullanarak matris çarpımlarını hesaplar. Ara toplama işlemleri, float32 hassasiyetinde gerçekleştirilir.

19c5fc432840c714.png

Nöral ağ eğitimi, genellikle azaltılmış kayan nokta hassasiyeti nedeniyle ortaya çıkan gürültüye karşı dayanıklıdır. Gürültünün, optimizasyon aracının yakınsamasına yardımcı olduğu durumlar da vardır. 16 bit kayan nokta hassasiyeti, hesaplamaları hızlandırmak için geleneksel olarak kullanılmıştır ancak float16 ve float32 biçimlerinin aralıkları çok farklıdır. Hassasiyetin float32'den float16'ya düşürülmesi, genellikle fazla ve az akışlara yol açar. Bunun için çeşitli çözümler mevcuttur ancak float16'nın çalışması için genellikle ek işlemler gerekir.

Google'ın TPU'larda bfloat16 biçimini kullanıma sunmasının nedeni budur. bfloat16, float32 ile tam olarak aynı üs bitlerine ve aralığa sahip, kısaltılmış bir float32'dir. Buna ek olarak TPU'lar, matris çarpımlarını bfloat16 girişleriyle ancak float32 çıkışlarıyla karışık hassasiyetle hesaplıyordur, böylece daha düşük hassasiyetten faydalanmak için genellikle hiçbir kod değişikliği gerekmez.

Sistolik dizi

MXU, "sistolik dizi" adı verilen bir mimari kullanarak donanımda matris çarpımlarını uygular. Bu mimaride veri öğeleri bir dizi donanım hesaplama biriminden geçer. (Tıpta "sistolik" terimi, kalp kasılmalarını ve buradan veri akışını ifade eder.)

Matris çarpımının temel öğesi, bir matristeki satır ile diğer matristeki sütun arasındaki nokta çarpımıdır (bu bölümün üst kısmındaki resme bakın). Y=X*W matris çarpımı için sonucun bir öğesi şöyle olur:

Y[2,0] = X[2,0]*W[0,0] + X[2,1]*W[1,0] + X[2,2]*W[2,0] + ... + X[2,n]*W[n,0]

GPU'da bu nokta çarpımı bir GPU "çekirdeği"ne programlanır ve ardından elde edilen matrisin her değerini aynı anda hesaplamaya çalışmak için paralel olarak kullanılabilen tüm "çekirdeklerde" yürütülür. Elde edilen matris 128x128 boyutundaysa 128x128=16K "çekirdek"in kullanılabilir olması gerekir. Bu da genellikle mümkün değildir. En büyük GPU'lar yaklaşık 4.000 çekirdeğe sahiptir. Öte yandan TPU, MXU'daki işlem birimleri için minimum düzeyde donanım kullanır: Yalnızca bfloat16 x bfloat16 => float32 çarpım toplayıcılar, başka bir şey yoktur. Bunlar o kadar küçüktür ki TPU, bunların 16K'sını 128x128 MXU'ya uygulayabilir ve bu matris çarpımını tek seferde işleyebilir.

f1b283fc45966717.gif

Görsel: MXU sistolik dizisi. Compute elemanları çarpma toplayıcılarıdır. Bir matrisin değerleri diziye yüklenir (kırmızı noktalar). Diğer matrisin değerleri diziden akar (gri noktalar). Dikey çizgiler, değerleri yukarı doğru iletir. Yatay çizgiler kısmi toplamları yayar. Veriler dizi boyunca akarken sağ taraftan çıkan matris çarpımının sonucunu aldığınızı doğrulamak kullanıcıya bırakılmıştır.

Buna ek olarak, skaler çarpımlar bir MXU'da hesaplanırken ara toplamlar bitişik hesaplama birimleri arasında kolayca akar. Depolanmaları ve belleğe, hatta bir kayıt dosyasına/belgeden alınmalarına gerek yoktur. Sonuçta TPU sistolik dizi mimarisi önemli bir yoğunluk ve güç avantajına sahip olmanın yanı sıra matris çarpımlarını hesaplarken GPU'ya göre göz ardı edilemez bir hız avantajına sahiptir.

Cloud TPU

Google Cloud Platform'da bir "Cloud TPU v2" isteğinde bulunduğunuzda, PCI'ye bağlı TPU kartı olan bir sanal makine elde edersiniz. TPU kartında dört adet çift çekirdekli TPU çipi bulunur. Her TPU çekirdeğinde bir VPU (Vektör İşleme Birimi) ve 128x128 MXU (Matris Çarpma Birimi) bulunur. Bu "Cloud TPU" daha sonra genellikle ağ üzerinden istek gönderen sanal makineye bağlanır. Tam tablo şöyle görünür:

dfce5522ed644ece.png

Çizim: Ağa bağlı "Cloud TPU" hızlandırıcıya sahip sanal makineniz. "Cloud TPU" da üzerinde dört çift çekirdekli TPU çipi bulunan PCI'ye bağlı TPU kartı bulunan bir sanal makineden oluşur.

TPU kapsülleri

Google'ın veri merkezlerinde TPU'lar, yüksek performanslı bilgi işlem (HPC) bağlantısına bağlanır. Bu bağlantı, TPU'ları tek bir çok büyük hızlandırıcı gibi gösterebilir. Google bu kapsülleri kapsül olarak adlandırır ve 512 TPU v2 çekirdeği veya 2.048 TPU v3 çekirdeği içerebilir.

2ec1e0d341e7fc34.jpeg

Çizim: TPU v3 kapsülü. HPC ara bağlantısı üzerinden bağlanan TPU kartları ve rafları.

Eğitim sırasında, tamamen azaltma algoritması kullanılarak TPU çekirdekleri arasında gradyanlar değiştirilir ( tüm azaltmaların iyi açıklamasını burada bulabilirsiniz). Eğitilen model, büyük toplu boyutlarda eğitim alarak donanımdan yararlanabilir.

d97b9cc5d40fdb1d.gif

Çizim: Google TPU'nun 2D toroidal örgü HPC ağında tamamen azaltma algoritması kullanılarak eğitim sırasında gradyanların senkronizasyonu.

Yazılım

Büyük gruplar için eğitim

TPU'lar için ideal toplu boyut, TPU çekirdeği başına 128 veri öğesidir ancak donanım, TPU çekirdeği başına 8 veri öğesi ile bile iyi bir kullanım gösterebilir. Bir Cloud TPU'nun 8 çekirdeğe sahip olduğunu unutmayın.

Bu kod laboratuvarında Keras API'yi kullanacağız. Keras'ta belirttiğiniz grup, TPU'nun tamamı için genel grup boyutudur. Gruplarınız otomatik olarak 8'e bölünür ve TPU'nun 8 çekirdeğinde çalıştırılır.

da534407825f01e3.png

Ek performans ipuçları için TPU Performans Kılavuzu'na bakın. Çok büyük toplu reklam boyutları için bazı modellerde özel dikkat gösterilmesi gerekebilir. Daha fazla ayrıntı için LARSOptimizer bölümüne bakın.

Perde arkası: XLA

TensorFlow programları, hesaplama grafiklerini tanımlar. TPU, doğrudan Python kodunu değil, Tensorflow programınız tarafından tanımlanan hesaplama grafiğini çalıştırır. Tüm bunların altında XLA (hızlandırılmış doğrusal cebir derleyici) adlı bir derleyici, hesaplama düğümlerinin Tensorflow grafiğini TPU makine koduna dönüştürür. Bu derleyici, kodunuzda ve bellek düzeninizde birçok gelişmiş optimizasyon da gerçekleştirir. İş TPU'ya gönderilirken derleme otomatik olarak gerçekleşir. Derleme zincirinize açıkça XLA eklemeniz gerekmez.

edce61112cd57972.png

Görselleştirme: Tensorflow programınız tarafından tanımlanan hesaplama grafiği, TPU'da çalıştırılmak üzere önce bir XLA (hızlandırılmış doğrusal cebir derleyicisi) temsiline çevrilir, ardından XLA tarafından TPU makine koduna derlenir.

Keras'ta TPU'ları kullanma

TPU'lar, Tensorflow 2.1 itibarıyla Keras API aracılığıyla desteklenmektedir. Keras desteği, TPU'larda ve TPU kapsüllerinde çalışır. TPU, GPU ve CPU'da çalışan bir örnek aşağıda verilmiştir:

try: # detect TPUs
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
    strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines

# use TPUStrategy scope to define model
with strategy.scope():
  model = tf.keras.Sequential( ... )
  model.compile( ... )

# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)

Bu kod snippet'inde:

  • TPUClusterResolver().connect(), ağdaki TPU'yu bulur. Çoğu Google Cloud sisteminde (AI Platform işleri, Colaboratory, Kubeflow, "ctpu up" yardımcı programıyla oluşturulan derin öğrenme sanal makineleri) parametre olmadan çalışır. Bu sistemler, TPU_NAME ortam değişkeni sayesinde TPU'larının nerede olduğunu bilir. TPU'yu manuel olarak oluşturursanız TPU_NAME ortam değişkenini, kullandığınız sanal makinede ayarlayın veya TPUClusterResolver'yi açık parametrelerle çağırın: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy, dağıtım ve "tüm azalt" gradyan senkronizasyon algoritmasını uygulayan kısımdır.
  • Strateji bir kapsam aracılığıyla uygulanır. Model, strategy scope() içinde tanımlanmalıdır.
  • tpu_model.fit işlevi, TPU eğitimi için giriş olarak bir tf.data.Dataset nesnesi bekler.

Genel TPU bağlantı noktası görevleri

  • Bir Tensorflow modeline veri yüklemenin birçok yolu olsa da TPU'lar için tf.data.Dataset API'nin kullanılması gerekir.
  • TPU'lar çok hızlıdır ve verileri beslemek, bunlarda çalışırken genellikle darboğaz haline gelir. Veri darboğazlarını tespit etmek için kullanabileceğiniz araçlar ve diğer performans ipuçlarını TPU Performans Kılavuzu'nda bulabilirsiniz.
  • int8 veya int16 sayılar int32 olarak değerlendirilir. TPU'nun, 32 bit'ten düşük değerler üzerinde çalışan tamsayı donanımı yoktur.
  • Bazı Tensorflow işlemleri desteklenmez. Listeyi buradan inceleyebilirsiniz. Bu sınırlamanın yalnızca eğitim kodu (ör. modelinizdeki ileri ve geri geçiş) için geçerli olması iyi bir haber. CPU'da yürütüleceğinden, veri giriş ardışık düzeninizdeki tüm Tensorflow işlemlerini kullanmaya devam edebilirsiniz.
  • tf.py_func, TPU'da desteklenmez.

4. [INFO] Nöral ağ sınıflandırıcı 101

Özet

Sonraki paragrafta kalın olarak yazılmış terimlerin tümünü biliyorsanız bir sonraki alıştırmaya geçebilirsiniz. Derin öğrenmeye yeni başlıyorsanız hoş geldiniz. Lütfen okumaya devam edin.

Keras, katman dizisi olarak oluşturulan modeller için Sequential API'yi sunar. Örneğin, üç yoğun katman kullanan bir resim sınıflandırıcı Keras'ta şu şekilde yazılabilir:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=[192, 192, 3]),
    tf.keras.layers.Dense(500, activation="relu"),
    tf.keras.layers.Dense(50, activation="relu"),
    tf.keras.layers.Dense(5, activation='softmax') # classifying into 5 classes
])

# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy']) # % of correct answers

# train the model
model.fit(dataset, ... )

688858c21e3beff2.png

Yoğun nöral ağ

Bu, görüntüleri sınıflandırmak için kullanılan en basit nöral ağdır. Katmanlar halinde düzenlenmiş "nöronlardan" oluşur. Birinci katman giriş verilerini işler ve çıkışlarını diğer katmanlara iletir. Bu katmana "yoğun" denmesinin nedeni, her bir nöronun bir önceki katmandaki tüm nöronlara bağlı olmasıdır.

c21bae6dade487bc.png

Tüm piksellerinin RGB değerlerini uzun bir vektöre düzleştirip giriş olarak kullanarak bir resmi bu tür bir ağa besleyebilirsiniz. Görüntü tanıma için en iyi teknik olmasa da daha sonra bu özelliği iyileştireceğiz.

Nöronlar, aktivasyonlar, RELU

Bir "nöron", tüm girişlerinin ağırlıklı bir toplamını hesaplar, "önyargı" adı verilen bir değer ekler ve sonucu, "etkinleştirme işlevi" olarak adlandırılan bir yöntemle iletir. Ağırlıklar ve önyargılar başlangıçta bilinmez. Bunlar rastgele başlatılır ve nöral ağ birçok bilinen veri üzerinde eğitilerek "öğrenilir".

644f4213a4ee70e5.png

En popüler etkinleştirme işlevi, Doğrulanmış Doğrusal Birim için RELU olarak adlandırılır. Yukarıdaki grafikte de görebileceğiniz gibi bu çok basit bir işlevdir.

Softmax etkinleştirme

Yukarıdaki ağ, çiçekleri 5 kategoriye (gül, lale, karahindiba, papatya, ayçiçeği) sınıflandırdığımız için 5 nöronlu bir katmanla biter. Ara katmanlardaki nöronlar, klasik RELU aktivasyon işlevi kullanılarak etkinleştirilir. Ancak son katmanda, bu çiçeğin gül, lale vb. olma olasılığını temsil eden 0 ile 1 arasındaki sayıları hesaplamak istiyoruz. Bunun için "softmax" adlı bir etkinleştirme işlevi kullanacağız.

Bir vektöre softmax uygulamak için her bir öğenin üstel değeri alınır ve ardından vektör normalleştirilir. Bu işlem genellikle L1 normu (mutlak değerlerin toplamı) kullanılarak yapılır. Böylece, değerler 1'e ulaşır ve olasılık olarak yorumlanabilir.

ef0d98c0952c262d.png d51252f75894479e.gif

Çapraz entropi kaybı

Sinir ağımız artık giriş görüntülerinden tahminler ürettiğine göre, bunların ne kadar iyi olduğunu, yani ağın bize bildirdiği bilgilerle doğru yanıtlar arasındaki mesafeyi (genellikle "etiketler" olarak adlandırılır) ölçmemiz gerekir. Veri kümesindeki tüm resimler için doğru etiketlere sahip olduğumuzu unutmayın.

Herhangi bir mesafe kullanılabilir ancak sınıflandırma sorunları için "çapraz entropi mesafesi" en etkili yöntemdir. Buna hata veya "kayıp" işlevimiz diyeceğiz:

7bdf8753d20617fb.png

Gradyan inişi

Sinir ağının "eğitimi", aslında çapraz entropi kayıp işlevini en aza indirmek amacıyla ağırlıkları ve yanlılıkları ayarlamak için eğitim görüntüleri ve etiketlerinin kullanılması anlamına gelir. İşleyiş şekline bakalım.

Çapraz entropi, eğitim görüntüsünün ağırlıklarının, sapmalarının, piksellerinin ve bilinen sınıfının bir fonksiyonudur.

Çapraz entropi değerinin tüm ağırlıklara ve tüm önyargılara göre kısmi türevlerini hesaplarsak belirli bir resim, etiket ve ağırlıkların ve önyargıların mevcut değeri için hesaplanan bir "eğim" elde ederiz. Milyonlarca ağırlık ve önyargıya sahip olabileceğimizi unutmayın. Bu nedenle, gradyanı hesaplamak çok fazla iş gibi görünebilir. Neyse ki Tensorflow bunu bizim için yapıyor. Bir renk geçişinin matematiksel özelliği, "yukarı"yı işaret etmesidir. Çapraz entropinin düşük olduğu yere gitmek istediğimizden ters yönde ilerliyoruz. Ağırlıkları ve sapmaları gradyanın bir kısmına göre güncelleriz. Ardından, bir eğitim döngüsünde sonraki eğitim resmi ve etiket gruplarını kullanarak aynı işlemi tekrar tekrar yaparız. Bunun, çapraz entropinin minimum düzeyde olduğu bir yere yaklaşacağını umuyoruz, ancak bu minimum değerin benzersiz olduğunu hiçbir şey garanti etmemektedir.

gradient descent2.png

Mini toplanma ve ivme

Renk geçişinizi tek bir örnek resim üzerinde hesaplayıp ağırlıkları ve sapmaları hemen güncelleyebilirsiniz. Ancak, örneğin 128 resimden oluşan bir grup üzerinde bunu yaptığınızda, farklı örnek görüntülerin uyguladığı kısıtlamaları daha iyi temsil eden ve dolayısıyla çözüme daha hızlı yaklaşma olasılığı yüksek bir gradyan elde edilir. Mini grubun boyutu ayarlanabilir bir parametredir.

Bazen "stokastik gradyan azalma" olarak da adlandırılan bu tekniğin daha pratik bir avantajı vardır: Gruplarla çalışmak, daha büyük matrislerle çalışmak anlamına da gelir ve bu matrislerin GPU'larda ve TPU'larda optimizasyonu genellikle daha kolaydır.

Yine de yakınsama biraz karmaşık olabilir ve gradyan vektörü tamamen sıfır ise durabilir. Bu, minimum değer bulduğumuz anlamına mı geliyor? Her zaman değil Bir renk geçişi bileşeni, minimum veya maksimum değerde sıfır olabilir. Milyonlarca öğe içeren bir gradyan vektöründe, bunların tümü sıfır ise her sıfırın minimuma ve hiçbirinin maksimum noktaya karşılık gelme olasılığı oldukça düşüktür. Birçok boyutu olan bir alanda, eyer noktaları oldukça yaygındır ve bunlarla yetinmek istemeyiz.

52e824fe4716c4a0.png

Çizim: Eyer noktası. Gradyan 0'dır ancak tüm yönlerde minimum değildir. (Resim ilişkilendirmesi Wikimedia: By Nicoguaro - Own work, CC BY 3.0)

Çözüm, optimizasyon algoritmasına bir hız eklemektir. Böylece, algoritma durmadan bağlı noktaların ötesine geçebilir.

Sözlük

toplu veya mini toplu: Eğitim, her zaman eğitim verisi ve etiket grupları üzerinde gerçekleştirilir. Bu, algoritmanın yakınlaşmasına yardımcı olur. "Toplu" boyutu, genellikle veri tenörlerinin ilk boyutudur. Örneğin, [100, 192, 192, 3] şeklindeki bir tensör, piksel başına üç değer (RGB) içeren 192x192 piksel boyutunda 100 resim içerir.

Çapraz entropi kaybı: Sınıflandırıcılarda sıklıkla kullanılan özel bir kayıp işlevidir.

Yoğun katman: Her nöronun önceki katmandaki tüm nöronlara bağlı olduğu bir nöron katmanı.

özellikler: Bir nöral ağın girişleri bazen "özellikler" olarak adlandırılır. İyi tahminler elde etmek için veri kümesinin hangi bölümlerinin (veya parça kombinasyonlarının) nöral ağa besleneceğini bulma sanatına "özellik mühendisliği" denir.

labels: "sınıflar"ın başka bir adı veya gözetimli sınıflandırma sorunundaki doğru cevaplar

öğrenme hızı: Ağırlıkların ve sapmaların, eğitim döngüsünün her iterasyonunda güncellendiği gradyanın oranı.

Logitler: Etkinleştirme işlevi uygulanmadan önce bir nöron tabakasının çıkışlarına "logitler" denir. Bu terim, daha önce en popüler aktivasyon fonksiyonu olan "sigmoid işlevi" olarak da bilinen "mantıksal fonksiyon"dan gelir. "Mantıksal işlevden önceki nöron çıkışları" ifadesi "mantıksallar" olarak kısaltıldı.

loss: nöral ağ çıkışlarını doğru yanıtlarla karşılaştıran hata işlevi

nöron: Girişlerinin ağırlıklı toplamını hesaplar, bir ağırlık ekler ve sonucu bir aktivasyon işlevi aracılığıyla iletir.

Tek sıcak kodlama: 5 üzerinden 3. sınıf, 5 öğeli bir vektör olarak kodlanır. 3. öğe 1'dir ve diğer tüm öğeler sıfırdır.

relu: Düzeltilmiş doğrusal birim. Nöronlar için popüler bir aktivasyon işlevidir.

sigmoid: Eskiden popüler olan ve özel durumlarda hâlâ yararlı olan başka bir etkinleştirme işlevidir.

softmax: Bir vektör üzerinde işlem yapan, en büyük bileşen ile diğer tüm bileşenler arasındaki farkı artıran ve ayrıca olasılık vektörü olarak yorumlanabilmesi için vektörün toplamını 1 olacak şekilde normalleştiren özel bir etkinleştirme işlevidir. Sınıflandırıcılarda son adım olarak kullanılır.

tensör: "Tensör", matris gibidir ancak isteğe bağlı sayıda boyuta sahiptir. 1 boyutlu bir tensör bir vektördür. 2 boyutlu tensör, bir matristir. Ayrıca 3, 4, 5 veya daha fazla boyuta sahip tensörlere sahip olabilirsiniz.

5. [BİLGİ] Evrişimli nöral ağlar

Özet

Sonraki paragrafta kalın olarak yazılmış terimlerin tümünü biliyorsanız bir sonraki alıştırmaya geçebilirsiniz. Devrimsel sinir ağlarını kullanmaya yeni başladıysanız lütfen okumaya devam edin.

convolutional.gif

Görsel: Her biri 4x4x3=48 öğrenilebilir ağırlıktan oluşan iki art arda filtreyle bir resmi filtreleme.

Basit bir konvolüsyonel nöral ağ Keras'ta şöyle görünür:

model = tf.keras.Sequential([
  # input: images of size 192x192x3 pixels (the three stands for RGB channels)
  tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu', input_shape=[192, 192, 3]),
  tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=6, padding='same', activation='relu'),
  tf.keras.layers.Flatten(),
  # classifying into 5 categories
  tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy'])

688858c21e3beff2.png

Konvolüsyonel nöral ağlar 101

Bir "nöron", bir konvolüsyon ağının katmanında, yalnızca resmin küçük bir bölgesinde, hemen üzerindeki piksellerin ağırlıklı toplamını yapar. Normal yoğun katmandaki bir nöron gibi, bir önyargı ekler ve toplamı bir etkinleştirme işlevi aracılığıyla besler. Daha sonra bu işlem, aynı ağırlıklar kullanılarak resmin tamamında tekrarlanır. Yoğun katmanlarda her nöronun kendi ağırlıklarına sahip olduğunu unutmayın. Burada, ağırlıklardan oluşan tek bir "yama", resim üzerinde her iki yönde de ("büyüme") kayabilir. Çıkış, resimde piksel sayısı kadar değere sahiptir (yine de kenarlarda bazı dolgu yapılması gerekir). 4x4x3=48 ağırlıktan oluşan bir filtre kullanan bir filtreleme işlemidir.

Ancak 48 ağırlık yeterli olmaz. Daha fazla serbestlik derecesi eklemek için aynı işlemi yeni bir ağırlık grubuyla tekrarlarız. Bu işlem, yeni bir filtre çıkışı grubu oluşturur. Bunu, giriş görüntüsündeki R,G,B kanallarına benzetme yaparak bir çıkış "kanalı" olarak adlandıralım.

Screen Shot 2016-07-29 at 16.02.37.png

İki (veya daha fazla) ağırlık grubu, yeni bir boyut ekleyerek tek bir tensör olarak toplanabilir. Bu, bir konvolüsyon katmanı için ağırlık tensörünün genel şeklini verir. Giriş ve çıkış kanallarının sayısı parametre olduğundan, konvolüsyon katmanlarını yığmaya ve zincirlemeye başlayabiliriz.

d1b557707bcd1cb9.png

Görsel: Evrişimli nöral ağ, veri "küplerini" başka veri "küplerine" dönüştürür.

Sıralı konvolüsyon, maksimum havuz hızı

Evrimleri 2 veya 3 adımla yaparak elde edilen veri küpünü yatay boyutlarında da küçültebiliriz. Bunu iki şekilde yapabilirsiniz:

  • Aşamalı konvolüsyon: Yukarıdaki gibi ancak adımı 1'den büyük olan kayan bir filtre
  • Maks. havuz: MAX işlemini uygulayan bir kayan pencere (genellikle 2x2 yamalarda, her 2 pikselde bir tekrarlanır)

2b2d4263bb8470b.gif

Görsel: Hesaplama penceresini 3 piksel kaydırarak daha az çıkış değeri elde edilir. Adımlamalı konvolüsyonlar veya maksimum havuzlama (2 adım kaydırarak 2x2 pencerede maksimum), veri kübünün yatay boyutlarda küçültülmesinin bir yoludur.

Konvolüsyonsal sınıflandırıcı

Son olarak, son veri kübünü düzleştirip yoğun, softmax etkinleştirilmiş bir katmandan besleyerek bir sınıflandırma başlığı ekleriz. Tipik bir konvolüsyonel sınıflandırıcı aşağıdaki gibi görünebilir:

4a61aaffb6cba3d1.png

Görsel: Devrimsel ve yumuşak maksimum katmanları kullanan bir resim sınıflandırıcı. 3x3 ve 1x1 filtreler kullanılır. Maxpool katmanları, en fazla 2x2 veri noktası grubunu alır. Sınıflandırma başlığı, softmax etkinleştirmesine sahip yoğun bir katmanla uygulanmıştır.

Keras'ta

Yukarıda gösterilen konvolüsyon grubu, Keras'ta şu şekilde yazılabilir:

model = tf.keras.Sequential([
  # input: images of size 192x192x3 pixels (the three stands for RGB channels)    
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu', input_shape=[192, 192, 3]),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=16, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=8, padding='same', activation='relu'),
  tf.keras.layers.Flatten(),
  # classifying into 5 categories
  tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy'])

6. [YENİ BİLGİ] Modern konvolüsyon ağları

Özet

7968830b57b708c0.png

Çizim: konvolüsyonlu "modül". Şu anda en iyi seçenek nedir? Maksimum havuz katmanının ardından 1x1 konvolüsyon katmanı mı yoksa farklı bir katman kombinasyonu mu? Tümünü deneyin, sonuçları birleştirin ve ağa karar vermesine izin verin. Sağda: Bu tür modüllerin kullanıldığı " başlangıç" kıvrımlı mimari.

Keras'ta, veri akışının farklı alanlara yayılabileceği modeller oluşturmak için "işlevsel" model stilini kullanmanız gerekir. Örnek:

l = tf.keras.layers # syntax shortcut

y = l.Conv2D(filters=32, kernel_size=3, padding='same',
             activation='relu', input_shape=[192, 192, 3])(x) # x=input image

# module start: branch out
y1 = l.Conv2D(filters=32, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=32, kernel_size=3, padding='same', activation='relu')(y)
y = l.concatenate([y1, y3]) # output now has 64 channels
# module end: concatenation

# many more layers ...

# Create the model by specifying the input and output tensors.
# Keras layers track their connections automatically so that's all that's needed.
z = l.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, z)

688858c21e3beff2.png

Diğer ucuz numaralar

Küçük 3x3 filtreler

40a7b15fb7dbe75c.png

Bu görselde art arda iki tane 3x3 filtresinin sonucunu görüyorsunuz. Hangi veri noktalarının sonuca katkıda bulunduğunu izlemeye çalışın: Bu iki ardışık 3x3 filtre, 5x5 boyutunda bir bölgenin bir kombinasyonunu hesaplar. Bu, 5x5 filtrenin hesaplayacağı kombinasyonla tam olarak aynı değildir ancak art arda iki 3x3 filtre tek bir 5x5 filtreden daha ucuz olduğu için denemeye değer.

1x1 konvolüsyonlar ?

fd7cac16f8ecb423.png

Matematiksel açıdan "1x1" konvolüsyon, sabit bir sayıyla çarpmadır ve çok kullanışlı bir kavram değildir. Ancak, filtrenin, konvolüsyonel sinir ağlarında yalnızca 2D bir resme değil, bir veri kümesine uygulandığını unutmayın. Bu nedenle, "1x1" filtre, 1x1 veri sütununun ağırlıklı toplamını hesaplar (resme bakın) ve filtreyi veriler üzerinde kaydırdıkça giriş kanallarının doğrusal bir kombinasyonunu elde edersiniz. Bu aslında faydalı. Kanalları, ayrı ayrı filtreleme işlemlerinin (ör. "sivri kulaklar" için bir filtre, başka bir "bıyıklar" filtresi, üçüncüsü ise "yarık gözler" için) bir diğeri olarak düşünürseniz, "1x1" kıvrımlı bir katman, bu özelliklerin birden çok olası doğrusal kombinasyonunu hesaplar. Bu, bir "kedi" ararken yararlı olabilir. Ayrıca 1x1 katmanlar daha az ağırlık kullanır.

7. Sıkıştırmalı

Bu fikirleri bir araya getirmenin basit bir yolu "Squeezenet" makalesinde gösterilmiştir. Yazarlar, yalnızca 1x1 ve 3x3 konvolüsyon katmanlarını kullanan çok basit bir konvolüsyon modülü tasarımı önermektedir.

1730ac375379269b.png

Görsel: "Ateş modüllerine" dayalı squeezenet mimarisi. Bu modeller, gelen verileri dikey boyutta "sıkarak", ardından veri derinliğini tekrar "genişleyen" iki paralel 1x1 ve 3x3 konvolüsyon katmanı gelen 1x1 katmanını değiştiriyor.

Uygulamalı

Önceki not defterinizde devam ederek squeezenet'ten ilham alan konvolüsyonel nöral ağ oluşturun. Model kodunu Keras "işlevsel stili" olarak değiştirmeniz gerekir.

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

Ek bilgi

Bu alıştırmada squeezenet modülü için yardımcı bir fonksiyon tanımlamak faydalı olacaktır:

def fire(x, squeeze, expand):
  y = l.Conv2D(filters=squeeze, kernel_size=1, padding='same', activation='relu')(x)
  y1 = l.Conv2D(filters=expand//2, kernel_size=1, padding='same', activation='relu')(y)
  y3 = l.Conv2D(filters=expand//2, kernel_size=3, padding='same', activation='relu')(y)
  return tf.keras.layers.concatenate([y1, y3])

# this is to make it behave similarly to other Keras layers
def fire_module(squeeze, expand):
  return lambda x: fire(x, squeeze, expand)

# usage:
x = l.Input(shape=[192, 192, 3])
y = fire_module(squeeze=24, expand=48)(x) # typically, squeeze is less than expand
y = fire_module(squeeze=32, expand=64)(y)
...
model = tf.keras.Model(x, y)

Bu seferki hedefimiz %80 doğruluk oranı elde etmektir.

Deneyebileceğiniz şeyler

Tek bir kıvrımlı katmanla başlayın, ardından MaxPooling2D(pool_size=2) katmanla dönüşümlü olarak "fire_modules" ile devam edin. Ağda maksimum 2 ila 4 havuz katmanıyla ve maksimum havuz katmanları arasında 1, 2 veya 3 ardışık yangın modülüyle deneme yapabilirsiniz.

Ateşleme modüllerinde "squeeze" parametresi genellikle "expand" parametresinden küçük olmalıdır. Bu parametreler aslında filtre sayısıdır. Genellikle 8 ile 196 arasında olabilir. Filtre sayısının ağda kademeli olarak arttığı mimarileri veya tüm ateşleme modüllerinin aynı sayıda filtreye sahip olduğu basit mimarileri deneyebilirsiniz.

Örnek:

x = tf.keras.layers.Input(shape=[*IMAGE_SIZE, 3]) # input is 192x192 pixels RGB

y = tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu')(x)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.GlobalAveragePooling2D()(y)
y = tf.keras.layers.Dense(5, activation='softmax')(y)

model = tf.keras.Model(x, y)

Bu noktada, denemelerinizin çok iyi gitmediğini ve %80 doğruluk hedefini yakalamanın zor olduğunu fark edebilirsiniz. Birkaç tane daha ucuz numaraya geçelim.

Toplu Normalleştirme

Toplu normalleştirme, yaşadığınız yakınsama sorunlarına yardımcı olacaktır. Bir sonraki atölyede bu teknikle ilgili ayrıntılı açıklamalar sunulacaktır. Şimdilik lütfen ağınızdaki her kıvrımlı katmandan sonra bu satırı ekleyerek fire_module fonksiyonunuzun içindeki katmanlarla birlikte kara kutu "sihirli" yardımcısı olarak kullanın:

y = tf.keras.layers.BatchNormalization(momentum=0.9)(y)
# please adapt the input and output "y"s to whatever is appropriate in your context

Veri kümemiz küçük olduğu için momentum parametresinin varsayılan değeri olan 0,99'dan 0,9'a düşürülmesi gerekir. Şimdilik bu ayrıntıyı boşver.

Veri artırma

Doygunluk değişikliklerinin sola doğru çevrilmesi gibi kolay dönüşümlerle verileri genişleterek birkaç yüzde puanı daha elde edebilirsiniz:

4ed2958e09b487ca.png

ad795b70334e0d6b.png

Bu işlemi TensorFlow'da tf.data.Dataset API ile çok kolay bir şekilde yapabilirsiniz. Verileriniz için yeni bir dönüşüm işlevi tanımlayın:

def data_augment(image, label):
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_saturation(image, lower=0, upper=2)
    return image, label

Daha sonra, bunu son veri dönüştürme işleminde kullanın (hücre "eğitim ve doğrulama veri kümeleri", "get_batched_dataset" işlevi):

dataset = dataset.repeat() # existing line
# insert this
if augment_data:
  dataset = dataset.map(data_augment, num_parallel_calls=AUTO)
dataset = dataset.shuffle(2048) # existing line

Veri artırmayı isteğe bağlı hale getirmeyi ve yalnızca eğitim veri kümesinin artırıldığından emin olmak için gerekli kodu eklemeyi unutmayın. Doğrulama veri kümesini büyütmenin bir anlamı yoktur.

Artık 35 dönemde% 80 doğruluk oranına ulaşabilirsiniz.

Çözüm

Çözüm not defteri buradadır. Sorun yaşadığınızda bu özelliği kullanabilirsiniz.

c3df49e90e5a654f.png Keras_Flowers_TPU_squeezenet.ipynb

Ele aldığımız konular

  • 🤔 Keras "işlevsel stil" modelleri
  • 🤓 Squeezenet mimarisi
  • 🤓 tf.data.datset ile veri artırma

Lütfen bu kontrol listesini gözden geçirin.

8. Xception ince ayarlandı

Ayrılabilir toplamalar

Yakın zamanda, konvolüsyon katmanlarını uygulamanın farklı bir yolu popülerlik kazanıyordu: derinlik ayırt edilebilir konvolüsyonlar. Çok ağızlı bir konu olduğunu biliyorum ancak konsept son derece basit. Tensorflow ve Keras'ta tf.keras.layers.SeparableConv2D olarak uygulanır.

Ayrılabilir bir topoloji de resim üzerinde bir filtre çalıştırır ancak giriş resminin her kanalı için farklı bir ağırlık grubu kullanır. Ardından, filtrelenen kanalların ağırlıklı toplamıyla sonuçlanan bir dizi nokta çarpımı olan "1x1 konvolüsyon" işlemi uygulanır. Her seferinde yeni ağırlıklarla, kanalların gerektiği kadar ağırlıklı yeniden birleşimi hesaplanır.

615720b803bf8dda.gif

Çizim: ayrılabilir kıvrımlar. 1. Aşama: Her kanal için ayrı bir filtre içeren konvolüsyonlar. 2. Aşama: Kanalların doğrusal yeniden birleşimleri. İstenilen çıkış kanalı sayısına ulaşılana kadar yeni bir ağırlık kümesiyle tekrarlanır. 1. Aşama da her seferinde yeni ağırlıklarla tekrarlanabilir ancak pratikte bu durum nadiren görülür.

Ayrılabilir konvolüsyonlar en yeni konvolüsyonlu ağ mimarilerinde kullanılır: MobileNetV2, Xception, EfficientNet. Bu arada, MobileNetV2, daha önce aktarım öğrenmesi için kullanmıştınız.

Normal örtüşmelerden daha ucuzdur ve pratikte aynı derecede etkili olduğu tespit edilmiştir. Yukarıda gösterilen örneğin ağırlık sayısını aşağıda görebilirsiniz:

Toplama katmanı: 4 x 4 x 3 x 5 = 240

Ayrılabilir konvolüsyonel katman: 4 x 4 x 3 + 3 x 5 = 48 + 15 = 63

Bu, okuyucunun her kıvrımlı katman ölçeği stilini benzer şekilde uygulamak için gereken çarpma sayısından daha fazla hesaplama yapması için bir alıştırma olarak bırakılır. Ayrılabilir kıvrımlar daha küçüktür ve hesaplama açısından çok daha etkilidir.

Uygulamalı

"Öğrenmeyi aktarma" oyun alanı not defterinden yeniden başlatın. Ancak bu kez önceden eğitilmiş model olarak Xception'ı seçin. Xception yalnızca ayrılabilir kıvrımlar kullanır. Tüm ağırlıkları eğitilebilir bırakın. Önceden eğitilmiş katmanları olduğu gibi kullanmak yerine, verilerimizde önceden eğitilmiş ağırlıklarda ince ayar yapacağız.

c3df49e90e5a654f.png Keras Flowers transfer learning (playground).ipynb

Hedef: %95'ten yüksek doğruluk (Evet, ciddi olarak mümkün!)

Bu son alıştırma olduğundan biraz daha fazla kod ve veri bilimi çalışması gerektiriyor.

İnce ayarlama hakkında ek bilgiler

Xception, tf.keras.application'daki standart önceden eğitilmiş modellerde kullanılabilir.* Bu sefer tüm ağırlıkları eğitilebilir durumda bırakmayı unutmayın.

pretrained_model = tf.keras.applications.Xception(input_shape=[*IMAGE_SIZE, 3],
                                                  include_top=False)
pretrained_model.trainable = True

Bir modeli hassaslaştırırken iyi sonuçlar elde etmek için öğrenme hızına dikkat etmeniz ve artış dönemi içeren bir öğrenme hızı programı kullanmanız gerekir. Aşağıdaki gibi:

9b1af213b2b36d47.png

Standart bir öğrenme hızıyla başlamak, modelin önceden eğitilmiş ağırlıklarını bozar. Model, verilerinize tutunup bunları makul bir şekilde değiştirene kadar kademeli olarak başlatma işlemi, verileri korur. Rampanın ardından sabit veya üssel olarak azalan bir öğrenme hızıyla devam edebilirsiniz.

Keras'ta öğrenme hızı, her dönem için uygun öğrenme hızını hesaplayabileceğiniz bir geri çağırma işlevi aracılığıyla belirtilir. Keras, her dönem için doğru öğrenme hızını optimizatöre iletir.

def lr_fn(epoch):
  lr = ...
  return lr

lr_callback = tf.keras.callbacks.LearningRateScheduler(lr_fn, verbose=True)

model.fit(..., callbacks=[lr_callback])

Çözüm

Çözüm not defteri buradadır. Sorun yaşadığınızda bu özelliği kullanabilirsiniz.

c3df49e90e5a654f.png 07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb

Ele aldığımız konular

  • 🤔 Derinlik ayırt edilebilir topoloji
  • 🤓 Öğrenme hızı programları
  • 😈 Önceden eğitilmiş bir modelde ince ayar yapma.

Lütfen bir dakikanızı ayırarak bu yapılacaklar listesini zihninizde inceleyin.

9. Tebrikler!

İlk modern konvolüsyonel nöral ağınızı oluşturup% 90'ın üzerinde doğruluk oranıyla eğittiniz. TPU'lar sayesinde sadece dakikalar içinde art arda eğitim tekrar uygulamaya başladınız. 4 "TPU codelab'de Keras" programının sonuna geldik:

TPU'ların pratikte kullanımı

TPU'lar ve GPU'lar Cloud AI Platform'da mevcuttur:

Son olarak, geri bildirimlere değer veriyoruz. Bu laboratuvarda yanlış bir şey görürseniz veya iyileştirilmesini düşünürseniz lütfen bize bildirin. Geri bildirim, GitHub sorunları [geri bildirim bağlantısı] üzerinden gönderilebilir.

HR.png

Martin Görner kimliği small.jpg
Yazar: Martin Görner
Twitter: @martin_gorner

tensorflow logo.jpg
www.tensorflow.org