Sıfırdan Python İle Neural Network (Yapay Sinir Ağ ) Oluşturmak : Bölüm 1 — Yapay Sinir Ağları ve Nöronlar Nasıl Çalışır?
Günümüzde sıklıkla duyduğumuz algoritmalarından biri olan Yapay Sinir Ağları (neural networks) makine öğrenmesinde en çok tercih edilen yöntemler arasında yer almaktadır. Python, R gibi sıklıkla veri bilimi üzerine kullanılan programlama dilleri için geliştirmiş kütüphaneler sayesinde yapay sinir ağı modelleri oluşturmak oldukça kolay hale gelmiştir. Aynı zamanda, yapay Sinir Ağı modellerinin oluşturulması üzerine internette sayısız kaynak ve örnek de bulunmaktadır. Verbilimi ile uğraşan veya veri bilimine yeni başlamış herkes Python, R gibi programlama dillerini kullanarak çeşitli modeller geliştirmiştir. Bu bağlamda, geliştirilen modelin geçerliliği ve gerçeğe yakın tahminler yapabilmesi büyük önem taşımaktadır. Modelin anlamlığına en büyük etki her ne kadar model eğitilirken kullanılan veri olsa da, kullanılan yöntemin teoriğine hakim olmak ve veriler üzerinde buna göre aksiyon almak da büyük etki göstermektedir. Yapay Sinir Ağlarının teoriği kulağa karmaşık gelse de anlaması oldukça kolaydır. Bu yazımda sizlere bir sinir ağının nasıl çalıştığını anlatmaya çalışacağım ve Python üzerinde sıfırdan bir sinir ağı oluşturacağız.
Yapay Sinir Ağları
Yapay sinir ağlarında da beynimizdeki sinir sisteminde olduğu gibi nöronlar bulunmaktadır. Bu nöronlar bağlı olduğu her bir nörona veri iletimi yapan işlemciler olarak dünüşünülebilir. Her bir nöronun görevi veri iletimini yapmadan önce diğer nörondan aldığı veriyi işleminden geçirmek ve bağlı olduğu diğer nörona iletmektedir. Fakat, Yapay Sinir Ağlarında bulunan nöronlar, beyinde yer alan nöronlardan farklı olarak belirli katmanlar üzrerinden bir birlerine bağlanmaktadır. Örneğin, basit bir ağ’da Girdi Katmanı, Gizli Katman ve Çıktı Katmanı bulunmaktadır. Girdi katmanı girdi verilerimizin bulunduğu katmandır. Gizli katmalar ise nöronların bulunduğu katmanlardır. Çıktı Katmanı ise sonuç olarak en son işlemin yapıldığı nöronları barındıran katmandır ve bu katmanda tahmin sonucu elde edilir. Örneğin Boy ve Kilo verileri üzerinden cinsiyet tahmini yapan bir modelin eğitildiği bir ağda, Girdi Katmanında iki adet girdi (Boy ve Kilo), Gizli Katmanda her birinin ayrı ayrı boy ve kilo girdilerine bağı olduğu iki nöron ve çıktı katmanında sonuç olarak cinsiyeti veren bir nöron bulunmaktadır (Gizli katman sayısı ve içerisindeki nöronlar isteğe bağlı olarak arttırılabilir).
Nöronlar beslendiği nörondan aldıkları değerleri ağırlıklandırarak beslediği nörona iletmektedirler. Son katman olan Çıktı katmanında yer alan nöronlar ise son ağırlıklandırma işlemini yaparak tahmin değerini çıktı olarak vermektedir. Elde edilen tahmin değeri gerçek değer ile karşılaştırılarak, ağırlıklandırma işlemi için kullanılan değerler elde edilen bu fark değerine göre güncellenir ve diğer girdi verileri ağ üzerinden geçer. Bu işlem tahmin değeri gerçek değere en yakın olan noktaya gelinceye kadar veya belirtilen devir sayısı kadar devam eder. Bu türde bir ağa İleri Beslenmeli ağ (Feedforward) denilmektedir.
Yapay Sinir Ağında Yer Alan Nöronların Görevi Nedir?
Nöronların katmanlar bazında birbirine bağlanması ile oluşan sinir ağında nöronların görevleri nedir? Nöronlar, içerisinde her bir bağlantının “Ağırlık” (W) değerini ve “Bias” (b) değeri barındırmaktadır. Nöronlar “Girdi Katmanı” üzerinden aldıkları değerleri o girdiye ait Ağırlık “W” değeri ile çarpmak ve ardından “bias” değeri ile toplamakla görevlilerdir. Burada “W”ağırlık değerleri o nöronun giridler için katsayıları “b” bias değeri de nörondaki sabit sayı olarak düşünülebilir. Her nöron ağın beslendiği girdi sayısı (veri setindeki tahmin edici değişken sayısı) kadar ağırlık değeri ve bir adet bias değeri barındırmaktadır. Örneğin; aşağıdaki şekilde bir nöronun görevi görselleştirilmiştir. Aşağıda yer alan Nöron X1 ve X2 girdileri ile bağlantılıdır. X1 girdisinin “W” değeri nöronda W1, X2 girdisinin “W” değeri nöronda W2 ve nöronun “b” değeri b ile gösterilmiştir.
Görsele dikkat edildiğinde nöron her bir “W” değerini denk gelen girdi değeri ile çarparak toplamaktadır. Bu işlem sonrasında elde edilen değer “b” bias değeri toplanmaktadır. Buradan elde edilen sonuç girdilerin ağırlıklandırılmış toplam değerleridir. Nöron, son olarak bir aktivasyon fonksiyonu kullanarak elde edilen değeri 0 ile 1 arasında standartlaştırmaktadır. Bu sayede kontrolsüz veya sınırsız (unbounded) olarak gelen veri tahmin edilebilir bir değere dönüştürülmektedir. Sigmoid ve Softmax fonksiyonları en çok kullanılan aktivasyon fonksiyonlarındandır.
Şimdi bu işlemi sayısal bir örnek üzerinden deneyelim;
Girdimiz X = [4,15] , Ağırlıklar W = [0,1] ve Bias b = 0 olsun;Girdi 1 => x1 -> x1 * w1
Girdi 2 => x2 -> x2 * w2Nöron = (x1 * w1) + (x2 * w2) + b = (4 * 0) + (15 * 1) + 0
= 15
= Activasyon Fonksiyonu f(15)Aktivasyon fonksiyonu sigmoid olduğunu varsayarsak;f(x) = 1 / ( 1 + exp(-x) ); x = 15
= 1 / ( 1 + exp(-15) )
= 1 / ( 1 + 3.05e-07 )
= 0.9999Nöron Çıktısı => 0.999
Yukarıdaki örnekte iki girdili bir ağda tek bir nöronun işlemini yapmış olduk. Bu nöron ,sonuç değerini 0.999 olarak tahmin etti.
Şimdi bu işlemleri tek bir nöron için Python üzerinde yapalım;
Eğer kodları düzgün, girdi, ağırlık ve bias değerlerini aynı yazdıysanız nöronun tahmin değeri 0.999999694097773 olarak elde edilecektir.
Tüm Sinir Ağının Oluşturulması
Bu bölüme kadar genel olarak bir nöronun girdi ağırlık ve bias değerleri üzerinden çalışma çeklini inceledik. Bir Sinir Ağının (Neural Network) birden fazla Nöronun birbirine katmanlar bazında bağlandığı göz önünde bulundurulduğunda, bir önceki bölümde yaptığımız tüm işlemler bir katmanda bulunan her bir nöron için gerçekleştirilmektedir. Bir sinir ağında bulunan katmanların nöronlarının diğer katmandaki nöronların her biri ile bağlantılı olması gerektiğini belirtmiştik. Bu durumda bir Sinir Ağı aşağıda gösterilen görseldeki gibi şematize edilebilir;
Yukarıda gösterilen Sinir Ağın’da 3 adet katman (Layer) bulunmaktadır. İlk katman Ağ’ımızı besleyeceğimiz Girdi (Input Layer) katmanı, ikinci katman ilk Nöron’larımızın yer aldığı Gizli Katman (Hidden Layer) ve üçüncü katman da tahmin değerimizi aldığımız Çıktı Katmanı (Output Layer)’dır. Bu örnekte “Gizli Katman” da 2, “Çıktı Katmanın” da 1 adet nöron kullanılmıştır. Bu nöronların sayısı arttırılabildiği gibi Gizli Katmanların sayısı da arttırılabilir fakat girdi ve çıktı katmanı sadece 1 adet olmalıdır.Görsele dikkat edildiğinde katmanlarda yer alan herbir nöron (ek olarak girdiler) bir sonra gelen katmandaki herbir nöron ile bağlantılır. Son olarak Çıktı Katmanında tek bir nöron ile tüm ağın tahmin değeri elde edilemektedir (Birden fazla çıktı değerinin elde edildiği durumlarda olabilir). Bu durumda katmanlarımızı incelersek elimizde toplam 3 adet nöron bulunmaktadır. Bu üç adet nöronun değerleri aşağıdaki gösterilen şekildeki gibi hesaplanmaktadır.
H1 ve H2 nöronları doğrudan girdi katmanıdan beslenirken, çıktı katmanı gizli katmanda yer alan H1 ve H2 nöronlarından beslenmektedir. Bu nedenle H1 ve H2 nöronlarının değerleri hesaplanırken Girdi 1 ve Girdi 2 değerleri bu girdiler için nöronda bulunan ağırlık değerleri ile çarpılarak toplanmaktadır (ek olarak nöronun bias değeride bu sonuca eklenmektedir). Burada unutulamaması gereken kısım nöronlar her bir girdi için bir ağırlık değeri barındırmaktadır. Bu bağlamda, girdi katmanı ile, gizli katman arasında 2 girdiden 4 adet bağlantı oluğu için 2'si H1 diğer 2'si H2'ye ait olmak üzere 4 adet “Ağırlık” W değeri bulunmaktadır.
Son katman olan çıktı katmanında bir adet nöron bulunmaktadır. Bu nörondan elde edilen sonuç bize tahmin edilen değeri vermektir. Tahmin değerini veren bu nöron ise gizli katmanda bulunana H1 ve H2 nöronlarından beslenmektedir. Bu durumda O1 için yapılacak hesaplamada H1 ve H2 nöronlarının değeleri ve bu değerler için O1 nöronunda yer alan ağırlıklar ve b değeri hesaba katılarak işlem yapılmaktadır.
Şimdi matematiksel olarak bir örnek üzerinden bu işlemleri gerçekleştirelim;
Tüm nöronlarımız aynı “Ağırlık” değerlerine sahip oludğunu varsayalım;
w = [0,1] (Her nöron iki bağlantıya sahip olduğu için iki adet ağırlık değeri içerir)Girdi katmanında yer alan girdilerimizin değerleri;
X = [4 , 15] olsun;Bias “b” değerimiz her bir nöron için
b = 0 olsun;
İlk önce gizli katmanda yer alan nöronların değerlerini hesaplayalım;
H1 = (4 * 0) + (15 * 1) + 0
= 15
= f(15) Sigmoid Fonksiyonu
= 1 / (1 + exp(-15))
= 0.999H2 = (4 * 0) + (15 * 1) + 0
= 15
= f(15)
= 1 / (1 + exp(-15))
= 0.99997
H1 ve H2 nöronlarının ağılıklandırılmış değerlerini hesaplayıp, sigmoid fonksiyonu ile standartlaştırmış olduk. Şimdi H1 ve H2 ile bağlantılı olan O1 nöronun değerini yani tahmin değerini hesaplamak gerekmektedir. Bütün nöronların “W” değerlerini [0,1] ve “b” değerini 0 olarak belirtmiştik. Bu nedenler O1 nöronun “W” değeri de [0,1] ve “b” değeri de 0 olacaktır.
O1 = (H1 * 0) + (H2 * 1) + b
= (0.99 * 0) + (0.99 * 1) + 0
= 0.99
= f(0.99997)
= 1 / (1 + exp(-0.99997))
= 0.731
“O1” nöronu üzerinden hesapladığımız tahmin değeri de 0.731 olarak elde edildi.
Şimdi yaptığımız bu işlemleri Python üzerinde uygulayalım;
İşlemleri aynı şekilde yaptığınız takdirde tahmin değerini 0.731058518485 olarak elde edeceksinizdir.
Evet, bir ağ (Network) oluşturmuş ve girdi değerlerimizi bir kere ağdan geçirerek ilk tahmin değerimizi elde etmiş olduk. Sinir Ağı modelimizi eğitebilmemiz için bu işlemi bir çok kere tekrar etmemiz gerekmektedir. Ta ki elde edilen tahmin değeri ile gerçek değer arasındaki fark minimum düzeye gelene kadar. Daha açık olmak gerekirse; yazının başlangıcında kilo ve boy değerleriyle kişinin cinsiyetini tahmin edebileceğimiz bir örnek vermiştik. Örneğin elinizde aşağıdaki gibi bir veri seti olduğunu düşünün,
Boy | Kilo | Cinsiyet
_________ _________ __________
165 75 E (0)
175 76 E (0)
154 45 K (1)
136 56 K (1)
145 70 E (0)
167 56 K (1)
... ... ...
Bu verilerden yola çıkarak, verilen Boy ve Kilo ile kişinin cinsiyetine tahmin edeceğiniz bir Netwok (Ağ) eğiteceksiniz. Şuana kadar yapmış olduğumuz işlemlerde, sadece bir örnek üzerinden çalıştık. Örneğin, ilk gözlem olan 165,75,E(0) satırı. Bunun gibi bir girdiyle ağımızı besleyerek tahmin değeri elde ettik. Peki eğitim işlemini nasıl gerçekleştirmemiz gerekiyor?
İleri besleme (Feedforward) ile eğitilen bir ağda son çıktı katmanında elde edilen değeler o girdiye ait olan gerçek değer ile karşılaştırılmaktadır. Örneğin yukarıda yer alan veri setimizde cinsiyet değişkenimizin değerlerini Erkekler 0 ve Kadınlar 1 olacak şekilde sayısallaştıralım. İlk satırı ağımıza dahil ettikten sonra aldığımız çıktı değerinin 0.7 olduğunu varsayalım. Gerçek değerimiz bu girdi için Erkek yani 1'di. Ağ eğitimindeki amacımız, girdinin tahmin değerini (0.7) 1'e olabildiğince yaklaştırmak olduğu için nöronlarda bulunan Ağırlık ve Bias değerleri bir sonraki girdi için (yani ikinci satır için ) elde edilen fark değerine göre (yani; 1 – 0.7) güncellenmesi gerekmetedir. Tabiki bu güncelleme işlemi içinde yapılması gereken çeşitli işlemler bulunmaktadır. Ayrıca her ağırlık değerinin güncelleneceği değer bir birinden farklılık göstermektedir. Bu günceleme işlemi (başka bir deyimle eğitme işlemi ) tahmin değeri ile gerçek değer arasındaki fark minimum’a inene kadar devam etmektedir. Bölüm 2'de yer alan alan yazımda sizelere bu eğitim sürecinin nasıl işlediğini anlatmaktayım. Bölüm 2'ye devam etmek isteseniz lütfen bu linke tıklayınız.
Bölüm 2 de Ağırlık ve Bias güncelenmenin nasıl yapıldığını sizlere anlatmaktayım. Aşağıdaki bağlantıdan bu yazıya ulaşabilirsiniz.
R üzerinde veri bilimi, istatistik ve makine öğrenmesi tekniklerini tüm detayları ile anlattığım, 50 Saat ve 300'den fazla ders içeriğinden oluşan Udemy eğitimine aşağıdaki bağlantıdan ulaşabilirsiniz.