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

1. Genel Bakış

Bu laboratuvarda, modern kıvrımlı mimari hakkında bilgi edinecek ve bilgilerinizi kullanarak "sıkıştırma" adı verilen basit ama etkili bir dönüşüm işlemi uygulayacaksınız.

Bu laboratuvar, konvolüsyonel nöral ağlarla ilgili gerekli teorik açıklamaları içerir ve derin öğrenme hakkında bilgi edinen geliştiriciler için iyi bir başlangıç noktasıdır.

Bu laboratuvar, "TPU'da Keres"in 4. bölümüdür seri. Bunları aşağıdaki sırayla veya bağımsız olarak yapabilirsiniz.

ca8cc21f6838eccc.png

Neler öğreneceksiniz?

  • Keras'ın işlevsel tarzında ustalaşmak için
  • Squeezenet mimarisini kullanarak model derleme
  • Hızlı eğitim sağlamak ve mimarinizi yinelemek için TPU'ları kullanmak
  • tf.data.dataset ile veri genişletmeyi 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 yanlışlık 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 Collaboratory kullanılı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ştirin ve TPU'yu seçin. Bu kod laboratuvarında, donanım hızlandırmalı eğitim için desteklenen güçlü bir TPU (Tensör İşleme Birimi) kullanacaksınız. Çalışma zamanı bağlantısı ilk çalıştırma sırasında otomatik olarak kurulur. Alternatif olarak, düğmesini tıklayın.

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ı da Çalışma zamanı > Tümünü çalıştır

İçindekiler

429f106990037ec4.png

Tüm not defterlerinin bir içindekiler tablosu vardır. 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 işlevleri destekler veya görselleştirme yapar. İçerideki işlevlerin tanımlanması için bu hücreleri yine de çalıştırmanız gerekir.

Kimlik Doğrulama

cdd4b41413100543.png

Yetkili bir hesapla kimlik doğrulaması yapmanız koşuluyla Colab, gizli Google Cloud Storage paketlerinize erişebilir. Yukarıdaki kod snippet'i bir kimlik doğrulama işlemini tetikler.

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

Özet

f88cf6facfc70166.png

Keras'ta TPU'da model eğitme (ve TPU yoksa GPU veya CPU kullanma) kodu:

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=...)

Bugün etkileşimli hızlarda (eğitim çalıştırması başına dakika) çiçek sınıflandırıcı 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üzenlenmiştir. Bu, 3D oluşturma, derin öğrenme, fiziksel simülasyonlar gibi çeşitli görevleri gerçekleştirmelerine olanak tanıyan çok esnek bir mimaridir. Öte yandan TPU'lar, bir klasik vektör işlemciyi özel bir matris çarpım birimiyle eşleştirir ve nöral ağlar gibi büyük matris çarpımlarının baskın olduğu tüm görevlerde üstün başarı elde eder.

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ı yaptığını doğrulamak için lütfen bir satır x sütun çarpım işlemini gerçekleştirin. Kıvrımlı katmanlar, biraz daha karmaşık olsa da matris çarpımları olarak da ifade edilebilir ( açıklama, 1. bölümdeki).

Donanım

MXU ve VPU

TPU v2 çekirdeği, matris çarpımları çalıştıran bir Matris Çarpma Biriminden (MXU) ve etkinleştirmeler, softmax vb. diğer tüm görevler için bir Vektör İşleme Birimi (VPU)ndan oluşur. VPU, float32 ve int32 hesaplamalarını yürütür. 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 birikimler, float32 hassasiyetinde gerçekleştirilir.

19c5fc432840c714.png

Sinir ağı eğitimi genellikle daha düşük kayan nokta hassasiyetinden kaynaklanan gürültüye karşı dirençlidir. Gürültünün optimize edicinin yakınlaşmasına bile yardımcı olduğu durumlar vardır. 16 bit kayan nokta hassasiyeti geleneksel olarak hesaplamaları hızlandırmak için kullanılır, ancak float16 ve float32 biçimleri çok farklı aralıklara sahiptir. Hassasiyeti float32'den float16'ya düşürmek, 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.

Bu nedenle Google, TPU'larda bfloat16 biçimini kullanıma sunmuştur. 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 donanım kullanarak donanımda matris çarpımlarını uygular veri öğelerinin bir dizi donanım hesaplama biriminden geçtiği mimari. (Tıpta "sistolik" terimi, kalp kasılmalarını ve buradan veri akışını ifade eder.)

