Makine Öğrenimi Eğlencelidir! 2. Kısım

Makine Öğrenimi kullanarak Super Mario Bölümleri yaratmak

Bu yazı serisinin birinci kısmına aşağıdaki linkten ulaşabilirsiniz.

İlk kısımda, makine öğrenmesinin(MÖ) soysal(generic) algoritmalar kullanarak veri kümemizden -o veriye özel kod yazmadan- ilginç çıkarımlar yapabilmek olduğunu söylemiştik.(Eğer birinci kısmı okumadıysanız, şimdi okuyun!).

Bu sefer soysal algoritmalardan birini çok havalı bir şey yaparken göreceğiz-İnsanlar yaratmış gibi duran bilgisayar oyunu bölümleri yaratırken. Bir sinir ağı(neural network) yapacağız, onu hali hazırdaki Super Mario bölümleri ile besleyeceğiz ve onun yeni bölümler yaratmasını izleyeceğiz.

Algoritmamızın yaratacağı bölümlerden biri

Birinci kısım gibi, bu kısım da makine öğrenmesi ile ilgilenen ancak nereden başlayacağını bilmeyenler için. Amaç herkes için ulaşılabilir olmak- Bu da çok fazla genelleme yapacağız ve çok fazla detayı atlayacağız demek. Ancak kimin umrunda ? Eğer bu yazı herhangi bir kişiyi bile MÖ’ye daha ilgili hale getirirse, görev tamamlanmış demektir.


Daha Akıllı Tahminler Yapmak

Birinci kısımda, özelliklerine bakarak bir evin fiyatını tahmin eden basit bir algoritma yaptık. Şekildeki gibi, bir eve ait veri verildiğinde:

Bu basit tahmin fonksiyonunu elde ettik:

def ev_satis_fiyati_tahmin_et(oda_sayisi, genislik, muhit):
fiyat= 0
# Bir tutam bundan
fiyat+= oda_sayisi* 0.123
# birazcık şundan
fiyat+= genislik* 0.41
# Bir ölçü de bundan ekleyelim
fiyat+= muhit* 0.57
return fiyat

Başka bir deyişle; bir evin fiyatını, evin özelliklerini her bir özelliğin ağırlığı ile çarparak hesapladık. Sonrasında da bu sayıları toplayarak evin tahmini fiyatını elde ettik.

Kodu kullanmak yerine, bu fonksiyonu bir diyagram kullanarak gösterelim:

Oklar fonskiyonumuzdaki ağırlıkları simgeliyor.

Ancak bu algoritma sadece girdi ve çıktıların arasında doğrusal ilişki bulunan basit problemlerde işe yarıyor. Ya ev fiyatları arkasındaki gerçek bu kadar basit değilse? Mesela, belki de muhit, büyük ve küçük evler için çok önemliyken orta büyüklükteki evler için hiç önemli değil. Bu tarz karmaşık detayları kendi modelimizde nasıl yakalayabiliriz ?

Daha akıllı olabilmek adına, bu algoritmayı her uç durumu yakalayan farklı ağırlıklarla birkaç sefer çalıştırabiliriz:

Problemi 4 farklı şekilde çözmeyi deneyelim

Şimdi 4 farklı tahmini fiyatımız var. Bu 4 farklı fiyat tahminini tek bir fiyat oluşturmak için birleştirelim. Yine aynı algoritmayı kullanacağız( ama farklı ağırlıklar vereceğiz )!

Yeni süper cevabımız problemi çözmek için yaptığımız 4 farklı girişimi birleştiriyor. Bu sayede, basit bir modelden daha fazla durumu yakalayabiliyoruz.

Sinir Ağı Nedir ?

Tahmin için yaptığımız 4 girişimi tek bir büyük diyagrama koyalım:

Bu bir sinir ağı! Her bir düğüm(node) veri girdisi almayı, ona ağırlıkları uygulamayı ve çıktıyı hesaplamayı biliyor. Bu düğümlerden bir çoğunu birbirine bağlayarak karmaşık fonksiyonları modelleyebiliriz.

Bu açıklamayı kısa ve öz tutmak adına atladığımız çok konu var ( veri ölçekleme ve aktivasyon fonksiyonu), ama en önemli konu bu basit fikrin bile çalıştığı:

  • Bir veri kümesi alıp , verileri ağırlıkları ile çarparak bir çıktı elde eden basit bir tahmin fonksiyonu oluşturduk. Bu basit fonksiyona nöron(sinir hücreleri) adını verelim.
  • Bir sürü basit nöron’u birbirine bağlayarak, tek bir nöronda modellenemeyecek kadar karmaşık fonksiyonları modelleyebiliriz.

Aynı LEGO gibi! Tek bir LEGO parçası kullanarak çok fazla şey yaratamayız, ancak elimizde yeteri kadar basit lego parçası varsa, onları birbirine takarak her şeyi yaratabiliriz.

sadece legolardan yapılmış bir zebra…

Sinir Ağımıza hafıza kazandırmak…

Bu ana kadar gördüğümüz sinir ağları, aynı veriyi girdiğinizde hep aynı çıktıyı veriyor. Herhangi bir hafızası yok. Programlama terimlerine göre, belleksiz bir algoritma(stateless algoritm).

Çoğu durumda( evin fiyatını tahmin etmek gibi), bu tam olarak istediğimiz şey. Ancak bu modelin yapamadığı bir şey de, zaman içinde veride oluşan şablonlara(pattern) cevap verebilmek.

Farz edin ki size klavyeyi veriyorum ve sizden bir hikaye yazmanızı istiyorum. Ancak siz başlamadan önce, yazacağınız ilk harfi tahmin etmem gerekiyor. Hangi harfi tahmin etmeliyim ?

Dil bilgimi kullanarak doğru harfi tahmin etme şansımı arttırabilirim. Örneğin , büyük olasılıkla kelimelerin başında çok kullanılan bir harfi yazacaksınız. Eğer sizin daha önceden yazdığınız hikayelere bakarsam, hikayelerde çoğunlukla kullandığınız ilk kelimelere dayanarak olasılıkları daha da azaltabilirim. Bir kere bütün veriye sahip oldum mu, her harf için o harfle başlama olasılığınızın ne kadar olduğunu hesaplayan bir sinir ağı kurabilirim.

Modelimiz buna benzer:

Girdi: Geçmişte yazılmış hikayeler -> Sinir ağımız -> Çıktı: tahmini ilk harfiniz

Problemi biraz daha zor hale getirelim. Mesela hikayenin herhangi bir yerinde yazacağınız bir sonraki harfi tahmin etmeye çalışayım. Bu çok daha ilginç bir problem.

Ernest Hemingway’in Güneş de Doğar kitabının ilk birkaç kelimesine bakalım.

Robert Cohn bir zamanlar orta sıklet bir boksö

Bir sonraki harf ne olacak ?

Muhtemelen tahmininiz ‘r’ harfi — kelime büyük olasılıkla boksör olacak. Bunu cümlede hali hazırda gördüğümüz harflere ve Türkçe’deki yaygın kelimeleri bilmemize dayanarak söylüyoruz. Ayrıca, ‘orta sıklet’ kelimesi de bize boks ile ilgili konuştuğumuza dair fazladan ipucu veriyor.

Başka bir deyişle, bir sonraki harfi tahmin etmek, eğer ondan hemen önce gelen harfleri ve Türkçe kuralları bilgimizi de hesaba katarsak baya kolay.

Bu problemi sinir ağı ile çözmek için, modelimize hafıza eklememiz gerekiyor. Sinir ağımıza cevabı sorduğumuz her seferde, ara hesaplamaları da kaydediyoruz ve bir sonraki seferde bunları da girdi olarak tekrar kullanıyoruz. Böylece modelimiz, tahminlerini, yakın zamanda gördüğü bilgilere dayanarak belirleyebilir.

Alt metin: Modelin anlık durumunu kaydet ve bir sonraki hesaplamada kullan.

Modelimizin durumunun kaydını tutmak, hikayenin sadece ilk harfinin değil, her seferinde bir sonraki harfin de tahmin edilmesini sağlar.

