Yinelemeli Sinir Ağlarının (RNN) Akıl Almaz Başarısı
2015'te yayımlanmış Andrej Karpathy’nin popüler “The Unreasonable Effectiveness of Recurrent Neural Networks” yazısının Türkçe çevirisidir.
Basit Açıklama
Diyelim ki, bilgisayara bir hikaye yazmasını öğretmek istiyoruz. Normal bilgisayarlar bir kelime yazdıktan sonra, diğer kelimeleri nasıl seçeceklerini pek bilmezler. Ama RNN’ler, yazdıkları her kelimeyi hatırlarlar ve bir sonrakini seçerken bunu kullanırlar. Bu tıpkı, bir cümlenin ortasında ne söylediğimizi bilip devamını getirmek gibidir.
Karpathy, bu makalede RNN’lerin ne kadar güçlü olduklarını gösteriyor. Bu ağlar sadece cümleleri tamamlamakla kalmıyor, aynı zamanda şarkı sözleri, şiirler, hatta kod bile yazabiliyorlar. Yani, bilgisayara metin veriyoruz ve o da bu metinlerin benzerlerini yazarak devam ettirebiliyor.
RNN’ler sürekli olarak öğrendiklerinden, yazdıkları şeyler gittikçe daha iyi hale gelmektedir. İlk başta belki biraz karışık şeyler yazabilirler ancak eğitildikçe daha anlamlı ve düzgün cümleler yazmayı öğrenirler. Bu makale, RNN’lerin nasıl çalıştığını ve ne kadar etkili olduklarını anlatıyor.
Makale
Yinelemeli Sinir Ağları (RNN’ler) ile ilgili büyülü bir şey var. İlk yinelemeli ağımı Görüntü Altyazısı için eğittiğimi hâlâ hatırlıyorum. Birkaç düzine dakikalık eğitimden sonra, ilk bebek modelim (oldukça keyfi seçilmiş hiperparametrelerle), anlam kazanmanın eşiğindeki görüntülerin çok güzel görünen açıklamalarını üretmeye başladı. Bazen modelinizin ne kadar basit olduğunun, ondan aldığınız sonuçların kalitesine oranı beklentilerinizi aşar ve bu da o zamanlardan biriydi. O zamanlar bu sonucu bu kadar şok edici kılan şey, RNN’lerin eğitilmesinin zor olduğu yönündeki yaygın kanıydı (daha fazla deneyimle aslında tam tersi sonuca ulaştım). Yaklaşık bir yıl ileri saralım: RNN’leri her zaman eğitiyorum ve onların gücüne ve sağlamlığına birçok kez tanık oldum, ancak yine de onların büyülü çıktıları beni hala eğlendirmenin bir yolunu buluyor. Bu yazı bu sihrin bir kısmını sizinle paylaşmakla ilgili.
RNN’leri karakter karakter metin oluşturmaları için eğiteceğiz ve “Bu nasıl mümkün olabilir?” sorusunu düşüneceğiz.
Bu arada, bu yazıyla birlikte, çok katmanlı LSTM’lere dayalı karakter düzeyinde dil modellerini eğitmenize olanak tanıyan kodu da Github’da yayınlıyorum. LSTM’e büyük bir metin yığını verirseniz, model her seferinde bir karakter olmak üzere buna benzer metinler oluşturmayı öğrenebilir. Ayrıca, bunları aşağıdaki deneylerimi yeniden oluşturmak için de kullanabilirsiniz. Ancak konuyu dağıtmayalım; RNN’ler nedir ki?
Yinelemeli Sinir Ağları (Recurrent Neural Networks)
Sıralar (Sequences)
Eğitim geçmişinize bağlı olarak merak ediyor olabilirsiniz: Yinelemeli Ağları bu kadar özel kılan nedir? Sade Sinir Ağları’nın (ve aynı zamanda Evrişimli Ağların) belirgin bir sınırlaması, API’lerinin çok kısıtlı olmasıdır: (Görüntü gibi) sabit boyutlu bir vektörü girdi olarak kabul ederler ve (farklı sınıfların olasılıkları gibi) sabit boyutlu bir vektörü çıktı olarak üretirler. Sadece bu da değil: Bu modeller, bu eşlemeyi (modeldeki katman sayısı gibi) sabit sayıda hesaplama adımı kullanarak gerçekleştirirler. Yinelemeli ağların daha heyecan verici olmasının temel nedeni, vektör sıraları üzerinde işlem yapmamıza olanak tanımalarıdır: Girdide, çıktıda veya en genel durumda her ikisinde de sıralama yaparlar. Birkaç örnek bunu daha somut hale getirebilir:
Tahmin edebileceğiniz gibi, sabit sayıda hesaplama adımı ile baştan mahvolan sabit ağlara kıyasla, sıralı çalışma yaklaşımı çok daha güçlüdür ve bu nedenle daha zeki sistemler oluşturma hayali kuranlarımız için çok daha çekicidir. Ayrıca, birazdan göreceğimiz gibi, RNN’ler girdi vektörünü, durum vektörü ile sabit (ancak öğrenilmiş) bir işlevle birleştirerek yeni bir durum vektörü üretir. Bu, programlama terimleriyle, belirli girdiler ve bazı iç değişkenlerle sabit bir program çalıştırmak olarak yorumlanabilir. Bu şekilde bakıldığında, RNN’ler esasen programları tanımlar. RNN’lerin, keyfi programları simüle edebilecekleri anlamında (uygun ağırlıklarla) Turing-Tam (Turing-Complete) oldukları bilinmektedir. Ancak, sinir ağları için evrensel yaklaşım teoremlerine benzer şekilde, buna fazla anlam yüklememelisiniz. Söylediklerimi unutun.
Sade sinir ağlarını eğitmek fonksiyonlar üzerinde optimizasyon yapmaksa, yinelemeli ağları eğitmek programlar üzerinde optimizasyon yapmaktır.
Sıraların olmadığı sıralı işleme
Muhtemelen girdi veya çıktı olarak sıraya sahip olmanın nispeten nadir olduğunu düşünebilirsiniz, ancak önemli bir nokta şudur ki, girdileriniz/çıktılarınız sabit vektörler olsa bile, onları sıralı bir şekilde işlemek için bu güçlü formalizmi kullanmak mümkündür. Örneğin, aşağıdaki görselde DeepMind’ın iki çok güzel makalesinden elde edilen sonuçlar gösterilmiştir. İlkinde, bir algoritma, dikkatini bir görüntüde dolaştıran bir yinelemeli ağ politikasını öğrenir; Özellikle, ev numaralarını soldan sağa okumayı öğrenir (Ba et al.). İkincide, bir yinelemeli ağ, bir tuvale sırayla renk ekleyerek rakam görüntüleri üretir (Gregor et al.):
Buradan çıkarılacak sonuç, verileriniz sıralı biçiminde olmasa bile, onu sırayla işlemeyi öğrenen güçlü modelleri formüle edip eğitebileceğinizdir. Sabit boyutlu verilerinizi sıralı olarak işleyen durum tabanlı programları öğreniyorsunuz.
RNN hesaplaması
Peki RNN’ler nasıl çalışıyor? Temelde, RNN’lerin aldatıcı derecede basit bir API’si vardır: Bir girdi vektörü x’i kabul ederler ve size bir çıktı vektörü y verirler. Ancak, bu çıktı vektörünün içeriği yalnızca az önce sağladığınız girdiden değil aynı zamanda geçmişte sağladığınız girdilerin tüm geçmişinden de etkilenir. Bir sınıf olarak yazılan RNN’nin API’si tek adımlı bir fonksiyondan oluşur:
rnn = RNN()
y = rnn.step(x) # x bir girdi vektörüdür, y ise RNN'nin çıktı vektörüdür
RNN sınıfının, her adım çağrıldığında güncelleneceği bazı dahili durumları vardır. En basit durumda bu durum tek bir gizli vektör h’den oluşur. Sade RNN’de adım (step) fonksiyonunun bir uygulaması:
class RNN:
def step(self, x):
# gizli durum güncellenir
self.h = np.tanh(np.dot(self.W_hh, self.h) + np.dot(self.W_xh, x))
# çıktı vektörünü hesaplanır
y = np.dot(self.W_hy, self.h)
return y
Yukarıdaki kısım, klasik bir RNN’nin ileri geçişini belirtir. Bu RNN’in parametreleri, W_hh, W_xh ve W_hy matrisleridir. Gizli durum self.h sıfır vektörü ile başlatılır. np.tanh fonksiyonu, aktivasyonları [-1, 1] aralığına sıkıştıran bir doğrusal olmayan fonksiyondur. Bunun nasıl çalıştığına kısaca bir göz atalım: tanh fonksiyonunun içinde iki terim vardır: biri önceki gizli duruma, diğeri ise mevcut girdiye dayanır. Numpy’de np.dot matris çarpımıdır. İki ara terim toplama ile etkileşime girer ve ardından tanh ile sıkıştırılarak yeni durum vektörüne dönüştürülür. Matematiksel notasyonla daha rahat ediyorsanız, gizli durum güncellemesini ht=tanh(W_{hh}h|{t−1}+W| {xh}xt)ℎ𝑡 olarak da yazabiliriz, burada tanh eleman bazında uygulanır.
RNN’in matrislerini rastgele sayılarla başlatırız ve eğitim sırasında yapılan işin büyük kısmı, x girdi sıralarına karşılık gelen y çıktılarında görmek istediğiniz davranış türlerini ifade eden bir kayıp fonksiyonu ile ölçülen istenen davranışları ortaya çıkaran matrisleri bulmaya yöneliktir.
Derine İniyoruz
RNN’ler sinir ağlarıdır ve eğer doğru yapılırsa, derin öğrenme yaklaşımı benimsenerek modeller üst üste yığıldığında her şey tekdüze bir şekilde daha iyi çalışır. Örneğin, aşağıdaki gibi 2 katmanlı bir yinelenen ağ oluşturabiliriz:
y1 = rnn1.step(x)
y = rnn2.step(y1)
Başka bir deyişle iki ayrı RNN’imiz bulunmaktadır: Bir RNN girdi vektörlerini almakta iken ikinci RNN, girdi olarak ilk RNN’in çıktısını almaktadır. Bu RNN’lerin hiçbiri bilmez ya da umursamaz, hepsi sadece giren ve çıkan vektörler ve geri yayılım sırasında her modülden geçen bazı gradyanlardırlar.
Gelişmiş yöntemler
Uygulamada çoğumuzun yukarıda sunduğumdan biraz farklı bir formülasyon olan Uzun Kısa Süreli Bellek (LSTM) ağını kullandığını kısaca belirtmek isterim. LSTM, daha güçlü güncelleme denklemi ve cazip geri yayılım dinamikleri sayesinde pratikte biraz daha iyi çalışan belirli bir tür yinelenen ağdır. Ayrıntılara girmeyeceğim, ancak RNN’ler hakkında söylediğim her şey tamamen aynı kalmaktadır, yalnızca güncellemeyi hesaplamak için kullanılan matematiksel format (self.h = … satırı) biraz daha karmaşık hale gelmektedir. Bundan sonra “RNN/LSTM” terimlerini birbirinin yerine kullanacağım, ancak bu yazıdaki tüm deneyler bir LSTM kullanmaktadır.
Karakter Düzeyinde Dil Modelleri (Character-Level Language Models)
Tamam, şu an RNN’lerin ne olduğu, neden çok heyecan verici oldukları ve nasıl çalıştıkları hakkında bir fikrimiz var. Şimdi bunu eğlenceli bir uygulamada somutlaştıralım: Karakter düzeyinde RNN dil modelleri eğiteceğiz. Yani, RNN’e büyük bir metin parçası vereceğiz ve ondan önceki karakterler dizisi verildiğinde ardışık karakterin olasılık dağılımını modellemesini isteyeceğiz. Bu bize zamanla bir karakter biriminde yeni metin üretmemize olanak tanıyacak.
Bir örnek düşünelim, sadece dört olası harften oluşan bir kelime dağarcığımız olduğunu ve “hello” eğitim dizisinde bir RNN eğitmek istediğimizi varsayalım. Bu eğitim dizisi aslında 4 ayrı eğitim örneği sunmaktadır: 1. “h” bağlamında “e” olasılığı muhtemel olmalıdır, 2. “he” bağlamında “l” muhtemelen olmalıdır, 3. “hel” bağlamında da “l” muhtemelen olmalıdır ve son olarak 4. “hell” bağlamında “o” muhtemelen olmalıdır.
Somut olarak, her karakteri, sözlükteki karakterin indeksinde tek bir tane dışında hepsi sıfır olan “1-of-k” (k’den biri) kodlaması kullanarak bir vektöre dönüştüreceğiz ve bunları adım fonksiyonu ile birer birer RNN’e besleyeceğiz. Daha sonra, RNN’nin dizide bir sonraki karakter olarak her bir karaktere şu anda atadığı güveni yorumladığımız 4 boyutlu (her karakter başına bir boyutlu) çıktı vektörlerinin bir sırasını gözlemleyeceğiz. İşte bunun bir diyagramı:
Örneğin, ilk zaman adımında RNN’in “h” karakterini gördüğünde bir sonraki harf olarak “h” için 1.0, “e” için 2.2, “l” için -3.0 ve “o” için 4.1 güven değeri atadığını görüyoruz. Eğitim verilerimizde (yani “hello” dizgisi) doğru karakter “e” olduğu için, onun güvenini arttırmak (yeşil) ve diğer tüm harflerin güvenini azaltmak (kırmızı) istiyoruz. Benzer şekilde, her bir zaman adımında ağın daha yüksek güven değeri atamasını istediğimiz bir hedef karakterimiz var. RNN tamamen türevlenebilir işlemlerden oluştuğundan, geri yayılım algoritmasını çalıştırarak (bu, sadece kalkülüste zincir kuralının yinelemeli uygulanmasıdır) doğru hedeflerin (yeşil kalın sayılar) puanlarını artırmak için ağırlıklarının her birini hangi yönde ayarlamamız gerektiğini belirleyebiliriz. Daha sonra, her ağırlığı bu gradyan yönünde küçük bir miktar dürten bir parametre güncellemesi gerçekleştirebiliriz. Parametre güncellemesinden sonra aynı girdileri RNN’e verirsek, doğru karakterlerin (örneğin ilk zaman adımında “e”) puanlarının biraz daha yüksek (örneğin 2.2 yerine 2.3) ve yanlış karakterlerin puanlarının biraz daha düşük olduğunu görürüz. Bu işlemi, ağın yakınsayıp tahminlerinin eğitim verileriyle tutarlı olana kadar tekrar tekrar uygularız, böylece her zaman doğru karakterler bir sonraki olarak tahmin edilebilir.
Daha teknik bir açıklama olarak, her çıktı vektörü üzerinde aynı anda standart Softmax sınıflandırıcısını (genellikle çapraz entropi kaybı olarak da anılır) kullanıyoruz. RNN, mini-toplam (mini-batch) Stokastik Gradyan İnişi ile eğitilir ve güncellemeleri stabilize etmek için RMSProp veya Adam (parametre başına uyarlanabilir öğrenme oranı yöntemleri) kullanmayı tercih ediyorum.
Ayrıca “l” karakteri ilk kez girildiğinde hedefin “l”, ikinci seferde ise hedefin “o” olduğuna dikkat edin. Bu nedenle RNN yalnızca girdiye güvenemez ve bu görevi gerçekleştirmek için bağlamı takip etmek amacıyla yinelenen bağlantısını kullanmak zorundadır.
Test zamanında, RNN’e bir karakter besliyoruz ve bir sonraki karakterin hangi karakterlerin gelebileceğine dair bir dağılım elde ediyoruz. Bu dağılımdan örnekler alıyoruz ve bir sonraki mektubu almak için onu hemen geri besliyoruz. Bu işlemi tekrarlayıp ve metni örnekleyin! Şimdi bir RNN’i farklı veri kümeleri üzerinde eğitelim ve ne olacağını görelim.
Daha net açıklamak için, eğitim amaçlı olarak Python/numpy kullanarak minimal bir karakter seviyesi RNN dil modeli yazdım. Yaklaşık 100 satır uzunluğunda ve kod okumayı metin okumaya tercih edenler için kısa, somut ve faydalı bir özet sunuyor. Şimdi, çok daha verimli olan Lua/Torch kod tabanı ile üretilen örnek sonuçları inceleyeceğiz.
Test zamanı geldiğinde, RNN’e bir karakter besleriz ve sonraki karakterlerin olasılık dağılımını elde ederiz. Bu dağılımdan bir örnek alır ve bir sonraki harfi elde etmek için tekrar RNN’e besleriz. Bu işlemi tekrarlayarak metin örneklemesi yapmış oluruz! Şimdi farklı veri kümeleri üzerinde bir RNN eğitelim ve neler olduğunu görelim.
RNN’lerle Eğlence
Altta yer alan 5 örnek karakter modeli, Github’da yayınladığım kod ile eğitildi. Her bir durumda girdi, içinde bir miktar metin bulunan tek bir dosyadır ve bir RNN’i sıradaki karakteri tahmin etmek üzere bu verilerle eğitiriz.
Paul Graham Üretici (Generator)
Öncelikle, doğrulama amacıyla küçük bir İngilizce veri kümesi deneyelim. Favori eğlenceli amaçlı veri kümem, Paul Graham’ın denemelerinin birleşimidir. Temel fikir, bu denemelerde çok fazla bilgelik bulunmasıdır, ancak ne yazık ki Paul Graham’ın yazı yazma hızı nispeten yavaştır. İstediğimiz zaman onun bilgeliğini alabilseydik harika olmaz mıydı? İşte burada bir RNN devreye giriyor.
Son 5 yıl içinde yazılan tüm pg makalelerini birleştirdiğimizde, yaklaşık 1MB’lık bir metin dosyası, yani yaklaşık 1 milyon karakter elde ediyoruz (bu arada bu boyutlu bir veri kümesi çok küçük bir veri kümesi olarak kabul edilir). Tekniğimiz: 512 gizli düğüme sahip 2 katmanlı bir LSTM eğitelim (yaklaşık 3.5 milyon parametreli), ve her katmandan sonra 0.5 sönümleme (dropout) uygulayalım. 100 örnekten oluşan gruplarla ve 100 karakter uzunluğunda kısaltılmış geriye yayılma (backpropogation) ile eğitim yapacağız. Bu ayarlarla bir TITAN Z GPU’da bir grup yaklaşık 0.46 saniye sürer (performansta ihmal edilebilir bir maliyetle 50 karakter BPTT kullanarak bu süre yarıya indirilebilir). Lafı fazla uzatmadan, RNN’den bir örnek görelim:
“Yatırımcılar şaşırmıştı ve para toplamayacaklardı. Zamanı olan şirket ben değilim, ilginç olan her şey hızla oluyor, aynı programcıları kaldırmanıza gerek yok. Süper-melek turu fon toplama var, neden yapabiliyorsunuz. Farklı bir fiziksel yatırımınız varsa, bir startupta azalan insanlarda olursunuz, satın alanın onları görmesi argümanıyla sadece kurucuların da kullanıcıların bir parçası olacağı ve fikre bir alternasyon olacağı bir yol. [2] İlk üyede çalışmayın, kötü bir başarılı startupın önünde çocukların nasıl görüneceğini görmek için. Ve büyük şirketle de hareket etmeniz gerekiyorsa.”
Tamam, yukarıdaki metin maalesef Paul Graham’in yerini alamaz, ancak şunu unutmayın ki RNN İngilizce’yi tamamen sıfırdan ve küçük bir veri kümesi ile öğrenmek zorunda kaldı (virgülleri, kesme işaretlerini ve boşlukları nereye koyacağınızı da dahil). Ayrıca, kendi argümanlarını desteklemeyi öğrenmesini de beğeniyorum (örneğin, yukarıdaki [2]). Bazen “bir şirket yatırımcılarla düşünmek için bir toplantıdır” gibi içgörü sunan bir şeyler söylüyor. Daha fazlasını görmek isterseniz, işte 50 bin karakterlik bir örnek bağlantısı.
Sıcaklık (Temperature)
Örnekleme sırasında Softmax’in sıcaklığıyla da oynayabiliriz. Sıcaklığı 1'den daha düşük bir sayıya (örneğin 0.5) düşürmek, RNN’i daha güvenli hale getirir, ancak örneklerinde daha tutucu olmasına neden olur. Tersine, daha yüksek sıcaklıklar daha fazla çeşitlilik sağlar, ancak daha fazla hata yapma (örneğin, yazım hataları vb.) pahasına olur. Özellikle, sıcaklığı sıfıra çok yakın bir değere ayarlamak, Paul Graham’ın söyleyebileceği en olası şeyi verir:
“hepsinin bir startup olduğu şeyin aynısı olmasıdır hepsinin bir startup olduğu şeyin aynısı olmasıdır hepsinin bir startup olduğu şeyin aynısı olmasıdır hepsinin bir startup olduğu şeyin aynısı olmasıdır”
Startup’larla ilgili sonsuz bir döngüye ulaştık gibi görünüyor.
Shakespeare
Görünüşe göre İngilizce kelimelerin yazılışını öğrenebiliriz. Ancak, verilerde daha fazla yapı ve stil olduğunda ne olur? Bunu incelemek için Shakespeare’in tüm eserlerini indirip tek bir dosyada (4.4MB) birleştirdim. Artık daha büyük bir ağı eğitme imkanımız var, bu durumda her katmanda 512 gizli düğüme sahip 3 katmanlı bir RNN deneyelim. Ağı birkaç saat eğittikten sonra şu tür örnekler elde ediyoruz:
- Pandarus:
Ne yazık ki, sanırım ona yaklaşılacak ve o gün,
hiç beslenmeyecek kadar az sıkıntı yaşanacak,
Ve onun ölümü için bir zincir ve tebaadan başka bir şey olmayan,
ben uyumamalıyım.
İkinci Senatör:
Ruhumun üzerinde oluşan bu sefaletleri uzaktalar,
Kırılıyorlar ve ben yok olduğumda güçlü bir şekilde gömülmeliler Dünyayı ve birçok devletin düşüncelerini.DÜK VINCENTIO:
Senin zekan Side'nin ve diğerlerinin elinde.İkinci Efendi:
Bu meclisten sonra yönetilecekler ve benim güzel sözlerim gerçeklerden yola çıkarak aktarılacak, Kimin asil ruhları savaşların kalbine sahip olacağım.Palyaço:
Gelin efendim, ibadetinizi seyrettireceğim.VİYOLA:
Ben içeceğim.
Unutmayın, RNN’nin bildiği tek şey karakterlerdir, bundan dolayı RNN özellikle hem konuşmacının adlarını hem de içeriğini örneklendirir. Bazen nispeten daha geniş monolog pasajları da alabiliriz, örneğin:
VİYOLA:
Salisbury etini bulup düşünmeli
Aps olmadığım, insan olmadığım ve ateşte olmadığım şey
Kuzgunların dizginlerini ve savaşları göstermek için
Elimi lütfetmek için içimde sitem var ve adil bir el değil
Şu Sezar'ın ve sevgili babamın dünyası
Ben varlık cennetiyken ve filolarımızken, Saatlerle ayırdık ama senin konseyini kestim Ben büyüğüm
Öldürüldü ve efendin orada hazır
Gücüm sana ancak cehennem kadarını verecek: Buradaki asil köleye biraz hizmet
Ona şarabını gösterecektim.
KRAL LEAR:
Ah, eğer kanunların lütfuyla, zayıf bir görüşe sahip olsaydın
Görüşünüz ve birkaç nefesiniz tanrıları yıpratacak
Onun başlarıyla, benim ellerimle amelleri hayrete düşüyor
Öyleyse efendinizin başına ve fikrinize bırakın; Şerefinize aykırı olacaktır.
Bu örnekleri gerçek Shakespeare’den zar zor tanıyabiliyorum :) Eğer Shakespeare’i seviyorsanız bu 100.000 karakter örneğini beğenebilirsiniz. Elbette verilen kodla farklı sıcaklıklarda sonsuz miktarda kendi örnekleminizi de oluşturabilirsiniz.
Vikipedi
LSTM’in kelimeleri hecelemeyi ve genel sözdizimsel yapıları kopyalamayı öğrenebildiğini gördük. Zorluk seviyesini artırarak yapılandırılmış markdown üzerinde eğitim yapalım. Özellikle, “Hutter Ödüllü 100MB’lık Ham Wikipedia Veri Kümesi”ni alıp bir LSTM üzerinde eğitelim. Graves ve arkadaşlarının izinden giderek, ilk 96MB’ı eğitim için, geri kalanını ise doğrulama için kullandım ve birkaç modeli gece boyunca çalıştırdım. Artık Wikipedia makalelerinden örnekler alabiliriz! Aşağıda bazı eğlenceli alıntılar var. İlk olarak, basit bir markdown çıktısı:
Arap ülkelerinin başkentinin çoğunluğu için natüralizm ve karar, Guangzham'ın egemenliğiyle ilişkilendirilen [[John Clair]], [[Japon İmparatorluk İsyanı]] tarafından İrlanda dili tarafından temellendirildi. Onun generalleri, doğrudan Kanton Tebliği'nde olduğu söylenebilecek [[Protestan İmmineners]]'da Portekiz'in güçlü hükümdarıydı; bir törenle takip edilen ve hapishane ve eğitimden ilham alan bir eğitimdi. İmparator, Kosta Rika Krallığı'nın batıda [[İskoçya]] bilinen [[Thrales]], [[Cynth'in Dajoard'ı]]'nı başarısız bir şekilde şekillendirdiğini not etmek için [[Antakya, Perth, 25|21 Ekim]]'e geri döndü. , İtalya yakınlarında çıkan çatışmayla Hindistan'ın fethine kadar. Telif hakkı, daha popüler, hizmetkar, doktrinsel olmayan ve cinsel güç makamına dayanan ünlü bir Alman hareketi olan Suriye nüfuzunun çöküşünde bağımsızlığın ardı ardına gelmesiydi. Pek çok hükümet, [[Pencap Kararı]]'na sempati duyan [[Sivil Liberalleşme ve Piyade Kararı 265 Macaristan Ulusal Partisi]]'nin askeri konutlarını tanıyor.
(PJS)[http://www.humah.yahoo.com/guardian. cfm/7754800786d17551963s89.htm Resmi ekonomi Nazizm'e bitişik olan Montgomery, Sosyalizmin yönetimi için kaynaklara ilerlemeye yemin etmişti ve büyük bir sürgün yardımı üçlüsüne imza atmaya başlamıştı.]]
Merak ediyorsanız, yukarıdaki Yahoo URL’si gerçek değil, model sadece halüsinasyon gördü. Ayrıca modelin parantezi doğru şekilde açıp kapatmayı öğrendiğini unutmayın. Modelin öğrendiği pek çok yapılandırılmış işaretleme de vardır; örneğin bazen başlıklar, listeler vb. oluşturabilmektedir:
{ { alıntılanan dergi| id=Cerling Orman Dışı Departmanı|format=Newlymeslated|none } }
''www.e-tamamlandı''.
'''Ayrıca bakınız''': [[Etik onay işleme listesi]]== Ayrıca bakınız ==
*[[ED'nin Iender kubbesi]]
*[[Anti-otizm]]===[[Din|Din]]===
*[[Fransızca Yazılar]]
*[[Maria]]
*[[Vahiy]]
*[[Agamul Dağı]]== Dış bağlantılar==
* [http://www.biblegateway.nih.gov/entrepre/ Dünya Festivali'nin web sitesi. Hindistan eyaletinin işçileri Kaliforniya Yolu'ndaki Ripper'da yenilgiye uğradı.]==Dış bağlantılar==
* [http://www.romanology.com/ Hollanda Anayasası ve Çift Bilabial ve İngiliz Milletler Topluluğu Endüstrisi için İspanyol Rekabeti (Hollanda Kapsamında Cumhuriyetçi Anayasası)]
Bazen model rastgele fakat geçerli bir XML oluşturma moduna geçer:
<page>
<title>Antichrist</title>
<id>865</id>
<revision>
<id>15900676</id>
<timestamp>2002-08-03T18:14:12Z</timestamp>
<contributor>
<username>Paris</username>
<id>23</id>
</contributor>
<minor />
<comment>Automated conversion</comment>
<text xml:space="preserve">#REDIRECT [[Christianity]]</text>
</revision>
</page>
Model tamamen zaman damgasını, kimliği vb. oluşturabilir. Ayrıca doğru etiketleri uygun şekilde ve doğru iç içe sırayla kapattığını unutmayın. Daha fazlasını görmek istiyorsanız işte Wikipedia’nın 100.000 karakterlik örneği: [https://cs.stanford.edu/people/karpathy/char-rnn/wiki.txt]
Cebirsel Geometri (LaTeX)
Yukarıdaki sonuçlar, modelin karmaşık sözdizimsel yapıları öğrenmede oldukça iyi olduğunu göstermektedir. Bu sonuçlardan etkilenen laboratuvar arkadaşım (Justin Johnson) ve ben, daha da yapılandırılmış alanlara yönelmeye karar verdik ve cebirsel yığınlar/geometri üzerine bir kitap bulduk. Ham LaTeX kaynak dosyasını (16MB’lık bir dosya) indirdik ve çok katmanlı bir LSTM eğittik. İnanılmaz bir şekilde, elde edilen örnek LaTeX neredeyse derleniyordu. Bazı sorunları manuel olarak düzeltmek zorunda kaldık, ancak sonrasında elde edilen matematik oldukça inandırıcı görünüyordu, bu gerçekten şaşırtıcı:
İşte başka bir örnek:
Yukarıda görebileceğiniz gibi, model bazen LaTeX diyagramları oluşturmaya çalışır ancak açıkça bunları çözememiştir. Ayrıca bir ispatı atlamayı seçtiği kısmı da sevdim (sol üstte “Kanıt atlandı.”). Tabii ki, laTeX’in benim bile tam olarak hakim olmadığım nispeten zor yapılandırılmış bir sözdizimsel formata sahip olduğunu unutmayın. Örneğin, modelden ham bir örnek (düzenlenmemiş) şu şekildedir:
\begin{proof}
$\mathcal{I}$'nin $\mathcal{C}$ üzerinde bir abelian katman olduğunu varsayabiliriz.
\item $\Delta : \mathcal{F} \to \mathcal{I}$ bir morfizm verilmiş olsun, $\mathcal{I}$ enjekte edicidir ve $\mathfrak q$ X üzerinde bir abelian katman olsun.
$\mathcal{F}$ fiberli bir kompleks olsun. $\mathcal{F}$ bir kategori olsun.
\begin{enumerate}
\item \hyperref[setain-construction-phantom]{Lema}
\label{lemma-characterize-quasi-finite}
$\mathcal{F}$ $\mathcal{C}$ üzerinde abelian yarı-uyumlu bir katman olsun.
$\mathcal{F}$ uyumlu bir $\mathcal{O}_X$-modül olsun. O zaman
$\mathcal{F}$ $\mathcal{C}$ üzerinde abelian katenaridir.
\item Aşağıdakiler eşdeğerdir
\begin{enumerate}
\item $\mathcal{F}$ bir $\mathcal{O}_X$-modüldür.
\end{lemma}
Nispeten iyi bir modelden alınan bu örnek, birkaç yaygın hatayı göstermektedir. Örneğin, model bir \begin{proof} ortamı açar ancak daha sonra bunu bir \end{lemma} ile bitirir. Bu, elle düzeltmemiz gereken bir soruna bir örnektir ve muhtemelen bağımlılığın çok uzun vadeli olmasından kaynaklanmaktadır: Model, kanıtla işi bittiğinde, kanıt mı yoksa lemma mı yaptığını unutmuştur. Benzer şekilde, bir \begin{enumerate} açar ancak sonra onu kapatmayı unutur. Daha büyük/daha iyi modellerde bunların daha az yaygınlaştığını gözlemledik ancak bunlar ortaya çıkan türde hatalardır.
Linux Kaynak Kodu (Linux Source Code)
Yapılandırılmış veriyi sınırına kadar zorlamak istedim, bu yüzden son meydan okuma için kod kullanmaya karar verdim. Özellikle, Github’daki Linux reposunda bulunan tüm kaynak ve başlık dosyalarını aldım ve hepsini tek bir devasa dosyada (474MB C kodu) birleştirdim (başlangıçta yalnızca çekirdek üzerinde eğitmeyi planlıyordum, ancak bu tek başına sadece ~16MB yer tutuyordu). Daha sonra birkaç gün boyunca GPU’ma sığacak şekilde mümkün olan en büyük 3 katmanlı LSTM’leri eğittim. Bu modeller yaklaşık 10 milyon parametreye sahip, ki bu hala RNN modelleri için düşük bir değerdir. Sonuçlar gerçekten çok eğlenceli:
/*
* Yeni hatalı UI_FILTER grup bilgilerinin boyut dosyasının boyutunu üretken bir şekilde artırın.
*/
static int indicate_policy(void)
{
int error;
if (fd == MARN_EPT) {
/*
* Boş çekirdek onu kullanıcı alanına yönlendirecektir.
*/
if (ss->segment < mem_total)
unblock_graph_and_set_blocked();
else
ret = 1;
goto bail;
}
segaddr = in_SB(in.addr);
selector = seg / 16;
setup_works = true;
for (i = 0; i < blocks; i++) {
seq = buf[i++];
bpf = bd->bd.next + i * search;
if (fd) {
current = blocked;
}
}
rw->name = "Getjbbregs";
bprm_self_clearl(&iv->version);
regs->new = blocks[(BPF_STATS << info->historidac)] | PFMR_CLOBATHINC_SECONDS << 12;
return segtable;
}
Kod genel olarak gerçekten oldukça harika görünüyor. Elbette, derlenebileceğini düşünmüyorum, ancak oluşturulan koda göz gezdirdiğinizde devasa bir C kod tabanı gibi hissettiriyor. RNN, koduna rastgele yerlerde yorumlar ekliyor. Ayrıca, çok az sözdizimsel hata yapmamaak konusunda çok iyi. Örneğin, dizgeleri doğru kullanıyor, işaretçi notasyonunu kullanıyor vb. ayrıca parantezleri {[ doğru şekilde açıp kapatıyor ve kodunu çok iyi şekilde girintiliyor. Yaygın bir hata, değişken adlarını takip edememesi: Genellikle tanımlanmamış değişkenler kullanıyor (örneğin, yukarıdaki rw), hiç kullanmadığı değişkenleri tanımlıyor (örneğin, int error) veya var olmayan değişkenleri döndürüyor. Birkaç örneğe daha bakalım. İşte RNN’nin öğrendiği daha geniş bir operasyon yelpazesini gösteren başka bir kod parçası:
/*
* Bu hata ayarlanırsa, BSD'den hemen sonra herhangi bir şeye ihtiyacımız olacak.
*/
static void action_new_function(struct s_stat_info *wb)
{
unsigned long flags;
int lel_idx_bit = e->edd, *sys & ~((unsigned long) *FIRST_COMPAT);
buf[0] = 0xFFFFFFFF & (bit << 4);
min(inc, slist->bytes);
printk(KERN_WARNING "Memory allocated %02x/%02x, "
"original MLL instead\n"),
min(min(multi_run - s->len, max) * num_data_in),
frame_pos, sz + first_seg);
div_u64_w(val, inb_p);
spin_unlock(&disk->queue_lock);
mutex_unlock(&s->sock->mutex);
mutex_unlock(&func->mutex);
return disassemble(info->pending_bh);
}
static void num_serial_settings(struct tty_struct *tty)
{
if (tty == tty)
disable_single_st_p(dev);
pci_disable_spool(port);
return 0;
}
static void do_command(struct seq_file *m, void *v)
{
int column = 32 << (cmd[2] & 0x80);
if (state)
cmd = (int)(int_state ^ (in_8(&ch->ch_flags) & Cmd) ? 2 : 1);
else
seq = 1;
for (i = 0; i < 16; i++) {
if (k & (1 << 1))
pipe = (in_use & UMXTHREAD_UNCCA) +
((count & 0x00000000fffffff8) & 0x000000f) << 8;
if (count == 0)
sub(pid, ppc_md.kexec_handle, 0x20000000);
pipe_set_bytes(i, 0);
}
/* Tümü kısa çizgi ise kamerayı yerleştirmek için kullanıcı sayfalarımızın işaretçisini serbest bırakın */
subsystem_info = &of_changes[PAGE_SIZE];
rek_controls(offset, idx, &soffset);
/* Şimdi bunu kasıtlı olarak cihaza koymak istiyoruz */
control_check_polarity(&context, val, 0);
for (i = 0; i < COUNTER; i++)
seq_puts(s, "policy ");
}
İkinci fonksiyonda modelin tty == tty’yi karşılaştırdığına dikkat edin ki bu her zaman “true” ile sonuçlanır. Öte yandan, en azından bu sefer tty değişkeni kapsam içinde mevcut! Son fonksiyonda ise kodun hiçbir şey döndürmediğine dikkat edin, bu da fonksiyonun dönüş türünün void olması nedeniyle doğru. Ancak, ilk iki fonksiyon da void olarak tanımlanmış ama değer döndürmüşler. Bu, uzun süreli etkileşimler (long time interactions) nedeniyle yaygın bir hatanın bir başka biçimidir.
Bazen model yeni bir dosyayı örnekleme zamanının geldiğine karar verir. Bu genellikle çok eğlenceli bir kısımdır: Model önce GNU lisansını karakter karakter okur, birkaç içeriği örnekler, bazı makrolar üretir ve ardından kodun içine dalar:
/*
* Telif Hakkı (c) 2006-2010, Intel Mobile Communications. Tüm hakları saklıdır.
*
* Bu program özgür bir yazılımdır; onu GNU Genel Kamu Lisansı sürüm 2'ye
* göre, Özgür Yazılım Vakfı tarafından yayımlandığı şekilde yeniden dağıtabilir
* ve/veya değiştirebilirsiniz.
*
* Bu programın yararlı olacağı umuduyla dağıtılmaktadır,
* ancak HERHANGİ BİR GARANTİ VERİLMEMEKTEDİR; hatta
* TİCARİLEŞTİRİLEBİLİRLİK veya BELİRLİ BİR AMACA UYGUNLUK
* garantisi dahi verilmemektedir. Daha fazla bilgi için
*
* GNU Genel Kamu Lisansına bakınız.
*
* Bu programla birlikte GNU Genel Kamu Lisansının bir kopyasını almış olmalısınız;
* eğer almadıysanız, lütfen Özgür Yazılım Vakfına yazın:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, ABD.
*/
#include <linux/kexec.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/multi.h>
#include <linux/ckevent.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/e820.h>
#include <asm/system_info.h>
#include <asm/setew.h>
#include <asm/pgproto.h>
#define REG_PG vesa_slot_addr_pack
#define PFM_NOCOMP AFSR(0, load)
#define STACK_DDR(type) (func)
#define SWAP_ALLOCATE(nr) (e)
#define emulate_sigs() arch_get_unaligned_child()
#define access_rw(TST) asm volatile("movd %%esp, %0, %3" : : "r" (0)); \
if (__type & DO_READ)
static void stat_PC_SEC __read_mostly offsetof(struct seq_argsqueue, \
pC>[1]);
static void
os_prefix(unsigned long sys)
{
#ifdef CONFIG_PREEMPT
PUT_PARAM_RAID(2, sel) = get_state_state();
set_pid_sum((unsigned long)state, current_state_str(),
(unsigned long)-1->lr_full; low;
}
Ele alınacak çok fazla eğlenceli kısım var; muhtemelen sadece bu kısım üzerine bir blog yazısı bile yazabilirim. Şimdilik kısa keseceğim ama sırf merak edenler için işte 1 MB’lık örnek Linux kodu: [http://cs.stanford.edu/people/karpathy/char-rnn/linux.txt]
Bebek Adları Oluşturma (Generating Baby Names)
Eğlenmek için bir tane daha deneyelim. RNN’e, her satırda bir tane olmak üzere listelenmiş 8000 bebek ismini içeren büyük bir metin dosyası besleyelim. Bunu RNN’e besleyebilir ve ardından yeni isimler oluşturabiliriz! Burada yalnızca eğitim verilerinde yer almayanları (%90'ı yok) gösteren bazı örnek adlar verilmiştir:
Rudi Levette Berice Lussa Hany Mareanne Chrestina Carissy Marylen Hammine Janye Marlise Jacacrie Hendred Romand Charienna Nenotto Ette Dorane Wallen Marly Darine Salina Elvyn Ersia Maralena Minoria Ellia Charmin Antley Nerille Chelon Walmor Evena Jeryly Stachon Charisa Allisa Anatha Cathanie Geetra Alexie Jerin Cassen Herbett Cossie Velen Daurenge Robester Shermond Terisa Licia Roselen Ferine Jayn Lusine Charyanne Sales Sanny Resa Wallon Martine Merus Jelen Candica Wallin Tel Rachene Tarine Ozila Ketia Shanne Arnande Karella Roselina Alessia Chasty Deland Berther Geamar Jackein Mellisand Sagdy Nenc Lessie Rasemy Guen Gavi Milea Anneda Margoris Janin Rodelin Zeanna Elyne Janah Ferzina Susta Pey Castina
Daha fazlasını burada görebilirsiniz: [http://cs.stanford.edu/people/karpathy/namesGenUnique.txt]
Favorilerimden bazıları arasında “Baby” (haha), “Killie”, “Char”, “R”, “More”, “Mars”, “Hi”, “Saddie”, “With” ve “Ahbort” yer alıyor. Bu çok eğlenceliydi. Elbette bunun bir roman yazarken veya yeni bir girişime isim verirken oldukça faydalı bir ilham kaynağı olarak kullanılabileceğini düşünebilirsiniz. :)
Neler olup bittiğini anlamak
Eğitimin sonunda sonuçların etkileyici olabileceğini gördük ama bunlar nasıl çalışıyor? Kısaca kaputun altına göz atmak için iki hızlı deney yapalım.
Eğitim Sırasında Örneklerin Evrimi
Öncelikle, model eğitilirken örneklenen metnin nasıl evrildiğine bakmak eğlencelidir. Örneğin, Lev Tolstoy’un Savaş ve Barış adlı eserinin bir LSTM modelini eğittim ve ardından her 100 iterasyonda örnekler ürettim. 100. iterasyonda model rastgele karışık örnekler üretiyor:
tyntd-iafhatawiaoihrdemot lytdws e ,tfti, astai f ogoh eoase rrranbyne 'nhthnee e
plia tklrgd t o idoe ns,smtt h ne etie h,hregtrs nigtike,aoaenns lng
Modelin en azından boşluklarla ayrılmış kelimeler hakkında bir fikir edinmeye başladığına dikkat edin. Bazen iki boşluk eklemesi dışında. Ayrıca virgülün hemen hemen her zaman ardından boşluk geldiğini de bilmiyor.
300 yinelemede modelin tırnak işaretleri ve noktalar hakkında fikir edinmeye başladığını görüyoruz:
"Tmont thithey" fomesscerliund
Keushey. Thom here
sheulke, anmerenith ol sivh I lalterthend Bleipile shuwy fil on aseterlome
coaniogennc Phe lism thond hon at. MeiDimorotion in ther thize."
Artık kelimeler de boşluklarla ayrılmış durumda ve model, cümlenin sonundaki noktalar hakkında fikir edinmeye başlıyor.
500. yineleme:
we counter. He stutn co des. His stanted out one ofler that concossions and was
to gearang reay Jotrets and with fre colt otf paitt thin wall. Which das stimn
Model artık ‘we’, ‘He’, ‘His’, ‘Which’, ‘and’ gibi en kısa ve en yaygın kelimeleri hecelemeyi öğrenmiş durumda.
700. yinelemede giderek daha fazla İngilizce benzeri metin ortaya çıkmaya başlıyor:
Aftair fall unsuch that the hall for Prince Velzonski's that me of
her hearly, and behs to so arwage fiving were to it beloge, pavu say falling misfort
how, and Gogition is so overelical and ofter.
1200. yinelemede artık alıntıları ve soru/ünlem işaretlerini görüyoruz. Artık daha uzun kelimeler de kullanılıyor:
"Kite vouch!" he repeated by her
door. "But I would be done and quarts, feeling, then, son is people...."
Ta ki en sonunda yaklaşık 2000 yinelemeyle sözcükleri, alıntıları, adları ve benzerlerini düzgün bir şekilde hecelemeye başlıyor:
"Why do what that day," replied Natasha, and wishing to himself the fact the
princess, Princess Mary was easier, fed in had oftened him.
Pierre aking his soul came to the packs and drove up his father-in-law women.
“Neden o günü yapasın,” diye cevapladı Natasha ve prensesin, Prenses Mary'nin daha kolay olduğunu ve onun için sık sık beslendiğini kendi kendine düşünerek. Pierre ruhunu sorarak paketlerin yanına geldi ve kayınpederinin kadınlarını sürdü.
Ortaya çıkan resim, modelin önce genel kelime-uzayı yapısını keşfetmesi, ardından hızla kelimeleri öğrenmeye başlamasıdır; Önce kısa kelimelerle başlayıp daha sonra uzun kelimeler kullanmaya başlıyor. Birden fazla kelimeyi kapsayan konular ve temalar (ve genel olarak uzun vadeli bağımlılıklar) ancak çok daha sonra ortaya çıkmaya başlıyorr.
RNN’lerdeki tahminleri ve “nöron” ateşlemelerini görselleştirme
Başka bir eğlenceli görselleştirme, karakterler üzerindeki tahmin edilen dağılımlara bakmaktır. Aşağıdaki görselleştirmelerde, bir Wikipedia RNN modeline doğrulama kümesinden karakter verileri besliyoruz (mavi/yeşil satırlar boyunca gösterilen) ve her karakterin altında modelin bir sonraki karakter için atadığı ilk 5 tahmini (kırmızıyla) görselleştiriyoruz. Tahminler olasılıklarına göre renklendirilmiştir (yani koyu kırmızı = çok olası olarak değerlendirilen, beyaz = çok olası olarak değerlendirilmeyen). Örneğin, modelin bir sonraki harf hakkında son derece emin olduğu karakter dizileri olduğunu fark edin (örneğin, model http://www. dizisi sırasında karakterler hakkında çok emin).
Girdi karakter dizisi (mavi/yeşil), RNN’in gizli temsilindeki rastgele seçilmiş bir nöronun ateşlenmesine göre renklendirilmiştir. Bunu, yeşil = çok heyecanlı ve mavi = çok heyecanlı değil olarak düşünebilirsiniz (LSTM’lerin ayrıntılarına aşina olanlar için, bunlar gizli durum vektöründe [-1,1] arasında değişen değerlerdir; bu, kapalı ve tanh uygulanmış LSTM hücre durumu anlamına gelir). Sezgisel olarak, bu, RNN’nin “beynindeki” bazı nöronların girdi dizisini okurkenki ateşleme oranını görselleştirmektir. Farklı nöronlar farklı kalıpları arıyor olabilir; Aşağıda ilginç veya yorumlanabilir (birçoğu da değil) bulduğum 4 farklı nöronu inceleyeceğiz :
Tabii ki, bu sonuçların çoğu biraz üstünkörü olabilir çünkü RNN’in gizli durumu çok büyük, yüksek boyutlu ve büyük ölçüde dağıtılmış bir temsildir. Bu görselleştirmeler özel HTML/CSS/Javascript ile üretilmiştir, benzer bir şey yaratmak isterseniz burada ilgili bir taslak görebilirsiniz.
Bu görselleştirmeyi, en olası tahminleri hariç tutarak ve sadece hücrenin aktivasyonlarıyla renklendirilmiş metni görselleştirerek de yoğunlaştırabiliriz. Görüyoruz ki, anlamlı bir şey yapmayan büyük bir hücre grubunun yanı sıra, yaklaşık %5'inin oldukça ilginç ve yorumlanabilir algoritmalar öğrendiği ortaya çıkıyor.
Yine, bunun güzel yanı şu ki, bir sonraki karakteri tahmin etmeye çalışırken örneğin, şu anda tırnak işaretlerinin içinde mi yoksa dışında mı olduğunuzu takip etmenin faydalı olabileceğini herhangi bir noktada kodlamamız gerekmemesidir. LSTM’i ham verilerle eğitip bunun takip edilmesi gereken faydalı bir miktar olduğuna karar verdik. Başka bir deyişle, hücrelerinden biri eğitim sırasında yavaş yavaş kendini tırnak işareti algılama hücresi haline getirdi çünkü bu şekilde nihai görevi daha iyi yerine getirebilirdi. Bu, Derin Öğrenme modellerinin (ve genel olarak uçtan uca eğitimin) gücünün nereden geldiğine dair en net ve en ikna edici örneklerden biridir.
Kaynak Kodu
Karakter seviyesinde dil modelleri eğitmenin çok eğlenceli bir egzersiz olduğunu umarım size ikna edebilmişimdir. Modellerinizi GitHub’da yayınladığım (MIT lisansı altında) char-rnn kodunu kullanarak eğitebilirsiniz [https://github.com/karpathy/char-rnn]. Bu kod, büyük bir metin dosyası alır ve ardından örnekleme yapabileceğiniz bir karakter seviyesi modeli eğitir. Ayrıca, bir GPU’ya sahip olmanız işinizi kolaylaştırır, aksi takdirde CPU üzerinde eğitim yapmak yaklaşık 10 kat daha yavaş olacaktır. Her durumda, bazı veriler üzerinde eğitim yapıp eğlenceli sonuçlar elde ederseniz bana bildirin! Ve eğer Torch/Lua kod tabanında kaybolursanız, bunun sadece bu 100 satırlık gist’in [https://gist.github.com/karpathy/d4dee566867f8291f086] daha süslü bir versiyonu olduğunu unutmayın.
Konudan kısa bir sapalım. Kod, son zamanlarda favori derin öğrenme framework’üm haline gelen Torch 7 ile yazılmıştır. Torch/LUA ile çalışmaya sadece birkaç ay önce başladım ve bu kolay olmadı (işleri halletmek için Github’daki ham Torch kodlarını incelemek ve Gitter’da sorular sormak için oldukça fazla zaman harcadım), ancak işleri kavradığınızda çok fazla esneklik ve hız sunan bir çerçeve olduğunu gördüm. Geçmişte Caffe ve Theano ile de çalıştım ve Torch’un, mükemmel olmasa da, soyutlama seviyeleri ve felsefesi açısından diğerlerinden daha iyi olduğunu düşünüyorum. Benim görüşüme göre, etkili bir framework’ün istenen özellikleri şunlardır:
- Çok sayıda fonksiyonaliteye (dilimleme, dizi/matris işlemleri vb.) sahip CPU/GPU şeffaf Tensor kitaplığı
- Tensörler üzerinde çalışan ve tüm Derin Öğrenme öğelerini (ileri/geri, hesaplama grafikleri vb.) uygulayan bir kodlama dilinde (ideal olarak Python) tamamen ayrı bir kod tabanı.
- Önceden eğitilmiş modelleri kolayca paylaşmak mümkün olmalıdır (Caffe bunu iyi yapar, diğerleri yapamaz)
- En önemlisi derleme adımının OLMAMASI (veya en azından şu anda Theano’da yapıldığı gibi olmaması). Derin Öğrenmedeki eğilim, karmaşık graflarda zamana göre değişen daha büyük, daha karmaşık ağlara doğru gidiyor. Bunlar uzun süre derlenmemelidir, aksi takdirde geliştirme süresi büyük ölçüde zarar görür. İkincisi, derleme yaparak yorumlanabilirlikten ve etkili bir şekilde kayıt/hata ayıklama yeteneğinden vazgeçilir. Grafı üretimde verimlilik için geliştirildikten sonra derleyebiliyorsak bu sorun olmaz.
Daha Fazla Okuma İçin
Yazının sonunda, RNN’leri daha geniş bir bağlamda konumlandırmak ve mevcut araştırma yönelimlerinin bir taslağını sunmak istedim. RNN’ler son zamanlarda Derin Öğrenme alanında önemli bir heyecan ve ilgi uyandırdı. Evrişimli Ağlar gibi onlar da onlarca yıldır var ancak tam potansiyelleri büyük ölçüde artan hesaplama kaynaklarımız sayesinde yakın zamanda fark edilmeye başlandı. İşte birkaç son gelişmenin kısa bir taslağı (kesinlikle tam bir liste değil ve bu çalışmaların çoğu 1990'lara kadar uzanan araştırmalardan yararlanıyor, ilgili çalışmalar bölümlerine bakın):
NLP/Konuşma alanında, RNN’ler konuşmayı metne dönüştürür [http://www.jmlr.org/proceedings/papers/v32/graves14.pdf], makine çevirisi gerçekleştirir [http://arxiv.org/abs/1409.3215 ] ve el yazısı metinler oluşturur [http://www.cs.toronto.edu/~graves/handwriting.html] ve elbette güçlü dil modelleri olarak kullanılırlar (Sutskever ve diğerleri [http://www.cs.utoronto.ca/~ilya/pubs/2011/LANG-RNN.pdf]) (Graves [http://arxiv.org/abs/1308.0850]) (Mikolov ve diğerleri [http://www.rnnlm.org/]) (hem karakter hem de kelime düzeyinde). Şu anda kelime düzeyindeki modellerin karakter düzeyindeki modellerden daha iyi çalıştığı görülüyor ancak bu kesinlikle geçici bir şey.
Bilgisayarlı Görü. RNN’ler (Tekrarlayan Sinir Ağları) Bilgisayarlı Görü alanında da hızla yaygınlaşmaktadır. Örneğin, kare düzeyinde video sınıflandırma, görüntü altyazılama [http://arxiv.org/abs/1411.4555] (kendi çalışmam ve birçok diğer çalışma dahil), video altyazılama [http://arxiv.org/abs/1505.00487] ve çok yakın zamanda görsel soru yanıtlama [http://arxiv.org/abs/1505.02074] gibi alanlarda RNN’leri görmekteyiz. Bilgisayarlı Görü alanında en sevdiğim RNN makalesi, hem yüksek düzeydeki yönleri (görüntülerin sırayla işlenmesi) hem de düşük düzeydeki modelleme (REINFORCE öğrenme kuralı, pekiştirmeli öğrenmede politika gradyan yöntemlerinin özel bir durumu olup, bir görüntünün etrafına bakış atmak gibi farklılaştırılamaz hesaplamalar gerçekleştiren modellerin eğitilmesine olanak tanır) nedeniyle Görsel Dikkatin Tekrarlayan Modelleri (Computer Vision paper are Recurrent Models of Visual Attention) [http://arxiv.org/abs/1406.6247] makalesidir. Ham algılama için bir CNN (Evrimsel Sinir Ağı) ile üstünde bir RNN bakış politikası (glance policy) karışımından oluşan bu tür hibrit modellerin, özellikle açıkça görülebilen bazı nesnelerin sınıflandırılmasının ötesine geçen daha karmaşık görevler için algılamada yaygın hale geleceğinden eminim.
Tümevarımsal Akıl Yürütme, Anılar ve Dikkat. Araştırmanın son derece heyecan verici bir diğer yönü de standart tekrarlayan ağların (vanilla RNN’lerin) sınırlamalarını ele almaya yöneliktir. Bir sorun, RNN’lerin tümevarımsal olmamasıdır: Dizileri son derece iyi hafızalarına kazırlar, ancak her zaman doğru genelleme yapma işaretleri göstermeyebilirler (bu konuyu daha somut hale getirecek birkaç nokta sunacağım). İkinci bir sorun ise, temsil boyutlarını gereksiz yere adım başına yapılan hesaplama miktarına bağlamalarıdır. Örneğin, gizli durum vektörünün boyutunu iki katına çıkarırsanız, matris çarpımı nedeniyle her adımda FLOPS miktarını dört katına çıkarırsınız. İdeal olarak, büyük bir temsil/belleği (örneğin, tüm Wikipedia veya birçok ara durum değişkenini içeren) korumak, aynı zamanda adım başına yapılan hesaplamayı sabit tutma yeteneğini sürdürmek isteriz.
Bu yönlere doğru ilerlemenin ilk ikna edici örneği DeepMind’in Neural Turing Machines makalesinde geliştirilmiştir [https://arxiv.org/abs/1410.5401]. Bu makale, büyük, harici bellek dizileri ve daha küçük bir bellek kayıtları kümesi (hesaplamanın gerçekleştiği çalışma belleğimiz olarak düşünebilirsiniz) arasında okuma/yazma işlemleri yapabilen modellerin yolunu çizdi. Özellikle, NTM makalesi, (yumuşak ve tamamen diferansiyellenebilir) bir dikkat modeliyle uygulanan çok ilginç bellek adresleme mekanizmalarını da içermekteydi. Yumuşak dikkat kavramı güçlü bir modelleme özelliği olarak ortaya çıktı ve Neural Machine Translation by Jointly Learning to Align and Translate [https://arxiv.org/abs/1409.0473] makalesinde Makine Çevirisi ve Memory Networks [https://arxiv.org/abs/1503.08895] makalesinde (oyuncak) Soru Cevaplama için de kullanıldı. Şöyle diyebilirim ki:
Dikkat kavramı, sinir ağlarının son zamanlardaki en ilginç mimari yeniliğidir.
Şimdi, çok fazla detaya girmek istemiyorum ama hafıza adreslemesi için yumuşak dikkat şeması uygundur çünkü modeli tamamen türevlenebilir kılar, ancak ne yazık ki, her şeye (ama yumuşakça) dikkat edilmesi gerektiğinden verimlilikten ödün verilir. Bunu, C’de belirli bir adrese işaret etmeyen, ancak tüm bellek üzerindeki tüm adreslerin dağılımını tanımlayan bir işaretçi olarak düşünün ve işaretçiyi dereference etmek, işaret edilen içeriğin ağırlıklı bir toplamını döndürür (bu pahalı bir işlem olurdu!). Bu, birçok yazarı, yumuşak dikkat modellerini sert dikkat ile değiştirmeye teşvik etti; burada belirli bir hafıza parçasına dikkat edilir (örneğin, bazı hafıza hücreleri için okuma/yazma işlemi, tüm hücrelerden belirli bir dereceye kadar okuma/yazma yerine). Bu model, felsefi olarak daha cazip, ölçeklenebilir ve verimli, ancak ne yazık ki, türevlenebilir değildir. Bu, Güçlendirme Öğrenmesi literatüründen tekniklerin (örneğin, REINFORCE) kullanılmasını gerektirir; burada insanlar türevlenemez etkileşimler kavramına alışkındırlar. Bu oldukça devam eden bir çalışma, ancak bu sert dikkat modelleri, örneğin Inferring Algorithmic Patterns with Stack-Augmented Recurrent Nets [https://arxiv.org/abs/1503.01007], Reinforcement Learning Neural Turing Machines [https://arxiv.org/abs/1505.00521], ve Show Attend and Tell [https://arxiv.org/abs/1502.03044] gibi çalışmalarda incelenmiştir.
İnsanlar. RNN’ler hakkında daha fazla bilgi edinmek isterseniz, Alex Graves [http://www.cs.toronto.edu/~graves/], Ilya Sutskever [http://www.cs.toronto.edu/~ilya/] ve Tomas Mikolov [http://www.rnnlm.org/] tarafından hazırlanan tezleri tavsiye ederim. REINFORCE ve genel olarak Pekiştirmeli Öğrenme ve politika gradyan yöntemleri (ki REINFORCE bunun özel bir durumudur) hakkında daha fazla bilgi edinmek için David Silver’ın [http://www0.cs.ucl.ac.uk/staff/d.silver/web/Home.html] dersi veya Pieter Abbeel’in [http://www.cs.berkeley.edu/~pabbeel/] derslerinden birini tavsiye ederim.
Kod. Eğer RNN’lerin eğitimi ile ilgileniyorsanız, Theano için keras [https://github.com/fchollet/keras] veya passage [https://github.com/IndicoDataSolutions/Passage] hakkında iyi şeyler duyuyorum. Bu gönderi ile birlikte yayımlanan Torch için kodu [https://github.com/karpathy/char-rnn] veya bir süre önce yazdığım, verimli ve toplu bir LSTM ileri ve geri geçişini uygulayan raw numpy kodu için bu gist’i [https://gist.github.com/karpathy/587454dc0146a6ae21fc] inceleyebilirsiniz. Ayrıca, bir RNN/LSTM kullanarak görüntüleri başlıklandıran numpy tabanlı NeuralTalk [https://github.com/karpathy/neuraltalk] veya Jeff Donahue tarafından yapılmış bu Caffe [http://jeffdonahue.com/lrcn/] uygulamasına da göz atabilirsiniz.
Sonuç
RNN’leri, nasıl çalıştıklarını, neden bu kadar önemli hale geldiklerini öğrendik, çeşitli eğlenceli veri kümeleri üzerinde karakter düzeyinde bir RNN dil modeli geliştirdik ve RNN’lerin nerelere gidebileceğini gördük. RNN’ler alanında büyük miktarda yeniliği emin bir şekilde bekleyebilirsiniz çünkü yapıların akıllı sistemler için yaygın ve kritik bir bileşen haline geleceklerine inanıyorum.
Son olarak bu yazıya biraz meta eklemek için bu blog yazısının kaynak dosyasında bir RNN eğittim. Ne yazık ki, bu blog yaklaşık 46.000 karakterde, ve bu RNN’i düzgün bir şekilde beslemek için yeterli miktarda bir veri değildir. Aşağıda alınan örnek çıktıyı görebilirsiniz (daha tipik bir örnek elde etmek için düşük sıcaklıkla oluşturulmuştur):
RNN'im var ve çalışıyor, ancak RNN'nin programı ile hesaplanıyor ve
RNN ile ve koduyla hesaplanıyor
Evet, bu paylaşım RNN ve ne kadar iyi çalıştığıyla ilgiliydi, sonuç olarak işe yaradığı bariz :).
Kaynak
Andrej Karpathy, (May 21, 2015), The Unreasonable Effectiveness of Recurrent Neural Networks: