Yapay Sinir Ağları (ANN) Keras ile Uygulama

Bir önceki yazımızda Neural Network konusuna giriş yapmıştık. En azından bir Neural Network modelinin hangi temel bileşenlerden oluştuğunu az çok biliyoruz. Şimdi bir uygulama ile nasıl çalıştıklarını daha yakından inceleyelim.

Bu uygulamada Neural Network kurarken Keras framework'ünden yararlanacağız. Aslında burada bir derin öğrenme çalışması yapmış oluyoruz. Başlamadan önce bir derin öğrenme projesinde ne gibi yapılar var ve hangi sıralamayla bu yapıları çalıştıracağız ona göz atalım. Bundan sonra Neural Network yerine Sinir Ağı şeklinde türkçe kavramı kullanacağız.

  • Data yükleme ve Ön işleme: Verimizi sinir ağına sokmadan önce bu sinir ağında işlenecek hale getirmek gerekiyor. Bu işlem sinir ağları uygulamalarında en temel ve dikkatli olunması gereken kısımdır. Bu kısımda veriyi daha anlaşılabilir hale getirmeye çalışacağız ve bu da bize veriyi daha iyi kavramamızı sağlayacaktır. 
  • Model tanımlama: Kuracağımız sinir ağı modelinin giriş çıkış sayılarını, gizli katman sayılarını ve boyutlarını tanımlamamız gerekiyor. Kısaca ağın yapısına ilişkin parametreleri tanımlayacağız.
  • Kayıp ve İyileştirici: Amacımıza yönelik olarak kayıp fonksiyonumuzu tanımlamaya ihityacımız var. Aynı zamanda iyileştirici modelini de belirlemek gerekiyor. Öğrenme oranı ve iyileştiricinin hiper parametrelerini tanımlayacağız. 
  • Modeli Fitleme: Bu aşama sinir ağı kurulumunun training (eğitme) aşaması. Burada sinir ağını eğitmek için gerekli epoch (iterasyon) sayısını belirleyeceğiz. 

Son aşamadaki işlem olarak modeli fitledikten sonra yani sinir ağı modelini kurduktan sonra artık test verisi üzerinde ağı test edebiliriz. Test ederken overfitting , underfitting gibi istenmeyen durumların gerçekleşip gerçekleşmediğini de kontrol etmiş oluruz.

Veri Önişleme

Bu yazıdaki uygulamada veri seti olarak mobil telefon fiyat aralığı verileri kullanılarak bir sınıflayıcı tasarlanacaktır. Veri seti 20 özelliklten oluşuyor ve telefonların fiyat aralığını bu özelliklere göre kestirmek istiyoruz. Bu aralık 4 sınıfa ayrılıyor. Verisetinin parametreleri şu şekildedir:

'battery_power', 'blue', 'clock_speed', 'dual_sim', 'fc', 'four_g',
'int_memory', 'm_dep', 'mobile_wt', 'n_cores', 'pc', 'px_height','px_width', 'ram', 'sc_h', 'sc_w', 'talk_time', 'three_g','touch_screen', 'wifi'

Burada her ürün için 20 adet nitelik referans alınmaktadır.

Sinir ağımızı beslemeden önce verimizi neye göre işleyeceğimizi belirten bir spesik yönteme ihtiyacımız var. Verinin önişlemesi verinin türüne bağlıdır aslında. Bu çalışmamızda aynı zamanda tablo halindeki bir verisetinin nasıl ele alınacağını da görmüş oluyoruz. Özellikle yine Tensorflow'un sıklıkla kullanıldığı "image classsification - görsel sınıflandırma"  uygulamalarında da tablo yerine resim verisi ele alınabilir. Şimdi kodumuza geçelim artık:

import numpy as np
import pandas as pd
#dataset import
dataset = pd.read_csv('train.csv') 
#You need to change directory accordingly
dataset.head(10) #Return 10 rows of data

Burada öncelikle numpy ve pandas veri analiz araçları kütüphane olarak ekleniyor. Daha sonra verimizin eğitilecek olan kısmı (train) .csv dosya formatında okunmak üzere pandas kütüphanesi yardımıyla çağırılır. Çağrılan veriseti "dataset" adında bir tablo değişkenine atanır. Daha sonra elde edilen "dataset" veri tablosunun ilk 10 satırı okunur. Bu işlemin sonucunda okunan veriseti aşağıdaki gibi görünür:


Görselde görüleceği üzere buraya tam sığmamış olsa da yukarıdaki parametrelerin karşılıkları farklı modeller için görülebilmektedir.

#Changing pandas dataframe to numpy array
X = dataset.iloc[:,:20].values
y = dataset.iloc[:,20:21].values


Burada dataset veri çerçevemizi (tablomuzu) numpy dizilerine dönüştürüyoruz. Burada X dizisi feature dedigimiz özellikleri içeriyorken, y dizisi class olarak tanımladığımız çıkış değişkenlerini içerir. Burada pandas veri tablosunu ayrıştırıp X ve y tablolarına atarken "iloc" fonksiyonundan faydalanıyoruz. "iloc" aslında matris seçimi yaparken pozisyon nitelikli seçim yapmamızı sağlar. 

[: , :20] ifadesi, tüm satırlar ve ilk 20 sütun anlamına gelir. Yani 0'dan başladığında 19.sütun sondur.

[: , 20:21] ifadesi ise tüm satırlar ve yalnız 20 numaralı sütun anlamına gelir. 21 numaralı sütuna kadardır. Bu da asıl verisetimizi dikkate aldığımızda en son sütundaki price_range sütununa karşılık gelir. Bu da zaten kestirim değerimiz olan y matrisini ifade edecek.

#Normalizing the datafrom sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X = sc.fit_transform(X)

Burada scikit-learn aracının fonksiyonlarından yararlanarak verinin normalizasyonu / standardizasyonu işlemini gerçekleştiriyoruz. Normalizasyon işlemi: farklı değerlerde farklı ölçeklerde olan verileri ortak bir ölçeğe indirgemek için kullanılır. Bunu yaparken değerler aralığı farklarını bozmadan yapmak gerekir. Mesela elimizdeki verisetinde battery_power değerleri 100'ler 1000'ler seviyesindeyken clock_speed değerleri 0'a yakın değerler. Hatta ondalıklı farklar var kendi aralarında. Eğer bu farklı ölçeklerdeki değerleri normalize etmeden sinir ağını besleyecek olursak, gradyenler her sütun (farklı ölçek) için farklı şekilde değişimler yaşayacak ve bu da öğrenme işleminin sürekli osilasyonlar yapmasına sebep olacaktır. Örneğin yine aynı değerleri kullanırsak, clock_speed'deki değişim değeri kestirimde ufak bir etki yaratırken battery_power parametresindeki değişimler daha büyük algılanacağı için sinir ağında kestirime daha büyük etkisi olabilir. Bu ölçek farklılığı sinir ağı tarafından algılanamayacağından sağlıksız kestirimler olacaktır. 

Normalizasyon işlemini gerçekleştirirken aşağıdaki formülden yararlanılır.

z = (x - u) / s

x : değişken
u : ortalama
s : standart sapma

Normalize edilmiş X veriseti şu şekilde olur:


Normalized data:
[-0.90259726 -0.9900495   0.83077942 -1.01918398 -0.76249466 
-1.04396559 -1.38064353  0.34073951  1.34924881 -1.10197128 
-1.3057501  -1.40894856 -1.14678403  0.39170341 -0.78498329  
0.2831028   1.46249332 -1.78686097 -1.00601811  0.98609664]


Burada yalnızca bir satır için yani bir model için parametreler örnek olarak koyulmuştur. 

Bundan sonraki adım, class'ları kodlamak olacak. Burada kodlama yöntemi olarak Hot-Encoding kullanılacak. Yani bir class tanımlarken her class'ta bir adet 1 değeri ve geri kalanları 0 olacak şekilde classlar oluşturulacaktır. Bu kodlamada amaç integer class'ları binary değerlere dönüştürmektir. Örneğin verisetimizde 3 adet class olsun. 1,2 ve 3 şeklinde tanımlansın. Bunları doğrudan kullanarak sinir ağını besleyemeyiz. Bunları önce uygun yapıya dönüştürmek laızm.

1  -    1 0 0

2  -    0 1 0

3  -    0 0 1

şeklinde kodlaması yapılır. Bu sayede her class için bir özel binary değer mevcut olur.

from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder()
y = ohe.fit_transform(y).toarray()

