TensorFlow'da Gradient Descent'in Birçok Uygulaması

Yayınlanan: 2022-03-11

Google'ın TensorFlow'u, derin öğrenme modellerini eğitmek ve dağıtmak için önde gelen araçlardan biridir. Yüz milyonlarca parametreyle son derece karmaşık sinir ağı mimarilerini optimize edebilir ve donanım hızlandırma, dağıtılmış eğitim ve üretim iş akışları için çok çeşitli araçlarla birlikte gelir. Bu güçlü özellikler, derin öğrenme alanının dışında korkutucu ve gereksiz görünmesini sağlayabilir.

Ancak TensorFlow, derin öğrenme modellerinin eğitimi ile doğrudan ilgili olmayan daha basit problemler için hem erişilebilir hem de kullanılabilir olabilir. Özünde, TensorFlow, tensör işlemleri (vektörler, matrisler vb.) ve rastgele hesaplama dizileri üzerinde gradyan inişi gerçekleştirmek için kullanılan kalkülüs işlemleri için optimize edilmiş bir kitaplıktır. Deneyimli veri bilimcileri, "gradyan inişinin" hesaplamalı matematik için temel bir araç olduğunu kabul edeceklerdir, ancak genellikle uygulamaya özel kod ve denklemlerin uygulanmasını gerektirir. Göreceğimiz gibi, TensorFlow'un modern “otomatik farklılaşma” mimarisi burada devreye giriyor.

TensorFlow Kullanım Örnekleri

  • Örnek 1: TensorFlow 2.0'da Gradient Descent ile Lineer Regresyon
    • Gradyan İniş Nedir?
  • Örnek 2: Maksimal Yayılmış Birim Vektörleri
  • Örnek 3: Karşıt Yapay Zeka Girdileri Oluşturma
  • Son Düşünceler: Gradient Descent Optimizasyonu
  • TensorFlow'da Gradient Descent: Minimumları Bulmaktan Yapay Zeka Sistemlerine Saldırmaya

Örnek 1: TensorFlow 2.0'da Gradient Descent ile Lineer Regresyon

Örnek 1 Defter

TensorFlow koduna geçmeden önce gradyan inişi ve lineer regresyon hakkında bilgi sahibi olmak önemlidir.

Gradyan İniş Nedir?

En basit ifadeyle, çıktısını en aza indiren bir denklem sisteminin girdilerini bulmak için sayısal bir tekniktir. Makine öğrenimi bağlamında, bu denklem sistemi bizim modelimizdir , girdiler modelin bilinmeyen parametreleridir ve çıktı, model ile verilerimiz arasında ne kadar hata olduğunu temsil eden, minimize edilecek bir kayıp fonksiyonudur . Bazı problemler için (doğrusal regresyon gibi), hatamızı en aza indiren parametreleri doğrudan hesaplamak için denklemler vardır, ancak çoğu pratik uygulamada, tatmin edici bir çözüme ulaşmak için gradyan inişi gibi sayısal tekniklere ihtiyaç duyarız.

Bu makalenin en önemli noktası, gradyan inişinin genellikle denklemlerimizi oluşturmayı ve kayıp fonksiyonumuz ile parametrelerimiz arasındaki ilişkiyi türetmek için hesabı kullanmayı gerektirmesidir. TensorFlow (ve herhangi bir modern otomatik farklılaştırma aracı) ile hesap bizim için halledilir, böylece çözümü tasarlamaya odaklanabiliriz ve çözümün uygulanması için zaman harcamak zorunda kalmazız.

İşte basit bir lineer regresyon probleminde nasıl göründüğü. 150 yetişkin erkeğin boylarının (h) ve ağırlıklarının (w) bir örneğine sahibiz ve bu çizginin eğimi ve standart sapması konusunda kusurlu bir tahminle başlıyoruz. Yaklaşık 15 gradyan iniş yinelemesinden sonra, optimuma yakın bir çözüme ulaşırız.

İki senkronize animasyon. Sol taraf, verilerden uzakta başlayan ve daha sonra ona doğru hızla hareket eden ve nihai uyumu bulmadan önce yavaşlayan yerleşik bir çizgi ile bir boy-ağırlık dağılım grafiğini gösterir. Doğru boyut, her karenin grafiğe yeni bir yineleme eklediği, yinelemeye karşı kayıp grafiğini gösterir. Kayıp, grafiğin üstünde 2.000'de başlar, ancak logaritmik bir eğri gibi görünen bir kaç yineleme içinde minimum kayıp çizgisine hızla yaklaşır.

TensorFlow 2.0 kullanarak yukarıdaki çözümü nasıl ürettiğimizi görelim.

Doğrusal regresyon için, ağırlıkların doğrusal bir yükseklik denklemi ile tahmin edilebileceğini söylüyoruz.

w-subscript-i,pred eşittir alfa nokta çarpımı h-subscript-i artı beta.

