Perceptron’u ve Eğitimini Python’da Kodlayarak Anlamak

Yiğit Mesci
Deep Learning Türkiye
5 min readDec 23, 2019

İlk bulunuşu 1958'lere dayanan ve nöral ağların temelini oluşturan “Perceptron” kavramını ve nasıl eğitildiğini anlamak için bu yazımızda ilk başta birkaç temel bilgiyi verdikten sonra makine öğrenmesi kütüphanelerini kullanmadan Python dilinde kodlayacağız.

Öncelikle başlamadan önce makine öğrenmesinin Supervised/Unsupervised Learning (Gözetimli/Gözetimsiz Öğrenme), Gradient Descent (Gradyan İnişi) konuları hakkında bilginizin olduğunu varsayacağım.

Perceptron, çıktısı 0 yada 1 olan bir lineer sınıflandırıcıdır. Bu cümleyi açıklamadan önce gelin yapısına bir göz atalım:

Perceptron Yapısı
Perceptron Yapısı (Resim 1)

N değişkenli eğitimi tamamlanmış bir Perceptron’da tüm değişkenler ve bias (genellikle 1 olarak tanımlanır) belirli bir ağırlık değeriyle(weights) çarpıldıktan sonra bu değerlerin toplamı, derin öğrenmede karşımıza “aktivasyon fonksiyonu” olarak çıkan fonksiyona girdi olarak verilir. Bu fonksiyondan çıkan sonuç ise modelimizin yaptığı tahmindir. Perceptron kavramı çerçevesinde adım (step) aktivasyon fonksiyonunu kullanacağız;

X sıfırdan büyük yada eşitse 1, aksi takdirde 0 (Resim 2)

Yukarıdaki cümleyi şu formülle özetleyebiliriz:

Perceptron matematiksel formülü (Formül 1)

Bu formül X0 = 1 olduğu durumda geçerlidir çünkü bias değerimizi her zaman hesaba katmamız gerekir. N değerimiz ise bias dahil olmak üzere tüm özelliklerimizin sayısını gösterir.

Örneğin şekildeki gibi bir veri setimiz olduğunu düşünelim:

Örnek 2 değişkenli veri seti (Resim 3)

Bu veri setinde A değerini 1, B değerini 0 olarak düşünürsek eğitim sonucunda şu şekilde ağırlıklar elde edeceğiz:

[ w0 = 2895, w1 = -555.21432875, w2 = -1128.9256635 ]

A ve B’nin ilk örneği için değişkenleri formülde yerine koyarsak;

(2895 * 1) + (-555 * 0.297115)+ (-1128 * -0.152625) =~ 2900 olarak çıkar ve sıfırdan büyük olduğu için A olur.

(2895 * 1) + (-555 * 6.430167) 3568 + (-1128 * 6.880947) =~ -8400 olarak çıkar ve sıfırdan küçük olduğu için B olur.

Tek bir perceptron yapısını göz önünde bulundurursak her değişkeni sadece bir tane ağırlık değeriyle çarptığımız için elimizde lineer bir model olacaktır. Bunun anlamı tek bir perceptron ile, hipotez düzleminde sadece düz bir çizgiyle ayrılabilen verileri sınıflandırabiliriz. (Derin nöral ağlarda bu lineerlikten kurtulmak için aktivasyon fonksiyonu seçimi de önemli bir faktör.)

Yeni örnekler eklendikçe lineer sınırın evrimi (Resim 4)

Örneğin XOR kapısının çıktısını tek bir perceptron ile öğrenemeyiz.

XOR Kapısı (Resim 5)

Çünkü gördüğünüz gibi kırmızı ve beyaz noktaları tek bir çizgiyle ayırmak imkansız.

Buraya kadar perceptronun nasıl bir yapıya sahip olduğunu öğrendiğimize göre asıl önemli kısım olan eğitim kısmının nasıl olacağına geçebiliriz.

Değiştirebileceğimiz iki parametre var: Ağırlıklar ve aktivasyon fonksiyonu. Perceptron kavramında ikili (binary) sınıflandırma yaptığımız için adım aktivasyon fonksiyonunu kullandığımızı zaten söylemiştik. Geriye değiştirebileceğimiz sadece ağırlıklar kalıyor. Peki elimizdeki veri setine göre bu ağırlıkları nasıl değiştirebiliriz?

Öncelikle ağırlıklar değişmeden önceki sistemin hatasını anlamak için bir hata fonksiyonu seçmeliyiz.

Ortalama Karesel Hata (Mean Squared Error) (Formül 2)

Formüldeki D veri setinin tüm örneklerini, Td istenilen, yani veri setinde doğru olarak verilmiş hedef değerini, Od ise ağırlıklara bağlı olan perceptronun yaptığı tahmin değerini gösterir.

Bu fonksiyonun her ağırlığa bağlı türevi ise o ağırlığın ne kadar değişmesi hakkında bilgi verecektir.