Matris çarpımlarının temel öğesi, bir matristeki çizgi ile diğer matristeki bir 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 şu şekilde 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 ürünü bir GPU "çekirdek" olarak programlanır ve ardından tüm bu çekirdeklerde Böylece, sonuç matrisin her değerini bir kerede hesaplamayı deneyebilirsiniz. Sonuç matris 128x128 büyükse 128x128=16K "çekirdek" gerektirir kullanılabilir ve genellikle bu mümkün değildir. En büyük GPU'lar yaklaşık 4.000 çekirdeğe sahiptir. TPU'da ise MXU'daki işlem birimleri için minimum donanım kullanılır: Yalnızca bfloat16 x bfloat16 => float32 çarpma toplayıcısı kullanılır, başka hiç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

Çizim: MXU sistolik dizisi. Compute elemanları çarpma toplayıcılardır. Bir matrisin değerleri diziye (kırmızı noktalar) yüklenir. Diğer matrisin değerleri, diziden (gri noktalar) geçer. Dikey çizgiler, değerleri yukarı doğru yayar. Yatay çizgiler kısmi toplamları yayar. Bu, kullanıcıya, veriler diziden geçerken sağ taraftan çıkan matris çarpımının sonucunu elde ettiğinizi doğrulayan bir alıştırma olarak bırakılır.

Buna ek olarak nokta çarpımları, MXU cinsinden hesaplanırken ara toplamlar, bitişik hesap birimleri arasında geçiş yapar. Depolanmaları ve belleğe, hatta 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

Bir " Cloud TPU v2 inç Google Cloud Platform'da, PCI'ye bağlı TPU kartı olan bir sanal makine alırsınız. TPU kartında dört adet çift çekirdekli TPU çipi bulunur. Her TPU çekirdeğinde bir VPU (Vektör İşleme Birimi) ve 128x128 MXU (MatriX çarpma Birimi) bulunur. Bu "Cloud TPU" daha sonra genellikle ağ üzerinden bunu isteyen sanal makineye bağlanır. Tam tablo şu şekilde görünür:

dfce5522ed644ece.png

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

TPU kapsülleri

Google'ın veri merkezlerinde TPU'lar, yüksek performanslı bir bilgi işlem (HPC) ara bağlantısına bağlıdır. Bu bağlantı, TPU'ların çok büyük bir hızlandırıcı gibi görünmesini sağlayabilir. Google bunlara kapsül adını verir ve en fazla 512 TPU v2 çekirdek veya 2048 TPU v3 çekirdeklerini kapsayabilir.

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 boyutlar üzerinde eğitim yaparak 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 iş boyutu, TPU çekirdeği başına 128 veri öğesidir ancak donanım zaten TPU çekirdeği başına 8 veri öğesinden iyi bir kullanım gösterebilmektedir. Cloud TPU'da 8 çekirdek olduğunu unutmayın.

Bu kod laboratuvarında Keras API'yi kullanacağız. Keras'ta belirttiğiniz grup, tüm TPU için genel grup boyutudur. Gruplarınız otomatik olarak 8'e bölünecek ve TPU'nun 8 çekirdeği üzerinde çalıştırılacaktı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.

Gelişmiş seçenekler: XLA

Tensorflow programları hesaplama grafiklerini tanımlar. TPU, Python kodunu doğrudan çalıştırmaz, 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

Çizim: TPU'da çalışmak için Tensorflow programınız tarafından tanımlanan hesaplama grafiği önce XLA (hızlandırılmış doğrusal cebir derleyici) temsiline dönüştürülür, 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'lar ve TPU kapsülleri üzerinde çalışır. Aşağıda TPU, GPU ve CPU'da çalışan bir örnek 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ğda 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. Elle bir TPU oluşturursanız TPU_NAME env. var. sanal makinede arayın veya açık parametrelerle TPUClusterResolver işlevini çağırın: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy, dağıtım ve "all-reduce"un uygulandığı kısımdır. gradyan senkronizasyon algoritmasından yararlanabilirsiniz.
  • 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 girişi için bir tf.data.Dataset nesnesi bekler.

