Derin Pekiştirmeli Öğrenmeye Bir Giriş

Bir oyun oynayarak derin pekiştirmeli öğrenmeye Python kodlarıyla bir giriş yapmaya çalışacağım. Oyunumuzun adı “donmuş göl” (frozen lake), $4 \times 4$ bir tahtada oynanıyor. Sol üst köşedeki kareden başlayarak her adımda sağ-sol-yukarı-aşağı gitme aksiyonlarından birini seçerek sağ alt köşedeki ödüle ulaşmaya çalışıyoruz.

Gri kareler buz, güvenli. Mavi kareler ise delik, buralara gelirseniz kaybediyorsunuz. Bu kadar olsaydı, oyun kolay, mavilere değmeden sağ alt köşeye istediğin gibi git. Fakat bir ilginçlik var, her karede bir garip rüzgar var, buzlarda zaten kaygan, bazen sağa gideyim derken yukarı gidiyorsun ya da ayağın kayıp düşüyorsun hiç bir yere gidemiyorsun. Herhangi bir kare üstündeyken, ne yöne gidersen ne kadar ihtimalle ne sonuç alacağın belli ama işte şans işi en doğru yolda bile ilerlesen rüzgar olmadık yerde deliklere düşürebilir.

Önce gerekli kütüphaneleri yükleyelim ve oyunu gym kütüphanesini kullanarak simüle edelim.

import gym
import numpy as np
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
import pandas as pd
import random
import matplotlib.pyplot as plt
%matplotlib inline
# Oyunu başlat ve görselleştir
env = gym.make('FrozenLake-v0')
env.render()

Yukarıdaki çıktılarda:

  • S = Başlangıç,
  • F = Buzlu (güvenli) kareler,
  • H = Delikler,
  • G = Hedef

Oyunda toplam 16 durum (state, tahtadaki kareler sağa ve satır satır sırayla numaralandığında 0-15 arasındaki numaralar) ve her bir durumda alınacak bilecek 0-3 arasında numaralandırılmış aksiyonlar (action) var.

  • 0: Sol
  • 1: Aşağı
  • 2: Sağ
  • 3: Yukarı

Ayrıca, oyunun içerisinde her bir durumda rüzgarın bizi hangi yöne götüreceğinin olasıklarını da görebiliriz.

# 0 numaralı durumun (state) aksiyonları ve olası sonuçları 
print(env.P[0])
print('---------')
# 4 numaralı durumun (state) aksiyonları ve olası sonuçları 
print(env.P[4])

Örneğin bu oyunda ilk adımda aşağıya gitmeyi deneyelim, 5 kere.

for i in range(5):
    env.reset()
    env.step(1)
    env.render()

Siz çalıştırdığınızda sonuçlar muhtemelen yukarıdakinden farklı olacaktır, fakat görüyoruz ki her zaman istediğimiz yönde hareket edemiyoruz. Her bir karede rüzgarın bizi atabileceği yönlere göre olabildiğince iyi seçimler yapmamız, nasıl yapacağız?

Soruyu çözmeye başlamadan önce yapabileceğimiz en akılsız şeyi yapıp sonuçları görelim, yani her köşede rastgele sağ, sol, yukarı, aşağı bir yön seçelim bakalım öyle oyunların ne kadarında şansa kazanabiliyoruz.

env = gym.make('FrozenLake-v0')
w = 0
n_games = 100000
max_steps = 200
for i_episode in range(n_games):
    observation = env.reset()
    for t in range(max_steps):
        # Choose random action from action space
        action = env.action_space.sample()
        # The step property returns the resulting state, reward, game finished flag and other extra information
        observation, reward, done, info = env.step(action)
        if done:
            # If the final state is 15 we win
            if observation == 15:
                w += 1
            break
env.close()
print('Random Strategy Win percentage: ', w*100/n_games)

1.4 civarında bir yüzdeyle kazanıyor serseri mayın gibi sağa sola gitmeye çalışmak.

Bu oyunda kullanacağımız stratejileri Q-tablosu denilen bir matriste temsil edebiliriz. Bu tabloda satırlar durumları (states), sütünlar da aksiyonları (action) temsil ediyor.

Bu tabloda önce her yeri $0$’la doldurup, sonra her bir durumda yapmak istediğimiz aksiyonun olduğu yere 1 yazarsak stratejimizi tabloda ifade etmiş oluruz.