Tahminler ve gerçek değerler arasındaki ortalama karesel hatayı (kayıp) en aza indiren α ve β (eğim ve kesişim) parametrelerini bulmak istiyoruz. Dolayısıyla kayıp fonksiyonumuz (bu durumda “ortalama kare hatası” veya MSE) şöyle görünür:

MSE, bir bölü N çarpı i'den gelen toplamın, w-subscript-i,true ve w-subscript-i,pred arasındaki farkın karesinin N'ye eşittir.

Ortalama kare hatasının birkaç kusurlu çizgiyi nasıl aradığını ve ardından tam çözümle (α=6.04, β=-230.5) görebiliriz.

Aynı boy-ağırlık dağılım grafiğinin, her biri farklı bir çizgiye sahip üç kopyası. Birincisi w = 4.00 * h + -120.0'a ve 1057.0'lık bir kayba sahiptir; çizgi verilerin altında ve ondan daha az dik. İkincisi w = 2.00 * h + 70.0 ve 720.8'lik bir kayba sahiptir; çizgi, veri noktalarının üst kısmına yakındır ve hatta daha az diktir. hird w = 60.4 * h + -230.5'e ve 127.1'lik bir kayba sahiptir; çizgi, çevresinde eşit olarak kümelenmiş görünecek şekilde veri noktalarından geçer.

Bu fikri TensorFlow ile hayata geçirelim. Yapılacak ilk şey, tensörleri ve tf.* işlevlerini kullanarak kayıp işlevini kodlamaktır.

 def calc_mean_sq_error(heights, weights, slope, intercept): predicted_wgts = slope * heights + intercept errors = predicted_wgts - weights mse = tf.reduce_mean(errors**2) return mse

Bu oldukça basit görünüyor. Tüm standart cebirsel operatörler tensörler için aşırı yüklenmiştir, bu nedenle yalnızca optimize ettiğimiz değişkenlerin tensör olduğundan emin olmamız gerekir ve diğer her şey için tf.* yöntemlerini kullanırız.

O zaman tek yapmamız gereken bunu bir gradyan iniş döngüsüne koymak:

 def run_gradient_descent(heights, weights, init_slope, init_icept, learning_rate): # Any values to be part of gradient calcs need to be vars/tensors tf_slope = tf.Variable(init_slope, dtype='float32') tf_icept = tf.Variable(init_icept, dtype='float32') # Hardcoding 25 iterations of gradient descent for i in range(25): # Do all calculations under a "GradientTape" which tracks all gradients with tf.GradientTape() as tape: tape.watch((tf_slope, tf_icept)) # This is the same mean-squared-error calculation as before predictions = tf_slope * heights + tf_icept errors = predictions - weights loss = tf.reduce_mean(errors**2) # Auto-diff magic! Calcs gradients between loss calc and params dloss_dparams = tape.gradient(loss, [tf_slope, tf_icept]) # Gradients point towards +loss, so subtract to "descend" tf_slope = tf_slope - learning_rate * dloss_dparams[0] tf_icept = tf_icept - learning_rate * dloss_dparams[1]

Bunun ne kadar temiz olduğunu anlamak için bir dakikanızı ayıralım. Gradyan inişi, optimize etmeye çalıştığımız tüm değişkenlere göre kayıp fonksiyonunun türevlerinin hesaplanmasını gerektirir. Calculus'un dahil olması gerekiyordu, ama aslında hiçbirini yapmadık. Sihir aslında şudur:

  1. TensorFlow, bir tf.GradientTape() altında yapılan her hesaplamanın bir hesaplama grafiğini oluşturur.
  2. TensorFlow, hesaplama grafiğindeki herhangi bir değişkenin diğer değişkenleri nasıl etkilediğini belirleyebilmesi için her işlemin türevlerini (gradyanlarını) nasıl hesaplayacağını bilir.

Süreç farklı başlangıç ​​noktalarından nasıl görünüyor?

Daha önce olduğu gibi aynı senkronize grafikler, ancak karşılaştırma için altlarında benzer bir grafik çifti ile senkronize edildi. Alt çiftin kayıp yineleme grafiği benzer ancak daha hızlı yakınsadığı görülüyor; karşılık gelen takılı çizgisi, aşağıdan ziyade veri noktalarının üstünden başlar ve nihai dinlenme yerine daha yakındır.

Gradyan inişi, optimal MSE'ye dikkat çekici bir şekilde yaklaşır, ancak aslında her iki örnekte de optimumdan önemli ölçüde farklı bir eğim ve kesişme noktasına yakınsar. Bazı durumlarda, bu, gradyan iniş algoritmalarının doğasında bulunan bir zorluk olan yerel minimuma yakınsayan gradyan inişidir. Ancak lineer regresyon kanıtlanabilir bir şekilde yalnızca bir küresel minimuma sahiptir. Peki nasıl yanlış eğime ve kesişme noktasına geldik?