Genel TPU bağlantı noktası görevleri

  • Tensorflow modelinde 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 üzerinde çalışırken veri kullanımı genellikle performans sorunlarına yol açar. TPU Performans Kılavuzu'nda, veri sorunlarını tespit etmek için kullanabileceğiniz araçlar ve diğer performans ipuçlarını bulabilirsiniz.
  • int8 veya int16 sayıları, int32 olarak kabul edilir. TPU'nun, 32 bitten düşük değerler üzerinde çalışan tam sayı donanımı yoktur.
  • Bazı Tensorflow işlemleri desteklenmez. Listeyi burada bulabilirsiniz. Neyse ki bu sınırlama, yalnızca eğitim kodu (modelinizde ileri ve geri geçiş) için geçerlidir. CPU'da yürütüldüğünden, veri girişi 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

Bir sonraki paragrafta yer alan kalın yazı tipindeki tüm terimleri zaten biliyorsanız bir sonraki alıştırmaya geçebilirsiniz. Derin öğrenmeye yeni başlıyorsanız hoş geldiniz. Lütfen okumaya devam edin.

Keras, bir dizi katman olarak oluşturulan modeller için Sequential API'yi sunmaktadır. Ö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. "Nöronlardan" oluşur. katmanlar halinde düzenlenir. Birinci katman giriş verilerini işler ve çıkışlarını diğer katmanlara iletir. "Yoğun" deniyor çünkü her nöron bir önceki katmandaki tüm nöronlara bağlıdır.

c21bae6dade487bc.png

Bir resmi, tüm piksellerinin RGB değerlerini uzun bir vektörde birleştirerek ve bunu giriş olarak kullanarak böyle bir ağa besleyebilirsiniz. Bu, görüntü tanıma için en iyi teknik değildir, ancak daha sonra bunu iyileştireceğiz.

Nöronlar, aktivasyonlar, RELU

Bir "nöron" tüm girişlerinin ağırlıklı bir toplamını hesaplar, buna "önyargı" adı verilen bir değer ekler ve sonucu "etkinleştirme işlevi" adı verilen bir şekilde iletir. Ağırlıklar ve yanlılık başlangıçta bilinmiyor. Rastgele bir şekilde başlatılır ve "öğrenilir" bilinen çok sayıda veri üzerinde nöral ağını eğiterek olumsuz bir etki yarattı.

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 çok basit bir fonksiyondur.

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 sona ermektedir. 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 (softmax) uygulanması, her elementin üssü alınıp ardından genellikle L1 normunun (mutlak değerlerin toplamı) kullanılmasıyla vektör normalleştirilir. Böylece, değerlerin toplamı 1'e eşit olur ve olasılık olarak yorumlanabilir.

ef0d98c0952c262d.png d51252f75894479e.gif

Çapraz entropi kaybı

Nöral 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 bilgiler ile doğru yanıtlar arasındaki mesafeyi, yani genellikle "etiketler"i ölçmemiz gerekir. Veri kümesindeki tüm görüntüler için doğru etiketlere sahip olduğumuzu unutmayın.

Herhangi bir mesafe işe yarar ancak sınıflandırma sorunları için "çapraz entropi mesafesi" en etkili olandır. Bunu hatamız veya "kayıp" olarak adlandıracağız işlev:

7bdf8753d20617fb.png

Gradyan inişi

"Eğitim" nöral ağ 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ülerini ve etiketleri kullanmayı ifade eder. İş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.

Tüm ağırlıklara ve tüm sapmalara göre çapraz entropinin kısmi türevlerini hesaplarsak, belirli bir görüntü, etiket ve ağırlıkların ve yanlılığın mevcut değeri için hesaplanan bir "gradyan" elde ederiz. Milyonlarca ağırlığa ve sapmaya sahip olabileceğimizi, bu nedenle gradyanı hesaplamanın çok büyük bir iş olduğunu unutmayın. Neyse ki Tensorflow bunu bizim için yapıyor. Bir renk geçişinin matematiksel özelliği, "yukarı" 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. Daha sonra, bir eğitim döngüsünde sonraki eğitim görüntüsü ve etiketi gruplarını kullanarak aynı şeyi 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.

gradyan descent2.png

Mini toplanma ve momentum

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 resimlerin 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 "olasılıksal gradyan inişi" olarak da adlandırılan bu teknik daha pragmatik bir faydası da var: Gruplarla çalışmak daha büyük matrislerle çalışmak anlamına geliyor. Bunların GPU ve TPU'larda optimize edilmesi genellikle daha kolay.