Buradaki kod bloğunda OneHotEncoder() fonksiyonunun kullanımı görülmektedir. Buradaki fonksyionları anlamak gerekirse, scikit-sklearn kütüphanesinin dönüşüm fonksiyonlarını gerçekleştirirken (OneHotEncoder, StandardScaler...) kullandığı metodlardan biri fit_transform'dur.

Sık kullanılan iki metod:

fit : İlgili fonksiyonu ilgili matrise uygular.

fit_transform: İlgili fonksiyonu (OneHotEncoder()) ilgili matrise (y) uygular ve sonra o matrisi dönüştürür.

OneHotEncoder() fonksiyonu tamamlanırken de toarray() metodu kullanılarak kodlanmış matris diziler halinde yazılıyor. Bu arada bu dönüşüm yapılırken kullanılan matrisler sparse(seyrek) matris olarak adlandırılır. Sparse matrisler, öğelerinin çoğu "0" olan matrislerdir. Bunun tam tersi olan matrisler zaten Dense (yoğun) matris olarak adlandırılır. Dönüşüm sonucunda elde edilen sparse matris aşağıdaki gibidir:

[[0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 1. 0.]
 [0. 0. 1. 0.]
 [0. 1. 0. 0.]
...
...]

Buraya kadar yaptığımız Normalizasyon ve Kodlama işlemleriyle, veri yükleme ve veri ön işleme adımının tamamlamış olduk. Artık verimizi sinir ağımızı beslemek için kullanabiliriz.

Sıklıkla sinir ağı uygulamalarında veri, training ve test verisi olarak iki kategoriye ayrılır. Training verisi adında da anlaşılacağı üzere sinir ağının eğitilmesi aşamasında kullanılır. Test verisi ise eğitimi tamamlanan sinir ağının test edilmesi için kullanılan veridir. Böylece sinir ağımızın ne düzeyde eğitildiğini anlayabiliriz. Bu test verisi sinir ağımız için yeni bir veri olacağından sinir ağımıza test verisinin girdilerini verdiğimizde çıktılarını ne kadar düzgün tahmin edebilirse (çıktıları biliyoruz nasılsa ama sinir ağı bilmiyor  - training verisinin aksine) o kadar dzgün bir fitting (uydurma-eşleme) yapılmış olur.

from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.1)

Bu kod bloğunda sklearn kütüphanesinin model_selection modülünden train_test_split fonksiyonunu çağırıyoruz. Buradaa test verisinin büyüklüğü, toplam verimizin yüzde 10'Luk bir kısmı olarak belirlenir. Verinin yüzde 90'lık kısmı ise training işlemi için kullanılmaktadır.

Sinir Ağı Kurma Aşaması

Sinir ağı modelimizi kurmak için Keras framework'ünden faydalanacağız. Keras, tensorflow, theano  ve cntk altyapısı üzerine kurulu yüksek seviye (high-level) bir frameworktür. Yukarıda önişlemlerden geçirdiğimiz verisetimizin 20 adet giriş değeri ve 4 adet çıkış değeri bulunmaktadır. Aslında çıkış değeri 1 adet idi fakat kodlama yapıp binary dizi haline getirirken her çıkış değeri 4 elamanlı bir dizi haline geldi hatırlarsak. 

from keras.models import Sequential
from keras.layers import Dense
# Neural networkmodel = Sequential()
model.add(Dense(16, input_dim=20, activation='relu'))
model.add(Dense(12, activation='relu'))
model.add(Dense(4, activation='softmax'))

Burada kurduğumuz yapay sinir ağında biri 16 katman diğeri 12 katman olmak üzere iki adet gizli katmanımız var. Kodu incelediğimizde keras kütüphanesinden models ve layers modüllerini kullanarak Sequential ve Dense fonksiyonlarını çağırıyoruz. 

Sequential foksiyonu burada şunu belirtiyor: ardışıl (sequentially) bir model kuruyoruz ve bu modelde eklediğimiz her katmanın çıkışı bir sonraki katmanın girişi olacak. 

