Hebb öğrenme kuralı

Uzun zamandır algoritma simulasyonlarına ve özellikle de yapay sinir ağlarına ara vermiştim. Bu sefer bu alana Hebb öğrenme kuralıyla bir dönüş yapmak istedim.

Bu basit kural Donald Hebb’in 1949 yılında yayınlanan “The Organization of Behaviour” kitabında tanımlanmıştır. Eğer bir nöronun bir aksonu başka bir nörona yeterince yakınsa ve bu nöron diğer nöronun ateşlenmesinde sık sık bir rol oynuyorsa aradaki bu bağlantı güçlenir.

Evet , bu kural biyolojik bir şekilde tanımlanmış. Çok normal, çünkü Donald Hebb nöropsikoloji alanında çalışan bir psikologtu. Peki yapay sinir ağlarında bu kuralı nasıl düşünebiliriz?

Bu şekilde simulasyonda da kullandığım basit bir örneği görüyorsunuz. Şistemin girdileri x1 ve x2 değerleri. Bunları başka nöronların çıktıları olarak da düşünebilirsiniz. Bu çıktılar w1 ve w2 ağırlıklarıyla çarpıldıktan sonra nöronda toplanıyor ve nöronun ateşleyip ateşlemediğine bakılıyor. Eğer ateşlemişse y çıktısı 1 değerini alıyor, ateşlememişse -1 değerini.

Kuralı şekle göre yazarsak yapay sinir ağlarında kullandığımız şekli şöyle oluyor:

\(\Delta w = \eta \cdot x \cdot y \)

Burada \(\Delta \) ağırlıklardaki değişimi göstermekte. \(\eta \) da öğrenme faktörü oluyor, genelde küçük bir sayıdır ve simulasyonda 0.1 değerini kullandım.

bu gösterimde x ve w vektördür. Bunları daha açık yazarsam şöyle olur.

\(\Delta w1 = \eta \cdot x1 \cdot y \)

\(\Delta w2 = \eta \cdot x2 \cdot y \)

Hesapta kullandığım şekli de şöyle:

\(w1 (sonraki) = w1 (önceki) + \eta \cdot x1 \cdot y \)

\(w2 (sonraki) = w2 (önceki) + \eta \cdot x2 \cdot y \)

Her adımda önce bir girdi seçiliyor. Yani o adım için x1 ve x2 değerleri seçiliyor. Ardından y çıktısı o anki ağırlık değerleriyle hesaplanıyor. Ardından o girdiler ve çıktılar için ağırlıkların yeni değerleri yukarıdaki formüllere göre hesaplanıyor ve ağırlık değerleri güncelleniyor.

Simulasyonda bu modeli yukarıda açıkladığım şekilde kullandım. Simulasyon başlatıldığında ağırlıklara rastgele değerler atanıyor. Simulasyonun altında ağırlıkları nasıl güncellediğimi gösterdim. En altta ise öğrenme için kullandığım girdi noktalarını bir koordinat sisteminde kırmızı noktalarla ve ağırlık vektörünün de bu noktaları birbirinden nasıl ayırdığını gösterdim. Bu öğrenmede sistem kendi kendine öğreniyor, yani sisteme kullanılan nokta için ulaşılması gereken çıktı değeri hiçbir şekilde verilmiyor. Bu durumda sistem verilen noktaları kendine göre sınıflandırıyor. Tabii ki sadece iki nokta ile bu kuralın öğrenme yeteneği yeterli bir şekilde anlaşılamaz ama en azından algoritmanın nasıl çalıştığını görebiliriz.

Simulasyon

Perceptron (Yapay sinir ağları)

Yapay sinir ağları, yapay zeka sayesinde bir süredir yine oldukça popüler konulardan biri oldu. Yaklaşık çeyrek asır önce üniversitede bu dersi almıştım ve hoşuma gitmişti. Sanki yeterince hobim yokmuş gibi bu alandaki yeniliklere göz atmaya karar verdim. Yapay zeka uygulamalarını programlamak için olası en kolay yolların matlab ya da tensorflow tabanlı kütüphaneler olduğunu gördüm. Bu arada biraz python da öğrenebilirim belki dedim ve tensorflow kendimi akımına bıraktım.

