“Reinforcement Learning” ile Veri Analizi ve Anlamlandırma

Bu yazıda, bir Reinforcement Learning (Pekiştirmeli Öğrenme) uygulaması tasarlayacak ve Dense nöron tipi katmanların verimliliğini karşılaştıracağız.
Sistemin amacı; uygulanması kolay, Dense nöron ağlarıyla etkili ve zaman açısından verimli bir Reinforcement Learning DQN (Deep Q-Networks) modeli tasarlamaktır. Sistem, kullanıcıların farklı optimizer, loss fonksiyonlarının performans ölçütlerinin karşılaştırmalarını içerecek, bu verilere ve yaptığımız ince ayarlara göre hangi yolun bizim için ve seçtiğimiz dataset için doğru olduğunu bulmaya çalışacağız.
Aşağıdaki kodları okumadan önce bilmemiz gereken birkaç terim var, bunlar;
Keras: Makine öğreniminde bugün en yaygın kullanılan kütüphanelerden biridir. Keras, makine öğrenimini daha hızlı ve daha kolay hale getiren sayısal hesaplama için açık kaynaklı bir kütüphanedir.
Dense: Kullanacağımız gizli katman (internetteki kaynaklarda geçen adıyla hidden layer) bir sinir ağındaki normal bir nöron katmanıdır. Her nöron, önceki katmandaki tüm nöronlardan girdi alır, böylece bir sonraki katmandaki nöronlara bağlanır.
DQN: Oluşturduğumuz neuralnet bir ceza-ödül sistemiyle çalışır ve kendisini eğitir. Bu ceza ve ödüllere göre kurduğumuz ağ, ağ üzerindeki bağlantıların weight’lerini değiştirerek en doğru cevaba ulaşmaya çalışır. Uzun vadede ödülünü en üst düzeye çıkarmak için “başvuruda bulunabileceği” çalışan agent (yani kurduğumuz sinir ağına verilen isim) için kesin bir matris oluşturur.
DQN i biraz daha açmak gerekirse, bir agent eğitim uygulamasında, ham girdili bir eylemler matrisi olabilir; birinci temsili katman eylemi soyutlayabilir ve durumları kodlayabilir; ikinci katman, durum düzenlemelerini oluşturup kodlayabilir; üçüncü katman, bir durum üzerindeki bir eylemi kodlayabilir ve dördüncü katman, mevcut durumun eyleminin uygun bir sonucunu tahmin edebilir.
Görselde gördüğümüz döngünün bize kısaca anlattığı şudur; oluşturduğumuz sinir ağı her aldığı kararın sonucunu bir sonraki kararı için tekrar işleyip, bir sonraki kararının başarı yüzdesini artırmaya çalışıyor ki bize en iyi sonucu sunabilsin.
Bu yazımda kodlarımı Google Colab kullanarak çalıştırdım. Kullandığım dataset’i de Kaggle’dan aldım. Kullandığım dataseti’inin ismi “unemployment_data_us”. Bu dataset içerisinde belli grupların yıllara göre işsizlik yüzdelerini bize sunuyor.
Öncelikle kullanacağımız kütüphaneleri ekleyelim:
import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense from tensorflow.keras.losses import MSE from tensorflow.keras.activations import softmax, sigmoid, relu, elu from tensorflow.keras.optimizers import Adam import matplotlib.pyplot as plt import seaborn as sns %matplotlib inline
Her bir kütüphanenin hangi parametreleri aldığını ve ne cevap verdiğini yazının en altındaki Tensorflow linkinden rahatça görebilirsiniz.
Dataset’i okumak için “Pandas” adlı bir kütüphanden yararlanıyorum.
dataset = pd.read_csv(‘unemployment_data_us.csv’) dataset.head()
Şimdi üzerinde çalışmaya ve manipüle etmeye başlamadan önce veriyle alkalı ortaya çıkarmamız gereken belli özellikler var. Öncelikle görmek istediğim verinin boyutu:
dataset.size
Sonuç: 1848
Verinin içerisinde NaN yani boş değerler var bunlar aslında pek işimize yaramıyor. Bunları yine Pandas’ın bir özelliği ile değiştirmemiz gerekiyor.
dataset.fillna(value=0, inplace=True)
İlk değer yerine ne koymak istediğim, diğeriyse herhangi bir eşitleme yapmadan dataseti güncellemesi için. Direkt bir değer vermektense bir metot da kullanabilirdim. Şu anlık bu daha çok işime yaradığı için bu şekilde ilerliyorum.
dataset.info()
Burada tüm NaN dataların kaybolduğunuda netçe görebiliriz.
Ayrıca sıkça kullanılan diğer bir Pandas özelliğiyse veriler arasındaki korelasyonu bize listeleyen .corr() methodudur.
dataset.describe()
Bu metot ile yine işimize yarayacak ve veriyi bize istatistiksel olarak tarif edecek diğer bilgilere ulaşabiliriz. Bunu genelde verinin bütünü sayısal verilerden oluşuyorsa kullanmak daha anlamlı.
Verimiz kadın erkek ve etnik değerleri ayrı değerlendirdiği için ben ayrı bir `total` sütunu daha oluşturup bunu doldurmak için erkek ve kadın verilerinden yararlanacağım.
dataset[‘total’] = dataset[[‘Men’, ‘Women’]].mean(axis=1)
Şimdi ilk önce yapmak istediğim bu dataları X(tahmin edilecek kısım) ve y(hedeflenen kısım) olarak ayırmak istiyorum. Biz insanlar düşünürken almak istediğimiz verileri gözümüzle rahatça seçip ona göre kafamızda sadece kendi anladığımız bir hesap oluşturabiliriz ama bunu koda da dökebilmemiz gerekiyor, bu işlemi bu nedenle yapıyoruz. Yani elimizdeki verilerin hangi kısmına göre istatistiğimizi kuracağımızı koda da anlatabilmemiz gerekiyor.
X = dataset.iloc[:, 4:6].values y = dataset.iloc[:, -1].values X, y
X: lise mezunu olanlar ve onların not ortalamaları
y: işizlik oranları
olarak belirledim.
İlk olarak bir clustering (kümeleme) ve classification (sınıflandırma) yapmak istiyorum. Peki nedir bu clustering ve classification KNN veya k-NN olarak da bilinen (k-en yakın komşular algoritması)? Bireysel bir veri noktasının gruplandırılması hakkında sınıflandırmalar veya tahminler yapmak için yakınlığı kullanan, parametrik olmayan, denetimli bir öğrenme sınıflandırıcıdır. Plot üzerine dataları koyduğumuzda, onların hangi grup içerisinde olduğunu görmemiz için kullanılan bir algoritmadır, veri içerisindeki sayı karmaşasının insan için daha anlaşılır hale gelmesi için basit güzel bir yoldur.
Bunu çizmek için birkaç tane yaklaşım var. Adından anlayabileceğimiz gibi en yakın komşuyu arıyoruz. Yani bu plot üzerinde noktaya ve/veya noktalara en yakın diğer noktaları kapsayan alana verilen isim. Peki bunu nasıl hesaplıyoruz? Bunun en basiti euclidian distance:
Bunu seçtiğimiz noktayı baz alarak diğer tüm veriler için uygulamamız gerekiyor. Fakat şöyle bir durum var; normalde classification devam eden türde veriler için çalışmaz. Yani y(hedef datası) üzerinde biraz oynama yapmamız gerekiyor. Aslında bunun açıklaması da şu şekilde; bizim hedef datamızın evet/hayır, 0–1, geçti/kaldı gibi datalar olması gerekiyor ancak öyle değil. Bu yüzden sklearn’ün labelEncoder() methodunu kullanabiliriz. Aksi takdirde veriler modele uymaz.
from sklearn import preprocessing from sklearn import utils lab = preprocessing.LabelEncoder() y_transformed = lab.fit_transform(y)
Şimdi verileri son modele sokmadan önce son bir işlem kaldı; o da X ve y’yi eğitim ve test verisi olarak ayırmak. Şanşlıyız ki; bunun içinde bir metot hali hazırda var.
X_train, X_test, y_train, y_test = train_test_split(X, y_transformed, test_size=0.2, random_state=0)
İlk olarak hiçbir şey bilmediğimizi ve metot girdilerinde ufak değişiklikler yaparak gittiğimizi farz edelim.
KNN’nin n değerini 15 olarak tanımlıyorum, daha sonra optimal n değeri nasıl bulunur ona da gelceğiz.
classifier = KNeighborsClassifier(n_neighbors=15) classifier.fit(X_train, y_train) classifier.predict([[30, 8000]])
Şimdi modeli tamamladığımıza göre test verilerini öngörülen verilerle karşılaştıralım:
y_pred = classifier.predict(X_test) y_pred, y_test
Buradaki sonuçlardan yola çıkarak loss (yani modeldeki kaybın) çok olduğunu rahatlıkla söyleyebiliriz. Ayrıca modelin accuracy değerine bakarsak;
accuracy = accuracy_score(y_test, y_pred)*100 str(round(accuracy, 2))
% 11.11 gibi bir sonuç çıkıyor. Şimdide optimal n değerini hesaplayıp sonuçları tekrardan gözden geçirelim.
Bunun için kendi yazacağımız birkaç methoda ve veride yapmamız gereken bazı ayarlamalara ihtiyaç duyacağız.
# creating list of K for KNN k_list = list(range(1,50,2)) # creating list of cv scores cv_scores = [] # perform 10-fold cross validation for k in k_list: knn = KNeighborsClassifier(n_neighbors=k) scores = cross_val_score(knn, X_train, y_train, cv=7, scoring='accuracy') cv_scores.append(scores.mean())
Burada hangi n değerine hangi score’ları aldığımızı bir listeye alıp aşağıdaki kod ile bunu görselleştireceğiz
MSE = [1 - x for x in cv_scores] plt.figure() plt.figure(figsize=(15,10)) plt.title('The optimal number of neighbors', fontsize=20, fontweight='bold') plt.xlabel('Number of Neighbors K', fontsize=15) plt.ylabel('Misclassification Error', fontsize=15) sns.set_style("whitegrid") plt.plot(k_list, MSE) plt.show()
k_list[MSE.index(min(MSE))]
Sonuç: 3
Şimdi bir de aynı işlemleri n yerine 3 değerini koyarak denersek, bize verdiği tahmini test değerleri aşağıdaki gibi çıkıyor.
array([21, 68, 38, 12, 9, 46, 0, 64, 68, 54, 65, 68, 0, 6, 68, 66, 12, 2, 16, 57, 20, 1, 1, 63, 32, 56, 58])
Bu sonuçların accuracy’sine bakarsak eğer;
accuracy = accuracy_score(y_test, my_y_pred)*100 str(round(accuracy, 3))
Sonuç: 72.815 çıkıyor yani bu daha başarılı bir model oldu diyebiliriz.
Buraya kadar veriyi istatistiksel metotlar kullanarak nasıl anlamlandırabiliriz kısmını gördük. Bir de bunu yapay sinir ağı oluşturarak nasıl oluşturabiliriz onu görelim.
Önce yeni bir sayfa oluşturup kütüphaneleri dahil ediyorum. Yukarıda yaptığım train_test_split() kısmına kadar olaylar aşağı yukarı aynı olduğu için tekrar anlatmayacağım.
Sadece burada veri içerisine farklı manipülasyonlar da eklememiz gerekecek.
dataset.drop('Date', axis=1, inplace=True) #date kolonunu siliyoruz #burada her ayı içeren bir dictionary oluşturuyoruz month = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8, 'Sep':9, 'Oct': 10, 'Nov': 11, 'Dec': 12} dataset['Month'] = dataset['Month'].map(month) dataset['Day'] = 1 #tarih saat değerine biçimlendirmeyi kolaylaştırmak için gün sütunu ekliyoruz dataset['Date'] = pd.to_datetime(dataset[['Year', 'Month', 'Day']]) dataset.set_index('Date', inplace=True) dataset.sort_index(inplace=True) #tarihe göre dataları sıralıyoruz
İlk olarak sequential basit bir nn oluşturalım. Bunun için Tensorflow kütüphanesinden faydalanacağız.
model = Sequential() model.add(Dense(units=16, activation='relu')) model.add(Dense(units=16, activation='relu')) model.add(Dense(units=1, activation='sigmoid')) model.compile(optimizer = Adam(learning_rate=0.1), loss=MSE, metrics=['accuracy']) model.fit(X_train, y_train, batch_size=64, epochs=100)
Aktivasyon fonksiyonu, küçük girdiler için küçük bir değer ve girdiler bir eşiği aşarsa daha büyük bir değer veren yapay sinir ağlarında kullanılan bir fonksiyondur.
Optimize ediciler, kayıpları azaltmak için sinir ağının ağırlıklar ve öğrenme oranı gibi özelliklerini değiştirmek için kullanılan algoritmalar veya yöntemlerdir. Optimize ediciler, fonksiyonu minimize ederek optimizasyon problemlerini çözmek için kullanılır.
Optimizer‘ın öğrenme değerini önce 0.1, hidden layer ın aktivasyon methodunu relu(rectified linear unit), output layer’ın aktivasyon methodunu ise sigmoid olarak belirledim.
relu:
Kısaca bu metot gelen negatif değerleri 0, pozitif olanlarıysa kendileri olarak geri döndürüyor.
sigmoid:
Bu metot ise değerleri 0 ile 1 arasında bir değer olarak optimize ediyor.
Bunu kullandığımda 50 iterasyonda da 100 iterasyonda da modelin loss’unu düşürmeyi başaramadım. 830 1500 gibi çok büyük değerler içerisinde gezdi. Bunun için batch_size’ı biraz değiştirmeyi denedim. Sonuç yine değişmeyince nn sayılarıyla oynamak istedim. Fakat parametre sayılarının artması sonuçların daha da ufalmasına ve loss’un artmasına sebep verdi.
Aslında burada başta dataset’imiz için doğru modeli bulmak için modeli biraz değiştirmekten başka yol yok.
Mevcut modelin parametre sayıları elimizdeki dataset in boyutu için yeterli.
model.summary()
Dediğimizde, bize eğitilen modelin parametre bilgilerini veriyor.
Burada parametre sayısını layerların önceki girdi sayısıyla çarpılması sonucu buluyoruz.
Uzun denemeler sonunda modeli şu hale getirdim:
model = Sequential() model.add(Dense(units=6, activation='relu')) model.add(Dense(units=6, activation='relu')) model.add(Dense(units=1, activation='elu')) model.compile(optimizer = Adam(learning_rate=0.001), loss=MSE, metrics=['accuracy']) model.fit(X_train, y_train, batch_size=16, epochs=50)
Katmandaki parametre sayısını düşürünce ve çıktı katmanının aktivasyon methodunu elu yapınca daha düzgün sonuçlar aldım. Aynı şekilde learning_rate değerini de biraz düşürmem gerekti.
Burada elu’yu açmak gerekirse:
Sigmoid sadece 0 ve 1 arasında bir değer verdiği ve bizim hedef verilerimiz daha büyük sayılar olduğu için bize çok uygun değildi. Burada aktivasyon methodunu değiştirmektense, y(hedef verileri) yi normalize etmek de bir seçenek. Daha büyük nn ler ile çalışırken hesapların daha hızlı olabilmesi için normalize edilmesi bazı işlerde daha işimize yarayacaktır.
Modeli bu şekle getirdikten sonra loss daha düşük değerler vermeye başladı ~2 ve ~1 arasında değerler gördüm.
Son olarak önceki KNN’deki şekilde test verimizle modeli test edip sonuçları karşılaştırma vakti geldi.
y_pred = model.predict(X_test) y_pred, y_test
Görüldüğü üzere, tahmin edilen sonuçlar bu sefer daha yakın çıkmış yani modelimiz daha başarılı tahminler yapma eğiliminde. Tekrar accuracy yi hesapladığımızda:
accuracy = accuracy_score(y_test, my_y_pred)*100 str(round(accuracy, 3))
Sonuç: 87.720
Bu yazıda, Dense nöron tipi için DQN model eğitimini ve karşılaştırmasını ele aldık. En önemli şey, doğru parametreleri ayarlamak ve karar vermekti, μ(learning_rate) odaklama yapısının önde gelen katsayısıdır. Katmanların doğru çalışıp çalışmadığından emin olmak için değerlendirmenin bir yolu loss karşılaştırması yapmaktır. Her q-değeri hesaplamasında loss değerleri alınarak ve bu değerlere göre tahmin edilmeye çalışılarak aynı değerlendirme uygulanabilir.
Kaynaklar:
https://keras.io/api/layers/core_layers/dense/ https://www.kaggle.com/datasets/aniruddhasshirahatti/us-unemployment-dataset-2010-2020 https://scikit-learn.org/stable/index.html https://www.tensorflow.org/api_docs/python/tf https://pandas.pydata.org/pandas-docs/stable/index.htmlYazan: Ersin Çebi
Yayınlanma Tarihi: 24.10.2022