model.add ifadesi ile sinir ağımıza katman ekliyoruz. Bunu yaparken ne tür bir katman istediğimizi bir argüman olarak belirtiyoruz.  "Dense " argümanı ile tam bir şekilde birbirine bağlı katmanlar ifade edilir. Dense argümanıyla buraki kullanımında, giriş katmanındaki 20 adet girişin ilk gizli katmanda 16 adet nörona tam olarak 16x20 bağlantı olacak şekilde bağlanmaları sağlanmış. İkinci Dense argümanıyla 12 nöron bir gizli katman daha eklenmiş. Burada giriş belirtilmemiş çünkü keras zaten Sequential fonksiyonuyla bir önceki satırdaki tanıımı algılayıp bir sonrakine giriş olarak sunuyor. Son satırda ise 4 nöronlu katman tanımlanmış ve burada son satır olduğu için otomatik olarak ağın son katmanı olarak algılanıyor. Giriş ve ara katmanlar tanımlanırken aktivasyon fonksyionları "relu" olarak belirlendi ama çıkış katmanında aktivasyon fonksiyonu farklı olacağı için burada da "softmax" fonksiyonları seçildi. 

Aktivasyon fonksiyonlarından kısa bahsetmek gerekirse sinir ağında nöronların çıkışlarını kontrol etmek amacıyla kullanılan 1 veya 0 , Evet veya Hayır durumlarıyla karar verilmesini sağlayan matematiksel ifadeler, kıyaslamalı işlemlerdir. Aktivasyon fonksiyonları türleri, karşılaştırmaları ve daha detaylı bilgi için şuradan [2] veya şu linkten  [3] faydalanılabilir. 

Aktivasyon fonksiyonlarının tek grafikte karşılaştırılması [3]
Şimdi sırada kayıp fonksiyonunu bir  de optimizer tanımlamak var. Keras'ta bu işi halletmek içim "compile" fonksiyonundan yararlanıyoruz. 

model.compile(loss='categorical_crossentropy', optimizer='adam', 
metrics=['accuracy'])

Kayıp fonksiyonu olarak cross entropy modeli kulllanılmıştır. Cross entropy fonksiyonu ile ama., çıkışı 0 ile 1 arasında olasılıksal değerler olan bir sınıflandırma modelinin (burada yapay sinir ağı modelimiz) performansını ölçmektir. Adından da anlaşılacğı üzere aslında entropinin tersi bir dırum ile performans değerlendirme kriterinin kararlı duruma gitmesi gerekir ki model düzgün bir yapıda olsun. Bu noktada kontrol sistemlerindeki maliyet fonksiyonlarını referans alabiliriz. Aşağıdaki grafikte görüleceği üzere cross entropy fonksiyonunu bir kestirim modeli için kullanılırken tahmin edilen değer "1" iken doğru değerden sapılması (predicted probability) durumunda "log loss" değerinin değeri artmaktaadır. Ancak asıl değeri doğru tahmin etmeye yakınlaşınca log loss değeri 0 a inmektedir. En ideal tahmin durumunda log loss = 0 olacaktır.

cross entropy tahmin kullanım grafiği [4]

Optimizasyon algoritması olarak ise Adam algoritması kullanılmıştır bu uygulamada. Optimizer olarak adlandırılan optimizasyon algoritmalarının kısaca bir tanımını yapmak gerekirse, bu yapıların yukarıda bahsettigimiz kayıp fonksiyonunu minimize etmek için sinir ağındaki nöron ağırlıkları ve öğrenme oranları gibi nitelikleri değiştirme yetkisine sahip yapılar olduklarını söyleyebiliriz. Aşağıdaki görselde güzel bir skeç görebiliriz bu yapılarla ilgili. Ayrıca optimizasyon algoritmaları hakkında daha detaylı bilgi ve karşılaştırmalar için bağlantıdan faydalanılabilir. Burada da anlatıldığı üzere hızlı ve verimli sonuçlar elde etmek için sıkça kullanılan algoritmalardan Adam optimizasyon algoritmasını kullandık biz de.

optimizasyon algoritmaları [5]

mode.compile satırının hemen altında belirtilen Metric yapısı ise sinir ağımızın performansını değerlendirmek istediğimiz yolu yöntemi belirtir. Burada biz değerlendirme kriteri olarak modelin doğruluğunu, kesinliğini baz aldık. Onu da belirtmiş olduk. Evet artık sinir ağımızı inşa etmiş bulunduğumuza göre şimdi eğitme kısmına geçebiliriz. 