Bu ikinci öğrenme aşamasında yaptıklarım üzerine yazılar yazmayı düşünüyorum. Sistem olarak Ubuntu 19.04 altında python 3.7 ve pip3 kullanıyorum. Editör olarak eclipse ve PyDev eklentisini (7.3 versiyonu) kullanıyorum. Bu sistemlerin kurulumu için internette bir sürü kaynak olduğundan bunlara şimdi girmeyeceğim.

İlk yazımda yapay sinir ağlarının temel taşlarından olan perceptron modeli üzerine bir animasyon göstermek istedim. Animasyonları mümkün olduğunca HTML 5 ile programlamak istiyorum. İlk animasyonda perceptron modelinin en basit halini programladım. Sadece bir girdisi ve tek bir ağırlık değeri var. Dolayısıyla hesaplamaları da oldukça kolay. Umarım anlaşılması da kolaydır. Bir sonraki animasyonda birden fazla girdisi olan perceptron programlayacağım.

Perceptron için basit bir blok diyagram

Perceptron kabaca birden fazla girdisi ve bir çıktısı olan bir nöron modelidir. Perceptrona gelen girdiler önce her girdiye ait olan ağırlık değerleriyle çarpılır ve sonra bu çarpımlar toplanır. Bu toplamlar belli bir değerin üzerindeyse bu perceptron aktifleşir, değilse aktifleşmez.

Şimdi animasyonla biraz oynayalım. Girdi değeri bu animasyon için -1 ile 1 aralığında seçilebiliyor. Ağırlık değeri de -10 ile 10 arasında seçilebiliyor. Ardından başlat düğmesine basıldığında yapılan hesaplamalar ekranda gösteriliyor. En sonunda perceptron etkinleşmişse yeşil, etkinleşmemişse kırmızı oluyor. Etkinleşme için bu animasyonda signum fonksiyonunu kullandım. Yani toplamın sonucu sıfırdan büyükse bu fonksiyon 1 değerini veriyor. Diğer her durumda da 0 değerini.

Animasyonun altında bir sayı doğrusu üzerinde seçilmiş ağırlık değeri için hangi girdilerde perceptronun etkinleşeceğini (yeşil renkli kısım) ve hangi girdi değerleri için etkinleşmeyeceğini (kırmızı renkli bölge) çizdirdim. Bu gösterim için JSXGraph kütüphanesini kullandım. Bu şekilden de anlaşıldığı gibi eğer ağırlık değeri pozitif ise, pozitif girdiler için etkinleşme olacak, negatif değerlerde perceptronun çıktısı 0 olacak. Eğer ağırlık değeri negatif ise bu sefer perceptron negatif girdiler için etkinleşecek ve pozitif girdiler için 0 çıktısını verecek. Bunu matematiksel olarak da görmek kolay. Yukarıdaki diyagramda sadece bir tek girdi ve tek ağırlık olduğunu var sayalım. Animasyonda kullandığım f fonksiyonu şu şekildedir.

math-sgn-function-in-cpp How to Implement The Sgn Function in C++? c / c++ math programming languages

O zaman çıktı fonksiyonunu şu şekilde yazabiliriz:

\(o = sign(i\cdot{w}) \)

Eğer girdi (i) sıfırsa ya da ağırlık (w) sıfırsa, çıktı 0 olacaktır ( o = sign(0) ). Bunun dışındaki durumlar için ise aşağıdaki şekilde olacaktır.

\(o = sign((w > 0) \cdot{( i < 0)}) = sign(< 0) = -1 \)

\(o = sign((w > 0) \cdot{( i > 0)}) = sign(> 0) = 1 \)

\(o = sign((w < 0) \cdot{( i < 0)}) = sign(> 0) = 1 \)

\(o = sign((w < 0) \cdot{( i > 0)}) = sign(< 0) = -1 \)

Yukarıda w > 0, pozitif ağırlık değerleri, i > 0 da pozitif girdi değerleri anlamına geliyor. sign(>0) da sign fonksiyonunun girdisinin pozitif olduğu anlamına geliyor. Aynı şekilde < işaretini de bu değerlerin negatif olduğu durumlar için kullandım.

Yani bu basit perceptron ağırlığın sıfır olmadığı durumda sayı doğrusunu hep sıfır noktasında ikiye bölüyor. Sıfırın bir tarafı için -1 değerini üretirken, diğer tarafı için de 1 çıktısını üretiyor. Bu şekliyle oldukça sıkıcı bir zeka birimine benziyor ama ileride bu aksaklıkların üstesinden gelmenin yollarına bakacağız.