Bu durumda sorun, gösteri için kodu aşırı basitleştirmemizdir. Verilerimizi normalleştirmedik ve eğim parametresi, kesişme parametresinden farklı bir özelliğe sahip. Eğimdeki küçük değişiklikler kayıpta büyük değişiklikler üretebilirken, kesişme noktasındaki küçük değişikliklerin çok az etkisi vardır. Eğitilebilir parametrelerin ölçeğindeki bu büyük fark, kesişme parametresi neredeyse göz ardı edilerek eğim hesaplamalarına hakim olan eğime yol açar.

Böylece gradyan inişi, ilk kesişim tahminine çok yakın olan en iyi eğimi etkili bir şekilde bulur. Ve hata optimuma çok yakın olduğu için etrafındaki gradyanlar küçüktür, bu nedenle her ardışık yineleme sadece küçük bir parça hareket eder. Önce verilerimizi normalleştirmek, bu fenomeni önemli ölçüde iyileştirebilirdi, ancak onu ortadan kaldırmazdı.

Bu nispeten basit bir örnekti, ancak sonraki bölümlerde bu “otomatik farklılaşma” yeteneğinin oldukça karmaşık şeylerle başa çıkabileceğini göreceğiz.

Örnek 2: Maksimal Yayılmış Birim Vektörleri

Örnek 2 Defter

Bu sonraki örnek, geçen yıl aldığım bir derin öğrenme kursundaki eğlenceli bir derin öğrenme alıştırmasına dayanmaktadır.

Sorunun özü, normal olarak dağıtılmış 32 sayıdan gerçekçi yüzler üretebilen bir "varyasyonlu otomatik kodlayıcıya" (VAE) sahip olmamızdır. Şüpheli tanımlaması için, bir tanığın seçebileceği çeşitli (teorik) yüzler oluşturmak için VAE'yi kullanmak, ardından seçilenlere benzer daha fazla yüz üreterek aramayı daraltmak istiyoruz. Bu alıştırma için, ilk vektör setinin rastgele seçilmesi önerildi, ancak ben optimal bir başlangıç ​​durumu bulmak istedim.

Problemi şu şekilde ifade edebiliriz: 32 boyutlu bir uzay verildiğinde, maksimum olarak dağılmış bir dizi X birim vektörü bulun. İki boyutta, bunu tam olarak hesaplamak kolaydır. Ancak üç boyut (veya 32 boyut!) için doğrudan bir cevap yoktur. Ancak, hedef durumumuza ulaştığımızda minimumda olan uygun bir kayıp fonksiyonu tanımlayabilirsek, belki gradyan inişi oraya ulaşmamıza yardımcı olabilir.

İki grafik. Soldaki grafik, Tüm Deneyler için Başlangıç ​​Durumu, diğer noktalara bağlı bir merkezi noktaya sahiptir ve neredeyse tamamı onun etrafında yarım daire oluşturur; bir nokta kabaca yarım dairenin karşısındadır. Sağdaki grafik, Hedef Durum, telleri eşit şekilde dağılmış bir tekerlek gibidir.

Yukarıda gösterildiği gibi rastgele 20 vektörden oluşan bir setle başlayacağız ve TensorFlow'un yeteneklerini göstermek için her biri artan karmaşıklığa sahip üç farklı kayıp işlevi deneyeceğiz.

Önce eğitim döngümüzü tanımlayalım. Tüm TensorFlow mantığını self.calc_loss() yönteminin altına koyacağız ve ardından bu döngüyü geri dönüştürerek her teknik için bu yöntemi basitçe geçersiz kılabiliriz.

 # Define the framework for trying different loss functions # Base class implements loop, sub classes override self.calc_loss() class VectorSpreadAlgorithm: # ... def calc_loss(self, tensor2d): raise NotImplementedError("Define this in your derived class") def one_iter(self, i, learning_rate): # self.vecs is an 20x2 tensor, representing twenty 2D vectors tfvecs = tf.convert_to_tensor(self.vecs, dtype=tf.float32) with tf.GradientTape() as tape: tape.watch(tfvecs) loss = self.calc_loss(tfvecs) # Here's the magic again. Derivative of spread with respect to # input vectors gradients = tape.gradient(loss, tfvecs) self.vecs = self.vecs - learning_rate * gradients

Denenecek ilk teknik en basit olanıdır. Birbirine en yakın vektörlerin açısı olan bir yayılma metriği tanımlarız. Yayılmayı en üst düzeye çıkarmak istiyoruz, ancak bunu bir minimizasyon sorunu haline getirmek gelenekseldir. Yani yayılma metriğinin negatifini alıyoruz:

 class VectorSpread_Maximize_Min_Angle(VectorSpreadAlgorithm): def calc_loss(self, tensor2d): angle_pairs = tf.acos(tensor2d @ tf.transpose(tensor2d)) disable_diag = tf.eye(tensor2d.numpy().shape[0]) * 2 * np.pi spread_metric = tf.reduce_min(angle_pairs + disable_diag) # Convention is to return a quantity to be minimized, but we want # to maximize spread. So return negative spread return -spread_metric