Bu Yinelenen Sinir Ağlarının(YNN) temel fikri. Ağı kullandığımız her an güncelliyoruz. Bu da onun tahminlerini en son ne gördüğüne bakarak güncellemesini sağlıyor. Yeteri kadar hafıza verdiğimiz durumda, zaman içinde veri içindeki şablonları da modelleyebilir.

Tek bir harfin ne faydası var ?

Bir hikayedeki tek bir harfi tahmin etmek çok da kullanışlı değil. Amaç ne ki ?

Güzel bir kullanım yeri cep telefonu klavyelerindeki otomatik tahmin özelliği olabilir.

Bir sonraki harf büyük olasılıkla ‘t’.

Ya bunu en uç noktaya taşırsak ? Ya modelimize tekrar ve tekrar bir sonraki harfi tahmin etmesini söylersek — sonsuza kadar ? Ondan eksiksiz bir hikaye yazmasını istemiş oluruz!

Hikaye üretmek

Hemingway’in cümlesinde ilk kelimeyi nasıl tahmin edebileceğimizi gördük. Şimdi Hemingway’in tarzında yeni bir hikaye üretmeyi deneyelim.

Bunu yapmak için, Andrej Karpathy’nin yazdığı Yinelenen Sinir Ağı uygulamasını kullanacağız. Andrej Stanford Üniversitesinde Derin Öğrenme araştırmacısı ve YNN ile yazı üretmek(ç.n. ingilizce kaynak) üzerine harika bir giriş yazısı yazmış. Modeliyle ilgili tüm kodu Github’da görebilirsiniz.

Modelimizi Güneş De Doğar’ın tamamından yaratacağız. 84 farklı harf kullanan(noktalama, büyük harf, küçük harf dahil) 362,239 karakter. Bu veri boyutu gerçek hayattaki uygulamalarda kullanılan veri boyutuna göre oldukça küçük. Hemingway’in tarzının çok iyi bir modelini üretmek için birkaç kat daha fazla metine sahip olmak çok daha iyi olur. Ancak şimdilik örnek olarak bu büyüklük yeterli.

YNN’yi eğitmeye yeni yeni başladığımızda, harfleri tahmin etmede o kadar başarılı değil. 100 kere yinelen bir eğitimden sonra ürettiği metin:

hjCTCnhoofeoxelif edElobe negnk e iohehasenoldndAmdaI ayio pe e h’e btentmuhgehi bcgdltt. gey heho grpiahe.
Ddelnss.eelaishaner” cot AAfhB ht ltny
ehbih a”on bhnte ectrsnae abeahngy
amo k ns aeo?cdse nh a taei.rairrhelardr er deffijha

Görebileceğiniz gibi bazı kelimelerin arasında boşluk olacağınızı çözmüş ancak şimdilik hepsi bu kadar.

1000 adet yinelemeden sonra, durum biraz daha umut verici görünüyor:

hing soor ither. And the caraos, and the crowebel for figttier and ale the room of me? Streat was not to him Bill-stook of the momansbed mig out ust on the bull, out here. I been soms
inick stalling that aid.
 “Hon’t me and acrained on .Hw’s don’t you for the roed,” In’s pair.”
 “Alough marith him.”

Model basit cümle yapısını tanımaya başladı. Cümlelerin sonuna nokta koyuyor ve hatta konuşma bile alıntılıyor. Birkaç kelime anlaşılabilir ancak metnin çoğunluğu hala anlamsız.

Birkaç bin yinelemeden sonra, metin gayet iyi görünüyor(ç.n. buradaki metni Türkçe’ye çevirirken metnin robotik havasını bozmamak adına dil bilgisi hatalarını tutup, anlam değil kelime bazlı çeviri yaptım):