Ancak yakınsaklık yine de biraz kaotik olabilir ve gradyan vektörü sıfır olsa bile durabilir. Bu, minimum değer bulduğumuz anlamına mı geliyor? Her zaman değil Gradyan bileşeni minimum veya maksimum sıfır olabilir. Milyonlarca öğe içeren bir gradyan vektöründe, hepsi sıfırsa, her sıfırın bir minimum değere karşılık gelme ve hiçbirinin bir maksimum noktaya ulaşma olasılığı oldukça küçü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ğer değildir. (Resim atfı Wikimedia: Yazan: Nicoguaro - Kendi eseri, 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" boyut, genellikle veri tensörlerinin ilk boyutudur. Örneğin, [100, 192, 192, 3] şeklindeki bir tensör, piksel başına üç değer (RGB) içeren 192x192 piksellik 100 resim içerir.

çapraz entropi kaybı: Sınıflandırıcılarda genellikle kullanılan özel bir kayıp işlevi.

yoğun katman: Her nöronun bir ö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) bir nöral ağa besleneceğini bulma sanatına "özellik mühendisliği" denir.

labels: "sınıflar" için başka bir ad veya doğru cevapları içeren gözetimli

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

logits: Aktivasyon işlevi uygulanmadan önce bir nöron katmanının çıktılarına "logit" adı verilir. Terim, "mantıksal fonksiyon"dan gelir "sigmoid işlevi" olarak da bilinir. eskiden en popüler aktivasyon fonksiyonuydu. "Lojistik fonksiyondan önce nöron çıkışları" kısaltılmıştı "logits" olarak değiştirildi.

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 öğeden oluşan bir vektör olarak kodlanır. 3'üncü olan (1) hariç tüm sıfırlar kullanılır.

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

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

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

tensor: Bir "tensor" bir matrise benzer ancak rastgele sayıda boyuta sahiptir. 1 boyutlu tensör bir vektördür. 2 boyutlu tensör, bir matristir. 3, 4, 5 veya daha fazla boyutlu tensörleriniz de olabilir.

5. [INFO] Konvolüsyonlu nöral ağlar

Özet

Bir sonraki paragrafta yer alan kalın yazı tipindeki tüm terimleri zaten biliyorsanız bir sonraki alıştırmaya geçebilirsiniz. Konvolüsyonel nöral ağlarla yeni başlıyorsanız lütfen okumaya devam edin.

convolutional.gif

Çizim: Bir resmi, her biri 4x4x3=48 öğrenilebilir ağırlıklardan oluşan art arda iki filtreyle 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

Konvolüsyonel ağ katmanında, bir "nöron" yalnızca resmin küçük bir bölgesinde, hemen üzerindeki piksellerin ağırlıklı bir toplamını yapar. Normal yoğun bir katmandaki bir nöronun yaptığı gibi, bir eğilim ekler ve toplamı bir aktivasyon fonksiyonu aracılığıyla iletir. 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ı olduğunu unutmayın. Burada tek bir "yama" resimdeki ağırlıkların her iki yönde ("büyütme") kaydığını gösterir. Çıkış, resimde piksel sayısı kadar değere sahiptir (yine de kenarlarda bazı dolgu yapılması gerekir). 4x4x3=48 ağırlıklardan oluşan bir filtre kullanan bir filtreleme işlemidir.

Ancak 48 ağırlık yeterli olmayacaktır. Daha fazla serbestlik derecesi eklemek için aynı işlemi yeni bir ağırlık grubuyla tekrarlarız. Bu işlem yeni bir filtre çıktıları kümesi oluşturur. Buna "kanal" diyelim. giriş resmindeki R,G,B kanallarına benzeterek çıktıların performansını görebilirsiniz.

Screen Shot 2016-07-29 at 16.02.37.png

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

d1b557707bcd1cb9.png

Çizim: konvolüsyonel nöral ağ "küpleri" dönüştürüyor başka "küplere" açısından son derece önemlidir.

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

Çizim: Hesaplama penceresinin 3 piksel kaydırılması daha az çıkış değerine neden olur. Katmanlı kıvrımlar veya maksimum havuz (2x2 kaydırılan 2x2 pencere için en fazla) veri küpünü yatay boyutlarda daraltmanın bir yoludur.

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