Bazı Matplotlib büyüsü bir görselleştirme sağlayacaktır.

Başlangıç ​​durumundan hedef duruma giden bir animasyon. Yalnız nokta sabit kalır ve yarım daire içindeki tellerin geri kalanı sırayla ileri geri titreşir, yavaşça yayılır ve 1.200 yinelemeden sonra bile eşit mesafeye ulaşmaz.

Bu hantal (tam anlamıyla!) ama işe yarıyor. Bir seferde 20 vektörden sadece ikisi güncellenir, aralarındaki boşluk artık en yakın olana kadar büyütülür, ardından en yakın iki vektör arasındaki açıyı artırmaya geçilir. Dikkat edilmesi gereken önemli şey, işe yaramasıdır . TensorFlow'un doğru olanı yapmak için tf.reduce_min() yöntemi ve tf.acos() () yöntemi aracılığıyla degradeleri geçirebildiğini görüyoruz.

Biraz daha ayrıntılı bir şey deneyelim. Optimal çözümde tüm vektörlerin en yakın komşularıyla aynı açıya sahip olması gerektiğini biliyoruz. Öyleyse kayıp fonksiyonuna “minimum açıların varyansını” ekleyelim.

 class VectorSpread_MaxMinAngle_w_Variance(VectorSpreadAlgorithm): def spread_metric(self, tensor2d): """ Assumes all rows already normalized """ angle_pairs = tf.acos(tensor2d @ tf.transpose(tensor2d)) disable_diag = tf.eye(tensor2d.numpy().shape[0]) * 2 * np.pi all_mins = tf.reduce_min(angle_pairs + disable_diag, axis=1) # Same calculation as before: find the min-min angle min_min = tf.reduce_min(all_mins) # But now also calculate the variance of the min angles vector avg_min = tf.reduce_mean(all_mins) var_min = tf.reduce_sum(tf.square(all_mins - avg_min)) # Our spread metric now includes a term to minimize variance spread_metric = min_min - 0.4 * var_min # As before, want negative spread to keep it a minimization problem return -spread_metric 

Başlangıç ​​durumundan hedef duruma giden bir animasyon. Yalnız tel sabit kalmaz, yarım daire içindeki tellerin geri kalanına doğru hızla hareket eder; Yalnızın konuştuğu her iki taraftaki iki boşluğu kapatmak yerine, titreşim şimdi zamanla büyük bir boşluğu kapatıyor. Eşitlik burada da 1.200 yinelemeden sonra tam olarak elde edilemez.

Bu yalnız kuzeye doğru vektör şimdi hızla emsallerine katılıyor, çünkü en yakın komşusuna olan açı çok büyük ve şimdi en aza indirilen varyans terimini artırıyor. Ama yine de sonuçta, yavaşlayan küresel minimum açı tarafından yönlendiriliyor. Bunu geliştirmem gereken fikirler genellikle bu 2B durumda işe yarar, ancak daha yüksek boyutlarda değil.

Ancak bu matematiksel girişimin kalitesine çok fazla odaklanmak asıl noktayı kaçırıyor. Ortalama ve varyans hesaplamalarında kaç tane tensör işleminin yer aldığına ve TensorFlow'un giriş matrisindeki her bileşen için her hesaplamayı nasıl başarılı bir şekilde izlediğine ve farklılaştırdığına bakın. Ve herhangi bir manuel hesap yapmak zorunda değildik. Biraz basit bir matematiği bir araya getirdik ve TensorFlow bizim için hesabı yaptı.

Son olarak, bir şey daha deneyelim: kuvvete dayalı bir çözüm. Her vektörün, merkezi bir noktaya bağlı küçük bir gezegen olduğunu hayal edin. Her gezegen, onu diğer gezegenlerden iten bir kuvvet yayar. Bu modelin bir fizik simülasyonunu çalıştıracak olsaydık, istediğimiz çözüme ulaşmalıyız.

Benim hipotezim, gradyan inişinin de çalışması gerektiğidir. Optimal çözümde, diğer gezegenlerden her gezegene uygulanan teğet kuvvet, net sıfır kuvvete eşit olmalıdır (sıfır olmasaydı, gezegenler hareket ediyor olurdu). O halde, her vektör üzerindeki kuvvetin büyüklüğünü hesaplayalım ve onu sıfıra doğru itmek için gradyan inişini kullanalım.