Kafenin kapısının oraya gitti. Bir kasaba yatağı gibiydi.
 “Ben olduğumu biliyordun mu”
 “Lanetlenmiş biz” Bill dedi.
 “Ben tehlikeliydim” dedim. “sen o onu yaptı ve seni güzel bir pelerinlerdim diye düşünüyor” dedim.
 “Takside garip görünemem”
 “biliyordun ben buyum ancak” dedi Brett.
 “Her şekilde kavga mı ?”
 “Onu yapmayı yapıyor.”
 “Sen yapıyor musun?”
 “Otur,” dedim ben. “Keşke adamla küçük yapmasaydım”
 “Onu buldun”
 “Bilmiyorum.”
 “Görüyorsun sohbetlerden özür dilerim,” dedi Bill. “O arkadan bir arkadaş ve seni gerçekten sarhoş yapıyor diye düşünüyorsun”

Bu noktada algoritma Hemingway’in kısa , doğrudan diyalog şablonunu yakalamış. Hatta birkaç cümle anlamlı gibi duruyor.

Bunu kitaptan gerçek bir kesit ile karşılaştıralım:

Barın içinde birkaç kişi vardı ve dışarıda, tek başına, Harvey Stone oturuyordu.
Önünde bir tabak yığını vardı ve traş olması gerekiyordu.
“Otur” dedi Harvey ,”Ben de seni arıyordum.”
“Sorun nedir?”
“Hiçbir şey. Sadece seni arıyordum.”
“Yarışlara gittin mi?”
“Hayır. Pazardan beri gitmedim”
“devletten(ABD) neler duydun?”
“Hiçbir şey. Kesinlikle hiçbir şey.”
“Sorun nedir?”

Şablonlara harf harf baktığı halde, algoritmamız kabul edilebilir görünen bir ve düzgün biçimli bir düz yazı üretmeyi başarabildi. Bu baya inanılmaz!

Metni tamamen sıfırdan da üretmemiz gerekmiyor. Algoritmayı ilk birkaç harfi verip besleyebilir ve ondan sadece son harfleri bulmasını isteyebiliriz.

Eğlenme amaçlı, kendi hayali kitabımız için algoritmamızı “Er” , “He” ve “The S” harfleriyle besleyerek yeni bir kitap başlığı ve yazar adı üretebiliriz.

Gerçek kitap solda ve bizim bilgisayar tarafından üretilen kitabımız sağda.

Fena değil!

Ancak asıl akıl almaz kısım bu algoritmanın herhangi bir verideki şablonları anlayabilir olması. Kolayca gerçekmiş gibi görünen yemek tarifleri ya da taklit Obama konuşmaları uydurabilir. Ancak kendimizi neden insan dili ile sınırlayalım ? Aynı mantığı bir şablona sahip her veriye uygulayabiliriz.

Gerçekten Mario yapmadan Mario yapmak

2015 yılında, Nintendo Wii U oyun konsolu için Super Mario Maker™’ı çıkarttı.

Her çocuğun rüyası!

Oyun konsol üzerinde kendi Super Mario bölümünüzü yaratmanıza izin veriyor ve sonrasında bu bölümü internete yükleyip arkadaşlarınızla paylaşabiliyorsunuz. Bölümünüze orijinal Mario oyunlarındaki düşmanları ve güç arttırıcıları da ekleyebiliyorsunuz. Super Mario oynayarak büyüyen insanlar için sanal bir LEGO seti gibi.

Yapay Hemingway metinleri üreten modelimizi , yapay Super Mario Kardeşler bölümleri üretmek için kullanabilir miyiz ?

Öncelikle, modelimizi eğitmek için bir veri kümesine ihtiyacımız var. 1985'te çıkarılan Super Mario oyununun bütün açık alan bölümlerini alalım.

Bu oyunun 32 bölümü var ve bütün açık alan bölümleri aynı tarza sahip. Bunlara bağlı kalacağız.

Her bölümün dizaynını almak için , oyunun orijinal kopyasını aldım ve oyunun hafızasından bölüm tasarımlarını çeken bir program yazdım. Super Mario kardeşler 30 yıllık bir oyun ve internette bölümlerin, oyunların hafızasında nasıl saklandığını çözmenizi sağlayacak bir sürü kaynak bulabilirsiniz. Eski bir bilgisayar oyunundan bölüm tasarımlarını çekmek eğlenceli bir programlama egzersizi ve bir gün denemelisiniz.