env = gym.make('FrozenLake-v0')
# Durum ve aksiyon sayılarını oyundan alarak 0'la dolu Q matrisini oluştur
Q = np.zeros([env.observation_space.n,env.action_space.n])
env.render()
# Gri karelerde gitmek istediğin yöne denk gelen yerlere 1 yaz
Q[0,1] = 1 # 0 numaralı karede, aşağı  
Q[1,0] = 1 # 1 numaralı karede, sola 
Q[2,1] = 1 # 2 numaralı karede, aşağı
Q[3,0] = 1 # 3 numaralı karede, sola
Q[4,1] = 1 # 4 numaralı karede, aşağı 
Q[6,1] = 1 # 6 numaralı karede, aşağı 
Q[8,2] = 1 # 8 numaralı karede, sağa
Q[9,2] = 1 # 9 numaralı karede, sağa
Q[10,0] = 1 # 10 numaralı karede, aşağı
Q[13,2] = 1 # 13 numaralı karede, sağa
Q[14,2] = 1 # 14 numaralı karede, sağa

Şimdi bu stratejiyle oynayınca ne kadar kazanıyoruz bakalım:

env = gym.make('FrozenLake-v0')

w = 0
n_games = 100000
max_steps = 200
for episode in range(n_games):
    state = env.reset()
    done = False
    for step in range(max_steps):
        
        # Bulunduğun karede En yüksek Q-değerli aksiyonu seç
        action = np.argmax(Q[state,:])
        new_state, reward, done, info = env.step(action)
        if done:
        # Eğer 15 numaralı kareye gelmişse kazanma sayısını 1 arttır
            if new_state==15:
                w += 1
            break
        state = new_state
env.close()
print('Pre-determined Strategy Win percentage: ', w*100/n_games)

Pekiştirmeli öğrenmenin temel çerçevesi, ortam (environment) ile etkileşen bir unsurun (agent) ortam aldığı ödülle (reward) her bir durumda en iyi seçeneği bulmaya çalışmasıdır. Bunu aşağıdaki şemayla gösterebiliriz.

Donmuş göl oyununda hedef kareye gelmenin ödülü $1$, diğer bütün karelerin ödülü $0$. Bunu bir strateji oluşturmak için doğrudan kullanamayız, çünkü bu ödül fonksiyonu bize hamlelerin uzun vadeli ödül beklentisinin ne olduğunu söylemiyor. Hedefe gidecek bir strateji oluşturmak için (buna genelde politika, policy, denir), Q-tablosunu her bir durumdaki her bir aksiyonun uzun vadeli ödül beklentisini hesaplayarak doldurmaya çalışabiliriz:

  • $0$’larla dolu bir Q-tablosuyla başla
  • Oyunu Q-tablosundaki en yüksek değerli aksiyonu seçerek oyna, ama ara sıra da rastgele bir kare seç ki, farklı yollar da denemiş ol
  • $s$ durumunda $a$ aksiyonunu alarak $s+1$ durumuna geçince 𝑄[𝑠,𝑎] değerini 𝑄[𝑠+1,] satırındaki en yüksek değeri kullanarak güncelle

Bu algoritma kısaca, başlangıçta rastgele denemeler yapıp, hedefe ulaştıkça, hedef yakınındaki durumlara yüksek puan vermeye başlayacak, denedikçe denedikçe en iyi stratejiyi veren Q-tablosuna yaklaşmaya başlayacak.

env = gym.make('FrozenLake-v0')
# Initialize Q-table with all zeros
Q = np.zeros([env.observation_space.n,env.action_space.n])
# Set learning parameters
lr = .85
gamma = .95
num_episodes = 2000

for i in range(num_episodes):
    #Reset environment and get first new observation
    s = env.reset()
    d = False
    j = 0
    #The Q-Table learning algorithm
    while j < 99:
        j+=1
        #Choose an action by greedily (with noise) picking from Q table
        a = np.argmax(Q[s,:] + np.random.randn(1,env.action_space.n)*(1./(i+1)))
        #Get new state and reward from environment
        s1,r,d,_ = env.step(a)
        #Update Q-Table with new knowledge
        Q[s,a] = Q[s,a] + lr*(r + gamma*np.max(Q[s1,:]) - Q[s,a])
        s = s1
        
        if d == True:
            break