Öncelikle tf.* yöntemlerini kullanarak kuvveti hesaplayan yöntemi tanımlamamız gerekiyor:

 class VectorSpread_Force(VectorSpreadAlgorithm): def force_a_onto_b(self, vec_a, vec_b): # Calc force assuming vec_b is constrained to the unit sphere diff = vec_b - vec_a norm = tf.sqrt(tf.reduce_sum(diff**2)) unit_force_dir = diff / norm force_magnitude = 1 / norm**2 force_vec = unit_force_dir * force_magnitude # Project force onto this vec, calculate how much is radial b_dot_f = tf.tensordot(vec_b, force_vec, axes=1) b_dot_b = tf.tensordot(vec_b, vec_b, axes=1) radial_component = (b_dot_f / b_dot_b) * vec_b # Subtract radial component and return result return force_vec - radial_component

Daha sonra yukarıdaki force fonksiyonunu kullanarak kayıp fonksiyonumuzu tanımlıyoruz. Her vektör üzerindeki net kuvveti biriktirir ve büyüklüğünü hesaplarız. Optimal çözümümüzde, tüm kuvvetler birbirini götürmeli ve sıfır kuvvete sahip olmalıyız.

 def calc_loss(self, tensor2d): n_vec = tensor2d.numpy().shape[0] all_force_list = [] for this_idx in range(n_vec): # Accumulate force of all other vecs onto this one this_force_list = [] for other_idx in range(n_vec): if this_idx == other_idx: continue this_vec = tensor2d[this_idx, :] other_vec = tensor2d[other_idx, :] tangent_force_vec = self.force_a_onto_b(other_vec, this_vec) this_force_list.append(tangent_force_vec) # Use list of all N-dimensional force vecs. Stack and sum. sum_tangent_forces = tf.reduce_sum(tf.stack(this_force_list)) this_force_mag = tf.sqrt(tf.reduce_sum(sum_tangent_forces**2)) # Accumulate all magnitudes, should all be zero at optimal solution all_force_list.append(this_force_mag) # We want to minimize total force sum, so simply stack, sum, return return tf.reduce_sum(tf.stack(all_force_list)) 

Başlangıç ​​durumundan hedef duruma giden bir animasyon. İlk birkaç kare, tüm konuşmacılarda hızlı hareket görüyor ve yalnızca 200 yinelemeden sonra, genel resim zaten hedefe oldukça yakın. Toplamda yalnızca 700 yineleme gösterilir; 300'ünden sonra, açılar her karede çok az değişiyor.

Çözüm sadece güzel bir şekilde çalışmakla kalmıyor (ilk birkaç karedeki bazı kaosların yanı sıra), aynı zamanda gerçek kredi TensorFlow'a gidiyor. Bu çözüm birden çok for döngüsü, bir if ifadesi ve devasa bir hesaplama ağı içeriyordu ve TensorFlow bizim için hepsinde degradeleri başarıyla izledi.

Örnek 3: Karşıt Yapay Zeka Girdileri Oluşturma

Örnek 3 Defter

Bu noktada okuyucular, "Hey! Bu yazının derin öğrenme ile ilgili olmaması gerekiyordu!" diye düşünüyor olabilir. Ancak teknik olarak giriş, "derin öğrenme modellerinin eğitimi "nin ötesine geçmeyi ifade eder. Bu durumda, eğitim yapmıyoruz, bunun yerine önceden eğitilmiş bir derin sinir ağının bazı matematiksel özelliklerinden yararlanarak onu yanlış sonuçlar vermesi için kandırıyoruz. Bunun hayal edilenden çok daha kolay ve etkili olduğu ortaya çıktı. Ve tüm gereken, TensorFlow 2.0 kodunun bir başka kısa bloğuydu.

Saldırmak için bir görüntü sınıflandırıcı bularak başlıyoruz. Köpekler ve Kediler Kaggle Yarışması için en iyi çözümlerden birini kullanacağız; özellikle, Kaggler “uysimty” tarafından sunulan çözüm. Etkili bir kedi-köpek modeli sağladıkları ve harika belgeler sağladıkları için tüm kredi onlara aittir. Bu, 18 sinir ağı katmanında 13 milyon parametreden oluşan güçlü bir modeldir. (Okuyucular, ilgili not defterinde bununla ilgili daha fazla bilgi alabilirler.)

Lütfen buradaki amacın bu belirli ağdaki herhangi bir eksikliği vurgulamak değil , çok sayıda girdiye sahip herhangi bir standart sinir ağının nasıl savunmasız olduğunu göstermek olduğunu unutmayın.

İlgili: Ses Mantığı ve Monotonik Yapay Zeka Modelleri

Biraz kurcalayarak, modelin nasıl yükleneceğini ve sınıflandırılacak görüntülerin ön işleme tabi tutulacağını anlayabildim.

Her biri bir köpek veya kediden oluşan, karşılık gelen sınıflandırma ve güven düzeyine sahip beş örnek görüntü. Gösterilen güven seviyeleri yüzde 95 ile yüzde 100 arasında değişmektedir.

Bu gerçekten sağlam bir sınıflandırıcıya benziyor! Tüm numune sınıflandırmaları doğrudur ve %95 güvenin üzerindedir. Saldıralım!

Açıkça bir kedi olan bir görüntü üretmek istiyoruz, ancak sınıflandırıcının yüksek güven ile bir köpek olduğuna karar vermesini istiyoruz. Bunu nasıl yapabiliriz?

Doğru şekilde sınıflandırdığı bir kedi resmiyle başlayalım, ardından belirli bir giriş pikselinin her bir renk kanalındaki (0-255 değerleri) küçük değişikliklerin son sınıflandırıcı çıktısını ne kadar etkilediğini anlayalım. Bir pikseli değiştirmek muhtemelen pek bir işe yaramaz, ancak belki de 128x128x3 = 49,152 piksel değerlerinin tümünün toplu ince ayarları hedefimize ulaşacaktır.

Her pikseli hangi yöne iteceğimizi nasıl bileceğiz? Normal sinir ağı eğitimi sırasında, 13 milyon serbest parametrenin tümünü eşzamanlı olarak güncellemek için TensorFlow'da gradyan inişini kullanarak hedef etiket ile tahmin edilen etiket arasındaki kaybı en aza indirmeye çalışıyoruz. Bu durumda, bunun yerine 13 milyon parametreyi sabit bırakacağız ve girdinin piksel değerlerini ayarlayacağız.

Kayıp fonksiyonumuz nedir? Eh, görüntünün bir kediye ne kadar benzediği! Her bir girdi pikseline göre cat değerinin türevini hesaplarsak, cat sınıflandırma olasılığını en aza indirmek için her birini hangi yöne iteceğimizi biliriz.

 def adversarial_modify(victim_img, to_dog=False, to_cat=False): # We only need four gradient descent steps for i in range(4): tf_victim_img = tf.convert_to_tensor(victim_img, dtype='float32') with tf.GradientTape() as tape: tape.watch(tf_victim_img) # Run the image through the model model_output = model(tf_victim_img) # Minimize cat confidence and maximize dog confidence loss = (model_output[0] - model_output[1]) dloss_dimg = tape.gradient(loss, tf_victim_img) # Ignore gradient magnitudes, only care about sign, +1/255 or -1/255 pixels_w_pos_grad = tf.cast(dloss_dimg > 0.0, 'float32') / 255. pixels_w_neg_grad = tf.cast(dloss_dimg < 0.0, 'float32') / 255. victim_img = victim_img - pixels_w_pos_grad + pixels_w_neg_grad

Matplotlib büyüsü yine sonuçları görselleştirmeye yardımcı olur.

Sırasıyla "Kedi %99,0," "Kedi %67,3", "Köpek %71,7", "Köpek %94,3" ve "Köpek %99,4" sınıflandırmalarıyla birlikte 4 yinelemeyle birlikte orijinal bir örnek kedi görüntüsü.

Vay! İnsan gözü için bu resimlerin her biri aynıdır. Yine de dört yinelemeden sonra, sınıflandırıcıyı yüzde 99,4 güvenle bunun bir köpek olduğuna ikna ettik!

Bunun bir tesadüf olmadığından ve diğer yönde de çalıştığından emin olalım.

Sırasıyla "Köpek %98,4," "Köpek %83,9", "Köpek %54,6", "Kedi %90.4" ve "Kedi %99,8" sınıflandırmalarıyla birlikte 4 yinelemeyle birlikte orijinal bir örnek köpek resmi. Daha önce olduğu gibi, farklılıklar çıplak gözle görülmez.

Başarı! Sınıflandırıcı başlangıçta bunu yüzde 98,4 güvenle bir köpek olarak doğru tahmin etti ve şimdi yüzde 99,8 güvenle bir kedi olduğuna inanıyor.

Son olarak örnek bir resim yamasına bakalım ve nasıl değiştiğini görelim.

Her pikselin kırmızı kanalı için sayısal değerleri gösteren piksel satırları ve sütunlarından oluşan üç ızgara. Sol görüntü yaması çoğunlukla mavimsi kareler gösterir, 218 veya daha düşük değerleri vurgular ve bazı kırmızı kareler (219 ve üstü) sağ alt köşede kümelenir. Ortadaki "mağdur" resim sayfası, çok benzer şekilde renkli ve numaralandırılmış bir düzen gösterir. Sağ taraftaki görüntü yaması, diğer ikisi arasındaki sayısal farkı, yalnızca -4 ile +4 arasında değişen ve birkaç sıfır dahil olmak üzere gösterir.

Beklendiği gibi, son yama orijinale çok benziyor, her piksel kırmızı kanalın yoğunluk değerinde yalnızca -4 ila +4 arasında değişiyor. Bu kayma, bir insanın farkı ayırt etmesi için yeterli değildir, ancak sınıflandırıcının çıktısını tamamen değiştirir.

Son Düşünceler: Gradient Descent Optimizasyonu

Bu makale boyunca, basitlik ve şeffaflık adına eğitilebilir parametrelerimize degradeleri manuel olarak uygulamayı inceledik. Bununla birlikte, gerçek dünyada, veri bilimcileri, herhangi bir kod şişkinliği eklemeden çok daha etkili olma eğiliminde olduklarından, optimize edicileri kullanmaya başlamalıdır.

RMSprop, Adagrad ve Adadelta dahil olmak üzere birçok popüler optimize edici vardır, ancak en yaygın olanı muhtemelen Adam'dır . Bazen, her parametre için dinamik olarak farklı bir öğrenme oranını korudukları için "uyarlanabilir öğrenme hızı yöntemleri" olarak adlandırılırlar. Birçoğu, yerel minimumlardan kaçmak ve daha hızlı yakınsama elde etmek amacıyla momentum terimlerini ve yaklaşık yüksek dereceli türevleri kullanır.

Sebastian Ruder'dan ödünç alınan bir animasyonda, kayıp bir yüzeye inen çeşitli optimize edicilerin yolunu görebiliriz. Gösterdiğimiz manuel teknikler en çok “SGD” ile karşılaştırılabilir. En iyi performans gösteren optimize edici, her kayıp yüzeyi için aynı olmayacaktır; ancak, daha gelişmiş optimize ediciler tipik olarak daha basit olanlardan daha iyi performans gösterir.

Bir hedef noktada birleştirmek için altı farklı yöntemle alınan yolu gösteren animasyonlu bir kontur haritası. SGD, başlangıç ​​noktasından itibaren sabit bir eğri alarak açık ara en yavaş olanıdır. Momentum başlangıçta hedeften uzaklaşır, daha sonra tamamen doğrudan olmayana doğru ilerlemeden önce kendi yolunu iki kez çaprazlar ve sanki onu aşıyor ve sonra geri dönüyor gibi görünüyor. NAG benzerdir, ancak hedeften çok uzaklaşmaz ve kendisini yalnızca bir kez çapraz geçer, genellikle hedefe daha hızlı ulaşır ve onu daha az aşar. Adagrad en rota dışı olan düz bir çizgide başlar, ancak çok hızlı bir şekilde hedefin bulunduğu tepeye doğru keskin bir dönüş yapar ve ilk üçünden daha hızlı kıvrılır. Adadelta'nın da benzer bir yolu vardır, ancak daha yumuşak bir eğriye sahiptir; Adagrad'ı sollar ve ilk saniyeden sonra onun önünde kalır. Son olarak, Rmsprop Adadelta'ya çok benzer bir yol izler, ancak erken aşamada hedefe biraz daha yaklaşır; özellikle, rotası çok daha istikrarlı, bu da animasyonun çoğu için Adagrad ve Adadelta'nın gerisinde kalmasına neden oluyor; diğer beşinden farklı olarak, animasyonun sonuna doğru, hareketi durdurmadan önce iki farklı yöne iki ani, hızlı sıçramaya sahip gibi görünüyor, diğerleri ise son anda, hedef tarafından yavaşça sürünmeye devam ediyor.

Bununla birlikte, yapay zeka geliştirme hizmetleri sağlamaya hevesli olanlar için bile, optimize ediciler konusunda uzman olmak nadiren yararlıdır. TensorFlow'da gradyan inişini nasıl iyileştirdiklerini anlamak için geliştiricilerin bir çiftle tanışmaları için zamanını daha iyi kullanmaktır. Bundan sonra, varsayılan olarak Adam'ı kullanabilir ve yalnızca modelleri yakınsamıyorsa farklı olanları deneyebilirler.

Bu optimize edicilerin nasıl ve neden çalıştığıyla gerçekten ilgilenen okuyucular için, animasyonun göründüğü Ruder'ın genel bakışı, konuyla ilgili en iyi ve en kapsamlı kaynaklardan biridir.

Optimize edicileri kullanmak için ilk bölümden lineer regresyon çözümümüzü güncelleyelim. Aşağıdaki, manuel degradeleri kullanan orijinal degrade iniş kodudur.

 # Manual gradient descent operations def run_gradient_descent(heights, weights, init_slope, init_icept, learning_rate): tf_slope = tf.Variable(init_slope, dtype='float32') tf_icept = tf.Variable(init_icept, dtype='float32') for i in range(25): with tf.GradientTape() as tape: tape.watch((tf_slope, tf_icept)) predictions = tf_slope * heights + tf_icept errors = predictions - weights loss = tf.reduce_mean(errors**2) gradients = tape.gradient(loss, [tf_slope, tf_icept]) tf_slope = tf_slope - learning_rate * gradients[0] tf_icept = tf_icept - learning_rate * gradients[1]