Oyunun ilk bölümü (Eğer oynadıysanız büyük olasılıkla hatırlarsınız):

Dikkatlice bakarsak, bölümün nesnelere sahip bir ızgaradan yapıldığını görebiliriz:

Bu ızgarayı , her sembolün bir nesneyi temsil ettiği bir karakter diziniyle resmedebiliriz:

--------------------------
--------------------------
--------------------------
#??#----------------------
--------------------------
--------------------------
--------------------------
-##------=--=----------==-
--------==--==--------===-
-------===--===------====-
------====--====----=====-
=========================-

Bölümdeki her nesneyi bir sembolle değiştirdik:

  • ‘-’ boş alan
  • ‘=’ katı nesne
  • ‘#’ kırılabilir tuğla
  • ‘?’ para kutusu

… vb., her farklı nesne için farklı bir karakter kullanıyoruz.

En sonunda elimde buna benzer metin dosyaları oluştu:

Bölümün metin olarak temsili

Metin dosyasını satır satır okursak, Mario bölümlerinin çok bir şablona sahip olmadığını görürüz.

Satır satır okuduğunuzda yakalayacak bir şablon yok. Satırların çoğu da boş

Şablonlar eğer veriyi sütun sütun okumaya başlarsanız ortaya çıkıyor:

Sütun Sütun okuduğunuza şablonlar ortaya çıkmaya başlıyor. Örneğin her sütun ‘=’ ile bitiyor.

Algoritmamızın verideki şablonları düzgünce bulabilmesi, onu sütun sütun beslememiz gerekiyor. Verimizin en kullanışlı gösterim şeklini bulmak ( adına özellik seçimi deniyor) , Makine Öğrenmesi algoritmalarını iyi kullanabilmenin anahtarlarından biri.

Modeli eğitmek için, metin dosyamı 90 derece döndürmem gerekiyor. Bu da karakterlerin modele, şablonun daha kolay görülebileceği şekilde gireceğini garanti ediyor.

-----------=
-------#---=
-------#---=
-------?---=
-------#---=
-----------=
-----------=
----------@=
----------@=
-----------=
-----------=
-----------=
---------PP=
---------PP=
----------==
---------===
--------====
-------=====
------======
-----=======
---=========
---=========

Modelimizi Eğitmek

Aynı Hemingway kesitinde olduğu gibi, modelimiz onu eğitmeye devam ettikçe daha iyi hale geliyor.

Azıcık eğitmeden sonara , modelimiz çöp üretiyor:

--------------------------
LL+<&=------P-------------
--------
---------------------T--#--
-----
-=--=-=------------=-&--T--------------
--------------------
--=------$-=#-=-_
--------------=----=<----
-------b
-

‘-’ ve ‘=’’lerin çok olması gerektiğine dair bir fikri var ama başka da bir şey yok. Şimdilik herhangi bir şablon fark edememiş.

Birkaç bin tekrardan sonra, çıktı bir şeylere benzemeye başlıyor:

--
-----------=
----------=
--------PP=
--------PP=
-----------=
-----------=
-----------=
-------?---=
-----------=
-----------=

Modelimiz her satırın uzunluğunun eşit olması gerektiğini anlamış. Ayrıca Mario’nun mantığını da anlamaya başlamış: Mario’daki borular(‘p’ harfi) hep 2 kutu genişliğindeler ve en az 2 kutu yüksekliğindeler, yani verideki ‘p’ler her 2x2 yığınlar halinde bulunmalı. Bu baya iyi!

Çok fazla tekrardan sonra, modelimiz tamamen geçerli çıktı üretmeye başlıyor:

--------PP=
--------PP=
----------=
----------=
----------=
---PPP=---=
---PPP=---=
----------=

Bütün bir bölüme karşılık gelen veriyi üretelim ve tekrar yatay hale getirelim:

Modelimiz tarafından yaratılmış bütün bölüm!