Ağırlıktaki değişim (Formül 3)

Hata fonksiyonumuzun ağırlığa bağlı türevini hesaplayıp, öğrenim katsayısı (learning rate) adını verdiğimiz bir oranla çarptığımızda her bir ağırlığın ne kadar değişmesi gerektiği hakkında bilgi sahibi oluyoruz. Öğrenim katsayısı değerin küçük seçilmesi kararlı ama yavaş bir şekilde; büyük seçilmesi hızlı ama sonlara doğru istemediğimiz ağırlıklara ulaşmamızı sağlayabilir. Bu yüzden bu parametrenin seçimi önemlidir. (Genellikle 0.01, 0.1 gibi değerler kullanılır.)

Hata fonksiyonumuzun ağırlığa bağlı türevini hesaplarsak;

Hata fonksiyonun ağırlığa göre türevi (Formül 4)

Tüm hepsini toparlarsak perceptron için ağırlıkları şu şekilde değiştirmemiz gerekiyor:

Percepton ağırlık güncellemesi (Formül 5)

Formüldeki Xi bir ya da birden fazla veri örneğini veya tüm veri setinin bir sütununu ifade ediyor olabilir. (Örn w1 Resim 3'deki ilk veri satırıyla ilişkilidir.) Bu Gradient Descent algoritmasının hangi yöntemini kullanmak istediğimize göre değişecektir. Bunları kabaca açıklayacak olursak;

Eğer tüm veri setinde hataları hesaplayıp ardından ağırlığımızı güncellemek istersek bunun ismi Batch Gradient Descent olacaktır. Bunu her bir örnek için (t-o) değerimizi bir vektörde sakladıktan sonra tüm veri setimizin değişkenlerinin bulunduğu X vektörü ile iç çarpımını hesaplayarak bulabiliriz.

Eğer her bir rastgele örnekten sonra ağırlıklarımızı güncellersek Stochastic Gradient Descent, belirli bir sayıda rastgele örneklerden sonra güncellersek Mini-Batch Gradient Descent olacaktır.

Bu yöntemlerin avantajları ve dezavantajlarına çok fazla değinmeyeceğim. Her ne kadar büyük ve çok verili nöral ağlarda çok zaman alsa ve RAM’i çok fazla işgal ettiği için işlevsiz olsa da bugünkü uygulamamızda veri setimiz büyük olmadığı ve sadece bir perceptron inşa ettiğimiz için Batch Gradient Descent algoritmasını kullanacağız.

Resim 3'deki gibi 400 tane örnekten oluşan TSV(Tab-separated values) formatında bir veri setine sahip olduğumuzu düşünelim;

Veri Hazırlık Aşaması

Verilerimizi kodumuzdaki gibi hazırladığımızda (M, N+1)’lik yani örneğimizde (400,3)’lük bir X matrisimizi ve (M,1)’lik yani (400,1)’lik sadece 1 veya 0'dan oluşan y hedef vektörümüzü elde ediyoruz.

Ardından Perceptron sınıfımızı oluşturacağız. Bunun için iki tane parametrenin belirlenmesi gerekiyor: Öğrenim katsayısı(learning rate) eta ve eğitim turu sayımız epoch. Bu eğitim turu tüm veri setinin üstünde kaç kere işlem yapacağımızı söyler. Biz zaten ağırlıkları tüm veri setindeki hatayı hesaplayıp güncelleyeceğimiz için (Batch Gradient Descent’den dolayı) epoch sayımız güncelleme sayısına eşit olacaktır.

Formül 1'deki güncel ağırlıklarla çıktı hesaplamayı ve tahmin işlemini yapmak için iki tane fonksiyon tanımlıyoruz. self.w_ ağırlıklarımızın bulunduğu vektör. (Temel eğitim fonksiyonunda tanımlanacak.)

Ardından temel eğitim fonksiyonumuz geliyor. İki tane iç içe for döngümüz olacak. Tüm veri setindeki örnekler için hata değerimizi hesaplayıp sonra ağırlıkları güncelleyeceğiz ve bunu eğitim turu (epoch) sayımız kadar yapacağız.

Ayrıca ilk başta büyük bir eğitim katsayısı (eta) ile büyük adımlarla ilerleyip gereksiz uzun işlemlerden kurtulduktan sonra her eğitim turunda (epoch) hatamız minimuma yaklaştıkça bu değeri azaltarak gitmek derin öğrenmede kullanılan bir yöntemdir ve buna “Annealing Learning Rate” denir.

Eğitimi tamamlarsak;

W vektörümüz ağırlıklarımızı, Errors vektörümüz ise her eğitim turundaki hata sayısımızı gösteriyor. Tek bir perceptron için en iyi eğitimle 311/400 doğruluk oranını yakalayabiliyoruz.

Bütün koda ve veri setlerine https://github.com/yitopeligo/perceptron-training adresinden erişebilirsiniz.

--

--