Son olarak, son veri küpünü düzleştirip softmax'ın etkin olduğu yoğun 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

Çizim: Konvolüsyon ve softmax katmanları kullanan bir resim sınıflandırıcı. 3x3 ve 1x1 filtreler kullanı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 kıvrımlı yığın, Keras'ta şöyle 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 evrişimsel mimariler

Özet

7968830b57b708c0.png

Çizim: konvolüsyonlu "modül". Bu noktada en iyisi hangisi? Ardından maksimum havuz katmanı ve ardından 1x1 konvolüsyonlu bir katman mı yoksa farklı bir katman kombinasyonu mu gelir? Tümünü deneyin, sonuçları birleştirin ve ağ karar versin. Sağda: " ilk" kıvrımlı mimariyi nasıl koruduğunu konuştuk.

Keras'ta, veri akışının farklı alanlara yayılabileceği modeller oluşturmak için "işlevsel" model stili. Ö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 püf noktaları

Küçük 3x3 filtreleri

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, bir 5x5 filtresinin hesapladığı kombinasyon değildir, ancak art arda gelen iki 3x3 filtresi tek bir 5x5 filtresinden daha ucuz olduğu için denemekte fayda vardır.

1x1 evrişimler ?

fd7cac16f8ecb423.png

Matematiksel anlamda bir "1x1", konvolüsyon sabit bir sayıyla çarpılır ve çok yararlı bir kavram değildir. Bununla birlikte, konvolüsyonlu nöral ağlarda filtrenin yalnızca 2D görüntüye değil, bir veri küpüne uygulandığını unutmayın. Dolayısıyla, bir "1x1" filtresi, 1x1'lik bir veri sütununun ağırlıklı toplamını hesaplar (resme bakın) ve sütunu veriler üzerinde kaydırdıkça giriş kanallarının doğrusal bir kombinasyonunu elde edersiniz. Bu aslında yararlı bir özelliktir. Kanalları, bağımsız filtreleme işlemlerinin sonuçları olarak düşünürseniz (ör. "noktalı kulaklar", "bıyıklanma" için başka bir filtre) üçüncüsü ise "yarık gözler" için ardından bir "1x1" konvolüsyonel katman, bu özelliklerin birden çok olası doğrusal kombinasyonunu hesaplayacaktır. Bu, bir "kedi" ararken yararlı olabilir. Üstelik 1x1 katmanlar daha az ağırlık kullanır.

7. Sıkıştırmalı

Bu fikirleri bir araya getirmenin basit bir yolu "Squeezenet" bakın. Yazarlar yalnızca 1x1 ve 3x3 konvolüsyonel katmanlar kullanan çok basit bir kıvrımlı modül tasarımını öne çıkarmıştır.

1730ac375379269b.png

Çizim: "Ateş modülleri"ne dayalı squeezenet mimarisi. Birbiriyle "sıkıştıran" 1x1'lik bir katman Dikey boyutta gelen veriler ve ardından "genişleyen" iki paralel 1x1 ve 3x3 konvolüsyon katmanı gelir derinliğini tekrar azaltır.

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 defaki hedef% 80 doğruluk oranına ulaşmaktır.

Denenebilecekler

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ş modüllerinde "sıkıştırma" parametresi genellikle "expand" parametresinden sonra bir değer girin. Bu parametreler aslında filtre sayısıdır. Genellikle 8 ile 196 arasında olabilir. Ağda filtre sayısının kademeli olarak arttığı mimariler veya tüm yangın modüllerinin aynı sayıda filtreye sahip olduğu basit mimarilerle denemeler yapabilirsiniz.

Ö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 pek iyi gitmediğini ve% 80 doğruluk hedefinin uzak gibi göründüğünü fark edebilirsiniz. Birkaç tane daha ucuz numaraya geçelim.

Toplu Normalleştirme