Eğitim (Training) Modeli

Keras kütüphanesi eğitim aşamasını kolaylıkla yapmamızı sağlayabiliyor.

history1 = model.fit(X_train, y_train,
validation_data=(X_test,y_test), epochs=100, batch_size=64)

Bu kod satırında giriş verimizi X_train, çıktı olarak kullanılan sınıflandırma parametresini y_train olarak fonksiyona tanıtıyoruz. Ayrıca test verimizi doğrulama verisi olarak kullanıyoruz (validation_data=(X_test, y_test)) ve bu şekilde her epoch'tan sonra modelin doğruluğunu test etmiş oluyoruz. Bu bize eğitim boyunca fitting problemleri gibi durumları takip edebilme imkanı sağlıyor. Sinir ağını eğitirken ağı beslemek için kullandığımız giriş verisini sinir ağına verirken bütün veriyi tek seferde vermek çok zor. Bu nedenle her bir iterasyonda batch_size kadar veriyi giriş olarak sinir ağına veririz. Burada bu değer 64 veri adeti olarak belirlendi. Her 64 adet giriş verisi örnek değer olarak belleğe alınır ve işlenir. Belleğe alınıp işlenme süreci biten batch, bellekten temizlenir ve bir sonraki batch işleme alınır. Şimdi sinir ağımızı eğitme sürecini başlatalım:

Epoch 1/100
1600/1600 [==============================] - 1s 600us/step - loss: 1.3835 - acc: 0.3019
Epoch 2/100
1600/1600 [==============================] - 0s 60us/step - loss: 1.3401 - acc: 0.3369
Epoch 3/100
1600/1600 [==============================] - 0s 72us/step - loss: 1.2986 - acc: 0.3756
Epoch 4/100
1600/1600 [==============================] - 0s 63us/step - loss: 1.2525 - acc: 0.4206
Epoch 5/100
1600/1600 [==============================] - 0s 62us/step - loss: 1.1982 - acc: 0.4675
.
.
.
Epoch 97/100
1600/1600 [==============================] - 0s 55us/step - loss: 0.0400 - acc: 0.9937
Epoch 98/100
1600/1600 [==============================] - 0s 62us/step - loss: 0.0390 - acc: 0.9950
Epoch 99/100
1600/1600 [==============================] - 0s 57us/step - loss: 0.0390 - acc: 0.9937
Epoch 100/100
1600/1600 [==============================] - 0s 60us/step - loss: 0.0380 - acc: 0.9950


Belirlediğimiz 100 epoch (döngü) tamamlandıktan sonra sinir ağımız eğitim işlemini tamamlamış bulunuyor. Eğitim doğruluğu %99.5 değerine ulaştı. Bundan sonrasında modelimizin performansını test verisi üzerinden kontrol edebiliriz.

y_pred = model.predict(X_test)
#Converting predictions to labelpred = list()
for i in range(len(y_pred)):
    pred.append(np.argmax(y_pred[i]))
#Converting one hot encoded test label to labeltest = list()
for i in range(len(y_test)):
    test.append(np.argmax(y_test[i]))

Bu adım, daha önce gerçekleştirdiğimiz "one hot encoding" işleminin tersi oluyor bir nevi. Buradaki işlemi gerçekleştirerek class'ların integer değerlerini tekrar elde edeceğiz. Keras'ta model.predic() metodunu kullanarak kolaylıkla test verisi üzerinde kestirim çalışması yapılabiliyor. X_test olarak daha önce ayırdığımız test verisini modele giriş olarak veririz ve modelin çıkışında kestirilen değerleri softmax olarak elde ederiz. 

from sklearn.metrics import accuracy_score
a = accuracy_score(pred,test)
print('Accuracy is:', a*100)

Doğruluk oranı %93 seviyesinde elde edildi.

Modelimiz düzgün çalışmaktadır. Doğrulama kayıpları ve modelin doğruluk grafikleri aşağıdaki gibidir.


Kaynaklar









Yorumlar

Bu blogdaki popüler yayınlar

KV260 Kria Starter Kit Series: 3 - Petalinux Install and Boot

KV260 Kria Starter Kit Series: 1 - Power and Boot Up

KV260 Kria Starter Kit Series: 2 - Smartcam Application (Ubuntu)