Elde ettiğimiz Q-tablosunu kullanarak oynadığımızda %58 civarında başarılı olduğumuzu göreceksiniz.

# Now evaluate the resulting strategy
w = 0
n_games = 1000
max_steps = 200
for episode in range(n_games):
    state = env.reset()
    done = False
    for step in range(max_steps):
        
        # Take the action (index) that have the maximum expected future reward given that state
        action = np.argmax(Q[state,:])
        new_state, reward, done, info = env.step(action)
        if done:
            if new_state==15:
                w += 1
            break
        state = new_state
env.close()
print('Iterative Q-table Strategy Win percentage: ', w*100/n_games)

Bu yöntem iyi, güzel ama bir kusuru var; eğer durum ve aksiyon uzaylarımız büyük olsaydı, çok büyük bir matrisi uzun uzun denemeler yapa yapa hesaplamak zorunda kalacaktık. Bu pratikte, önemli pek çok problemde Q-tablosu hesaplamayı imkansız hale getiriyor. Tam bu noktada derin öğrenme resme girip bizi kurtarıyor: Q-tablosunu her bir durum, aksiyon ikilisi için bir uzun vadeli ödül beklentisi hesaplayan $Q(s,a)$ şeklinde bir fonksiyon olarak görelim. Daha sonra bu fonksiyona yapay sinir ağlarıla yaklaşmaya çalışalım. Bu örnekte kayıp fonksiyonu olarak hata farkı karelerini kullanacağız, ve geri-yayılımla (back-propagation) optimizasyon yapacağız.

#These lines establish the feed-forward part of the network used to choose actions
inputs1 = tf.placeholder(shape=[1,16],dtype=tf.float32)
W = tf.Variable(tf.random_uniform([16,4],0,0.01))
Qout = tf.matmul(inputs1,W)
predict = tf.argmax(Qout,1)

#Below we obtain the loss by taking the sum of squares difference between the target and prediction Q values.
nextQ = tf.placeholder(shape=[1,4],dtype=tf.float32)
loss = tf.reduce_sum(tf.square(nextQ - Qout))
trainer = tf.train.GradientDescentOptimizer(learning_rate=0.1)
updateModel = trainer.minimize(loss)
init = tf.initialize_all_variables()

# Set learning parameters
y = .99
e = 0.1
num_episodes = 2000
#create lists to contain total rewards and steps per episode
jList = []
rList = []
with tf.Session() as sess:
    sess.run(init)
    for i in range(num_episodes):
        #Reset environment and get first new observation
        s = env.reset()
        rAll = 0
        d = False
        j = 0
        #The Q-Network
        while j < 99:
            j+=1
            # Choose an action by greedily (with e chance of random action) from the Q-network
            a,allQ = sess.run([predict,Qout],feed_dict={inputs1:np.identity(16)[s:s+1]})
            if np.random.rand(1) < e:
                a[0] = env.action_space.sample()
            #Get new state and reward from environment
            s1,r,d,_ = env.step(a[0])
            #Obtain the Q' values by feeding the new state through our network
            Q1 = sess.run(Qout,feed_dict={inputs1:np.identity(16)[s1:s1+1]})
            #Obtain maxQ' and set our target value for chosen action.
            maxQ1 = np.max(Q1)
            targetQ = allQ
            targetQ[0,a[0]] = r + y*maxQ1
            #Train our network using target and predicted Q values
            _,W1 = sess.run([updateModel,W],feed_dict={inputs1:np.identity(16)[s:s+1],nextQ:targetQ})
            rAll += r
            s = s1
            if d == True:
                #Reduce chance of random action as we train the model.
                e = 1./((i/50) + 10)
                break
        jList.append(j)
        rList.append(rAll)
print("Percent of succesful episodes: " + str(sum(rList)/num_episodes) + "%")

Bu derin pekiştirmeli öğrenme modeli Q-tablosu yöntemi kadar iyi çalışmadı, ama bunun temel sebebi Q-tablosunun çok küçük olması. Derin Q-öğrenme (Deep Q-learning) denilen bu yöntemle pek çok ilginç soru çözmek mümkün.

İlk yorum yapan olun

Bir yanıt bırakın

E-posta hesabınız yayımlanmayacak.


*