Bu veri baya iyi görünüyor. Fark etmemiz gereken birkaç harika nokta var:

  • Aynı gerçek Super Mario bölümlerindeki gibi Latiku’yu(bulutların üzerinde gezen canavar) bölümün başına koymuş.
  • Havadaki boruların havada asılı kalmayıp, katı nesnelerin üzerinde durması gerektiğini biliyor.
  • Düşmanları mantıklı yerlere koyuyor.
  • Oyuncunun yoluna, onun ileri gitmesini engelleyecek hiçbir nesne koymuyor.
  • Super Mario Kardeşler’deki gerçek bir bölüm gibi hissettiriyor, çünkü onların tarzından yararlandı.

Son olarak, bu bölümü alalım ve Super Mario Maker’da yaratalım:

Super Mario Maker’a girildikten sonra bölümümüz.

Kendiniz de oynayabilirsiniz!

Eğer Super Mario Maker’ınız varsa, bu bölümü internet üzerinden yer işaretlerinize ekleyerek veya 4AC9–0000–0157-F3C3 kodunu kullanarak oynayabilirsiniz.

Oyuncaklara Karşı Gerçek Hayattaki Uygulamalar

Bizim modelimizi geliştirmek için kullandığımız yinelenen sinir ağı algoritması gerçek hayattaki şirketlerin tercüme ve konuşma tanıma gibi zor problemler için kullandığı algoritmalarla aynı tipte. Bizim modelimizi son teknoloji yerine bir oyuncak yapan sebep ise çok az veriden üretilmiş olması. Çok iyi bir model üretmek için Super Mario’da yeteri kadar fazla veri yok.

Eğer Nintendo gibi, kullanıcılar tarafından yaratılmış yüz binlerce bölüme erişimimiz olsaydı, harika bir model yaratabilirdik. Ancak yapamıyoruz-çünkü Nintendo bize izin vermiyor. Büyük şirketler ellerindeki veriyi bedavaya vermezler.

Makine öğrenmesi daha fazla endüstride daha da önemli hale geldikçe, modelinizi eğitmek için ne kadar veriye sahip olduğunuz , iyi ve kötü program arasındaki farkı belirleyecek. Bu yüzden Google ve Facebook gibi şirketlerin sizin verinize bu kadar çok ihtiyacı var.

Örnek olarak, Google yakın zamanda büyük çaplı Makine Öğrenmesi uygulamaları yapmaya yarayan TensorFlow yazılım kitini açık kaynaklı hale getirdi. Google bu kadar önemli ve kabiliyetli bir teknolojiyi bedava vermesi büyük bir olay. Google Çeviri’nin arkasındaki teknoloji de bu bahsettiğimiz yazılım.

Google’ın her dille ilgili sahip olduğu veri hazinesine sahip olmadan, Google Çeviri’ye rakip bir uygulama çıkartamazsınız. Google’a avantajı sağlayan elindeki veri. Google Haritalar’daki konum geçmişinize veya Facebook konum geçmişinize bakıp sizin gittiğiniz her yeri kaydettiklerini farkettiğinizde bunu düşünün.

İleriki Okumalar

Makine Öğrenmesinde, bir problemi çözmek için tek bir yol yok. Verinizi nasıl işleyeceğinizi ve hangi algoritmayı seçeceğinize karar verirken önünüzde sınırsız seçenek var. Genellikle birden fazla yaklaşımı birleştirmek size tek bir yaklaşımdan daha iyi bir sonuç verecektir.

Okuyucular bana Super Mario bölümleri yaratmak için kullandıkları ilginç teknikleri yolladılar:


Eğer bu yazıyı beğendiyseniz, yazarın Machine Learning is Fun! eposta grubuna kayıt olabilirsiniz. Yazar sadece enterasan ve yeni şeyler bulduğunda mail atıyor. Bunun gibi yazılar yazdığında haberdar olmanızın en iyi yolu bu.

Ayrıca yazara Twitter ile @ageitgey, mail ile veya linkedin üzerinden ulaşabilirsiniz.

Yazarın izniyle çevrilmiştir. Yazının kendisine buradan ulaşabilirsiniz. Bana da buradan mail ile ulaşabilirsiniz.