Toplu norm, yaşadığınız yakınlaşma sorunlarına yardımcı olur. Bir sonraki atölyede bu teknikle ilgili ayrıntılı açıklamalar bulunacaktır. Şimdilik lütfen bunu bir kara kutu "sihir" olarak kullanın yardımcı olacak şekilde, fire_module fonksiyonunuzun içindeki katmanlar da dahil olmak üzere ağınızdaki her konvolüsyonel katmanın arkasına bu satırı ekleyebilirsiniz:

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ümeniz küçük olduğundan 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 genişletme

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 kolayca yapabilirsiniz. Verileriniz için yeni bir dönüştürme 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ürmenizde 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 genişletmeyi isteğe bağlı yapmayı ve yalnızca eğitim veri kümesinin genişletildiğinden emin olmak için gerekli kodu eklemeyi unutmayın. Doğrulama veri kümesini genişletmek anlamlı değildir.

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

Çözüm

Çözüm not defterini burada bulabilirsiniz. Takılırsanız kullanabilirsiniz.

c3df49e90e5a654f.png Keras_Flowers_TPU_squeezenet.ipynb

İşlediklerimiz

  • 🤔 Keras "işlevsel tarz" modeller
  • 🤓 Squeezenet mimarisi
  • 🤓 tf.data.datset ile veri genişletme

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

8. Xception ince ayarı yapıldı

Ayrılabilir kıvrımlar

Konvolüsyonel katmanları uygulamanın farklı bir yolu da son zamanlarda popülerlik kazanıyordu: derinlikten ayrılabilen kıvrımlar. Ç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 evrişim, resim üzerinde bir filtre de çalıştırır, ancak giriş resminin her bir kanalı için ayrı bir ağırlık grubu kullanır. Ardından, filtrelenen kanalların ağırlıklı toplamıyla sonuçlanan bir dizi nokta ürünü olan "1x1 konvolüsyon" gelir. Her seferinde yeni ağırlıklarla, kanalların mümkün olduğunca çok sayıda ağırlıklı rekombinasyonu gerektiğinde hesaplanır.

615720b803bf8dda.gif

Çizim: ayrılabilir kıvrımlar. 1. Aşama: Her bir kanal için ayrı bir filtreyle evrişimler. 2. Aşama: Kanalların doğrusal rekombinasyonları. İstenen çıkış kanalı sayısına ulaşılana kadar yeni bir ağırlık grubuyla tekrarlanır. 1. aşama da her seferinde yeni ağırlıklarla tekrarlanabilir ancak pratikte bu nadiren yaşanı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 kıvrımlardan daha ucuzdur ve pratikte aynı derecede etkili oldukları tespit edilmiştir. Yukarıda gösterilen örneğin ağırlık sayısını aşağıda görebilirsiniz:

Konvolüsyonel 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 aktar" bölümünden yeniden başlatma Ancak bu kez önceden eğitilmiş model olarak Xception'ı seçiyor. Xception yalnızca ayrılabilir kıvrımlar kullanır. Tüm ağırlıkları eğitilebilir bırakın. Önceden eğitilmiş katmanları bu şekilde kullanmak yerine, verilerimiz üzerindeki önceden eğitilmiş ağırlıklara ince ayar yapacağız.

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

Hedef: doğruluk > %95 (Hayır, cidden mümkün!)

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

İnce ayarlar hakkında ek bilgi

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

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

Bir modelde ince ayarlar yaparken iyi sonuçlar almak için öğrenme hızına dikkat etmeniz ve kampanya gelişim dönemi olan 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. Başlatma işlemi, verileriniz model tarafından mantıklı bir şekilde değiştirilinceye kadar kademeli olarak korunur. Rampadan sonra, sabit veya katlanarak artan bir öğrenme hızıyla devam edebilirsiniz.

Keras'ta öğrenme hızı, her bir dönem için uygun öğrenme hızını hesaplayabileceğiniz bir geri çağırma ile belirtilir. Keras, her bir dönem için doğru öğrenme hızını optimize ediciye 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 defterini burada bulabilirsiniz. Takılırsanız kullanabilirsiniz.

c3df49e90e5a654f.png 07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb

İşlediklerimiz

  • 🤔 Derinlikten ayrılabilen konvolüsyon
  • 🤓 Öğrenme oranı 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:

Uygulamada TPU'lar

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

Son olarak, geri bildirimlere değer veriyoruz. Bu laboratuvarda bir eksiklik görürseniz veya bir şeylerin iyileştirilmesi gerektiğini düşünüyorsanız lütfen bize bildirin. Geri bildirimlerinizi, GitHub sorunları [feedback link] sayfasında bulabilirsiniz.

HR.png

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

tensorflow logosu.jpg
www.tensorflow.org