Şimdi, bunun yerine bir optimize edici kullanan aynı kod. Fazladan kod olmadığını göreceksiniz (değişen satırlar mavi renkle vurgulanmıştır):

 # Gradient descent with Optimizer (RMSprop) def run_gradient_descent (heights, weights, init_slope, init_icept, learning_rate) : tf_slope = tf.Variable(init_slope, dtype= 'float32' ) tf_icept = tf.Variable(init_icept, dtype= 'float32' ) # Group trainable parameters into a list trainable_params = [tf_slope, tf_icept] # Define your optimizer (RMSprop) outside of the training loop optimizer = keras.optimizers.RMSprop(learning_rate) for i in range( 25 ): # GradientTape loop is the same with tf.GradientTape() as tape: tape.watch( trainable_params ) predictions = tf_slope * heights + tf_icept errors = predictions - weights loss = tf.reduce_mean(errors** 2 ) # We can use the trainable parameters list directly in gradient calcs gradients = tape.gradient(loss, trainable_params ) # Optimizers always aim to *minimize* the loss function optimizer.apply_gradients(zip(gradients, trainable_params))

Bu kadar! Gradyan iniş döngüsünün dışında bir RMSprop optimize edici tanımladık ve ardından eğitilebilir parametreleri güncellemek için her gradyan hesaplamasından sonra optimizer.apply_gradients() yöntemini kullandık. Optimize edici, momentum ve yüksek dereceli türevler gibi ekstra terimleri hesaplamak için tarihsel gradyanları takip edeceğinden döngünün dışında tanımlanır.

Bakalım RMSprop optimizer ile nasıl görünüyor.

Önceki senkronize animasyon çiftlerine benzer şekilde; takılan hat, dinlenme yerinin üzerinde başlar. Kayıp grafiği, yalnızca beş yinelemeden sonra neredeyse yakınsadığını gösteriyor.

Harika görünüyor! Şimdi Adam optimizer ile deneyelim.

Başka bir senkronize dağılım grafiği ve karşılık gelen kayıp grafiği animasyonu. Kayıp grafiği, kesinlikle minimuma yaklaşmaya devam etmemesiyle diğerlerinden ayrılıyor; bunun yerine, zıplayan bir topun yoluna benzer. Dağılım grafiğindeki karşılık gelen sabit çizgi, örnek noktaların üzerinde başlar, altlarına doğru sallanır, sonra geri gider, ancak o kadar yüksek değildir ve bu şekilde devam eder, her yön değişikliği bir merkezi konuma daha yakın olur.

Vay, ne oldu burada? Görünüşe göre Adam'daki momentum mekaniği, optimal çözümü aşmasına ve rotayı birçok kez tersine çevirmesine neden oluyor. Normalde, bu momentum mekaniği karmaşık kayıp yüzeylerde yardımcı olur, ancak bu basit durumda bize zarar verir. Bu, modelinizi eğitirken optimize edici seçimini ayarlanacak hiperparametrelerden biri yapma tavsiyesini vurgular.

Derin öğrenmeyi keşfetmek isteyen herkes, standart iş akışına kolayca sığdırılamayan karmaşık kayıp mekaniğine ihtiyaç duyulan özel TensorFlow mimarilerinde yaygın olarak kullanıldığı için bu modele aşina olmak isteyecektir. Bu basit TensorFlow gradyan inişi örneğinde, eğitilebilir yalnızca iki parametre vardı, ancak optimize etmek için yüz milyonlarca parametre içeren mimarilerle çalışırken bu gereklidir.

TensorFlow'da Gradient Descent: Minimumları Bulmaktan Yapay Zeka Sistemlerine Saldırmaya

Tüm kod parçacıkları ve görüntüler, ilgili GitHub deposundaki not defterlerinden üretildi. Ayrıca, kodun tamamını görmek isteyen okuyucular için, tek tek not defterlerine bağlantılar içeren tüm bölümlerin bir özetini içerir. İletiyi basitleştirmek adına, kapsamlı satır içi belgelerde bulunabilecek birçok ayrıntı atlandı.

Umarım bu makale bilgilendirici olmuştur ve sizi TensorFlow'da gradyan inişini kullanmanın yollarını düşünmeye sevk etmiştir. Kendiniz kullanmasanız bile, umarım tüm modern sinir ağı mimarilerinin nasıl çalıştığını daha net hale getirir - bir model oluşturun, bir kayıp işlevi tanımlayın ve modeli veri kümenize uydurmak için gradyan inişi kullanın.


Google Cloud İş Ortağı rozeti.

Bir Google Cloud İş Ortağı olarak, Toptal'ın Google sertifikalı uzmanları, en önemli projeleri için şirketlere talep üzerine hizmet vermektedir.