Ana içeriğe geç

Python'da SARSA Pekiştirmeli Öğrenme Algoritması: Kapsamlı Rehber

On-policy bir pekiştirmeli öğrenme algoritması olan SARSA'yı öğrenin. Güncelleme kuralını, hiperparametrelerini ve Q-learning'den farklarını uygulamalı Python örnekleri ve implementasyonu ile anlayın.
· 15 dk. oku

Yapay Zeka ile Keşfedin

Pekiştirmeli öğrenme (RL), güçlü bir makine öğrenimi paradigmasıdır. RL'de, genellikle ajan olarak adlandırılan yazılım, insan müdahalesi olmaksızın deneme-yanılma yoluyla ortamlarla etkileşime geçmeyi öğrenerek karmaşık problemleri çözer. RL algoritmaları arasında, SARSA, verimli on-policy (politika-üstü) doğasıyla öne çıkar.

SARSA, State-Action-Reward-State-Action (Durum-Eylem-Ödül-Durum-Eylem) ifadesinin kısaltmasıdır ve ajanın sorunları çözmek için izlediği döngüyü temsil eder. Bu döngü, ajanın geçmiş hatalarından öğrenmesini ve ara sıra yeni şeyler denemeye cesaret etmesini sağlar. Bu davranış, algoritmayı belirli problem türlerinde özellikle etkili kılar ve onu Q-learning gibi off-policy algoritmalardan ayrıştırır.

Bu eğitimde, SARSA'nın nasıl çalıştığını ve Python'da nasıl uygulayabileceğinizi öğreneceksiniz. Anlamınızı somutlaştırmak için klasik Taksi Yolculuğu problemini baştan sona kullanacağız. Ayrıca SARSA'nın avantajlarını, sınırlamalarını ve gerçek dünyadaki kullanım alanlarını da tartışacağız.

SARSA nedir? Kısa Cevap

SARSA, State-Action-Reward-State-Action'ın kısaltması olup bir öğrenme sürecindeki olaylar dizisini açıklar. Farklı senaryolarda iyi kararlar vermesi gereken bilgisayar programları (ajanlar) için etkili bir öğrenme yöntemidir.

SARSA'nın ardındaki temel fikir deneme-yanılmadır. Ajan, bir durumda bir eylemde bulunur, sonucu gözlemler ve sonuca (iyi ya da kötü) göre stratejisini ayarlar. Bu süreç defalarca tekrarlanır ve zaman içinde ajanın kararlarında iyileşmelere yol açar.

SARSA'yı RL algoritmaları arasında özel kılan nokta, ajanın yeni şeyler denerken yaptığı gerçek seçimlerden de öğrenmesidir. Bu yaklaşım, öğrenme yolculuğunun en az nihai sonuç kadar önemli olduğu durumlarda özellikle kullanışlıdır.

Bu, A'dan B'ye en kısa yolu, yardımcı denge tekerlekleriyle bulmaya çalışmak yerine, düşe kalka gerçekten bisiklete binerek öğrenen bir robota benzer.

Eğitim İçin Ortamınızı Hazırlama

Bu eğitimde, Numpy ve Gymnasium pekiştirmeli öğrenme kütüphanesini yoğun biçimde kullanacağız. Numpy, SARSA algoritmasını yazmamıza yardımcı olurken, Gymnasium çalışmamızı test etmek için yerleşik ortamlar sağlar.

Bunları yeni bir sanal ortamda şu komutlarla kuralım:

$ conda create -n sarsa python=3.9 -y
$ conda activate sarsa
$ pip install "gymnasium[atari]" numpy matplotlib
$ pip install autorom[accept-rom-license]  # Downloading Gym env data files
$ AutoROM --accept-license  # Accepting the license for data files
$ pip install ipykernel  # Install Jupyter kernel manager
$ ipython kernel install --user --name=sarsa  # Add the new Conda env to Jupyter

Devam etmeden önce, Pekiştirmeli Öğrenmeye Giriş makalemizi okumanızı özellikle öneririm. Ajanlar, ortamlar, keşif ve sömürü ikilemi ile Q-learning gibi RF'nin temel fikirlerini kapsar.

Taxi-V3 Ortamının Açıklaması

Eğitim boyunca, Gymnasium kütüphanesinin sağladığı klasik bir pekiştirmeli öğrenme problemi olan Taxi-v3 ortamını kullanacağız. Bu ortam, yolcuları alıp bırakmak için 5x5 bir ızgara dünyasında gezinmek zorunda olan bir taksi şoförünü simüle eder.

Ortamı yüklemek için Gymnasium'un .make() metodunu rgb_array çizim modu ile kullanıyoruz (ileride ortamı görselleştirmek için):

import gymnasium as gym
env = gym.make('Taxi-v3', render_mode='rgb_array')

Ortam, kırmızı (R), yeşil (G), sarı (Y) ve mavi (B) olmak üzere dört belirlenmiş konuma sahip 5x5 bir ızgaradır. Taksi rastgele bir karede başlar ve yolcuyu renkli konumlardan birinden alıp başka birine bırakmalıdır. 

Taksi Kuzey, Güney, Doğu veya Batı'ya hareket edebilir ve ayrıca yolcu almayı veya bırakmayı deneyebilir.

Ortamın başlangıç durumunu Matplotlib ile şöyle görselleştirebiliriz:

import matplotlib.pyplot as plt
# Reset the environment to get an initial state
# Each time we reset the environment, we get a new random state
initial_state, _ = env.reset()
# Render the initial state
img = env.render()
# Create a figure and display the environment
fig, ax = plt.subplots(figsize=(8, 8))
ax.imshow(img)
# Remove axis ticks
ax.set_xticks([])
ax.set_yticks([]);

SARSA kullanılarak çözülecek taksi yolculuğu pekiştirmeli öğrenme probleminin örnek bir başlangıç durumu

Önce, başlangıç durumu elde etmek için ortamı sıfırlarız. Ardından, bu başlangıç durumunu render fonksiyonu ile görüntü olarak alırız; bu bir Numpy görüntü dizisidir. Matplotlib'in imshow() fonksiyonu bu diziyi alır ve eksen işaretleri olmadan temiz bir görselleştirme üretir.

Taksi-v3 ızgara dünyasının düzenini, taksinin konumunu, bariyerleri, yolcu konumlarını ve hedefi bir an için inceleyin.

Ajan (taksi), başarılı bir bırakma için +20 puan alır. Yasal olmayan alma veya bırakma işlemi -10 ödülle sonuçlanır. Bu arada, her zaman adımı, taksiyi görevi hızlıca tamamlamaya teşvik etmek için -1 ödül üretir.

Taxi-v3 ortamındaki tek bir bölüm, başarılı bir bırakma gerçekleştiğinde veya maksimum zaman adımı sayısına ulaşıldığında sonlanır.

# The number of states and actions
n_states = env.observation_space.n
n_actions = env.action_space.n
print(n_states)
print(n_actions)
500
6

Ortamın durum uzayında 500 durum vardır. Her durum şu şekilde temsil edilir:

  • Taksinin satırı (0–4)
  • Taksinin sütunu (0–4)
  • Yolcu konumu (R, G, Y, B için 0–3 veya taksi içinde ise 4)
  • Hedef konum (R, G, Y, B için 0–3)
  • Toplam durum sayısı =  5 (satır)×5 (sütun)×5 (yolcu konumu)×4 (hedef konumu)=500.

Eylem kodları şunlardır:

  • 0: Güneye hareket
  • 1: Kuzeye hareket
  • 2: Doğuya hareket
  • 3: Batıya hareket
  • 4: Yolcu alma
  • 5: Yolcu bırakma

Amaç, ajanın toplam ödülünü en üst düzeye çıkarmak için yolcuları verimli şekilde alıp bırakacağı optimal bir politika öğrenmesidir.

SARSA Etkileşim Döngüsü

Tam algoritmayı yazmadan önce, ortamımızı kullanarak Durum-Eylem-Ödül-Durum-Eylem döngüsünün nasıl çalıştığını görelim.

Önce, etkileşimin kaç bölüm sürmesini istediğimizi tanımlarız. Tek bir bölüm, başlangıç durumundan yolcunun başarıyla bırakılmasına veya maksimum zaman adımı sayısına ulaşılmasına kadar taksi görevini baştan sona temsil eder. Her bölüm, ajanın deneyimlerinden öğrenmesine ve zamanla stratejisini geliştirmesine olanak tanır.

n_episodes = 5000

Şimdi, etkileşim döngüsünü yazalım:

for episode in range(n_episodes):
   state, _ = env.reset()
   done = False
   total_reward = 0
   steps = 0
   while not done:
       # Choose a random action
       action = env.action_space.sample()
       # Take the action and observe the result
       next_state, reward, terminated, truncated, _ = env.step(action)
       done = terminated or truncated
       # Update total reward and step count
       total_reward += reward
       steps += 1
       # Move to the next state
       state = next_state
   if episode % 1000 == 0:
       print(f"Episode {episode}, Total Reward: {total_reward}, Steps: {steps}")
Episode 0, Total Reward: -812, Steps: 200
Episode 1000, Total Reward: -830, Steps: 200
Episode 2000, Total Reward: -902, Steps: 200
Episode 3000, Total Reward: -522, Steps: 129
Episode 4000, Total Reward: -767, Steps: 200

Yukarıdaki kod, herhangi bir öğrenme algoritması uygulanmadan temel SARSA etkileşim döngüsünü gösterir:

  1. Her bölümün başında ortamı sıfırla — (S), env.reset().
  2. Rastgele bir eylem yap — (A), env.step(action). Bu sürüm, etkileşim döngüsünü göstermek için rastgele eylemler kullanır.
  3. Ödülü (R) ve bir sonraki durumu (S_1) al, next_state, reward, ... = env.take(action).
  4. Yeni durumda bir eylem (A_1) yap.

Yukarıdaki etkileşim döngüsünde taksi, zaman adımları bitene kadar ortamda her yöne gidip rastgele eylemler yapmaya devam eder.

SARSA Etkileşim Döngüsünü Canlandırma

Devam etmeden önce, etkileşim döngülerimizi canlandıracak bir özellik uygulayalım. Bu, taksinin ortamla etkileşimini görsel olarak görmemizi sağlar.

Animasyon oluşturma basit bir süreç olacak:

  • Her zaman adımında, ortamın durumunu env.render() fonksiyonunu kullanarak bir görüntü dizisine yakalarız.
  • Görüntü dizilerini ayrı bir değişkende toplarız.
  • moviepy adlı bir kütüphaneyi kullanarak tüm görüntü dizilerini birleştirip tek bir GIF oluştururuz.

Kodu değiştirelim:

env = gym.make("Taxi-v3", render_mode="rgb_array")
n_episodes = 1
frames = []  # for animation
for episode in range(n_episodes):
   # Reset the environment
   state, _ = env.reset()
   # Capture the state as an image
   img = env.render()
   frames.append(img)
   done = False
   total_reward = 0
   steps = 0
   while not done:
       # Choose a random action
       action = env.action_space.sample()
       # Take the action and observe the result
       next_state, reward, terminated, truncated, _ = env.step(action)
       done = terminated or truncated
      
       # Capture the next state as an image
       img = env.render()
       frames.append(img)
       # Update total reward and step count
       total_reward += reward
       steps += 1
       # Move to the next state
       state = next_state

Bu sürümde, ortam durumlarını görüntü olarak oluşturmak çok zaman aldığından bölüm sayısını bire ayarladık. Ayrıca görüntü dizilerini depolamak için boş bir liste oluşturduk. Sonrasında, etkileşim döngüsü eskisi gibi devam eder. Tek fark, ortamı görüntü olarak işlediğimiz ve bunu frames listesine eklediğimiz iki ekstra satırdır.

Artık frames içinde 201 görüntü olmalı (ek bir durum için bir görüntü fazladan):

>>> len(frames)
201

Bu görüntüleri moviepy kütüphanesini kullanarak bir GIF'e dönüştürelim:

from moviepy.editor import ImageSequenceClip  # pip install moviepy
def create_gif(frames: list, filename, fps=5):
   """
   Creates a GIF animation from a list of RGBA NumPy arrays.
   Args:
       frames: A list of RGBA NumPy arrays representing the animation frames.
       filename: The output filename for the GIF animation.
       fps: The frames per second of the animation (default: 10).
   """
   clip = ImageSequenceClip(frames, fps=fps)
   clip.write_gif(filename, fps=fps)
# Example usage
create_gif(frames, "animation.gif", fps=25)  # saves the GIF locally

create_gif() fonksiyonu bir kare listesini alır ve moviepy'nin ImageSequenceClip sınıfını kullanarak bunları bir GIF'e dönüştürür. Önemli bir parametre, saniyedeki kare sayısını kontrol eden fps'dir; bu GIF'in süresini belirler. Saniyedeki kare sayısı arttıkça GIF kısalır.

Sonuçta, tek bir bölümün karelerini 25 FPS ile bir GIF'e dönüştürüyoruz. İşte sonuç:

Durum-Eylem-Ödül-Eylem-Durum döngüsünü temsil eden, taksi ortamında bir ajanın (taksi) rastgele etkileşimlerini gösteren bir GIF

Gördüğünüz gibi taksi ne yaptığını bilmiyor ve yolcuya yaklaşamıyor bile. Hadi, gezinme sistemine SARSA ekleyerek ona biraz akıl verelim.

Adım Adım Python ile SARSA Uygulaması

Her adımı net hatırlamanız için SARSA kodunu sıfırdan inşa edeceğiz.

1. Gymnasium ortamını kurma:

import gymnasium as gym
import numpy as np
import matplotlib.pyplot as plt
# Create the Taxi environment
env = gym.make("Taxi-v3", render_mode="rgb_array")

2. Bir Q-tablosu başlatma

# Initialize Q-table
n_states = env.observation_space.n
n_actions = env.action_space.n
Q_table = np.zeros((n_states, n_actions))

Bu adımda yeni bir veri yapısı — Q-tablosunu tanıtıyoruz. Boyutları (durum sayısı) x (eylem sayısı) şeklindedir. Tablo, ajanımız olan taksi şoförüne şöyle görünürdü:

Q-learning ve SARSA pekiştirmeli öğrenme algoritmaları için kilit önemde olan Q-tablosunun görsel temsili.

Başlangıçta Q-tablosu sıfırlarla doldurulacaktır:

>>> Q_table.shape
(500, 6)

Daha sonra, SARSA rehberliğinde ajan ortamla etkileşime başladıkça Q-tablosunu Q-değerleriyle güncelleyecektir. Q-değerleri, mevcut duruma göre hangi eylemin en iyi olduğuna dair ajana bilgi veren puanlardır.

3. SARSA hiperparametrelerini tanımlama

Q-tablosunu başlattıktan sonra, SARSA'nın hiperparametrelerini yaygın değerlere ayarlarız (bunlar hakkında daha sonra):

# SARSA parameters
alpha = 0.1  # Learning rate
gamma = 0.99  # Discount factor
epsilon = 0.1  # Exploration rate for epsilon-greedy policy
n_episodes = 20000

4. Performans metrikleri için depolama tanımlama

Daha sonra her bölüm için toplam ödülü ve zaman adımı sayısını depolamak üzere iki liste tanımlarız. Ajanın hedefi, mümkün olduğunca kısa sürede olabildiğince fazla ödül toplamaktır:

# Lists to store performance metrics
episode_rewards = []
episode_lengths = []

5. Eylem almak için epsilon-greedy politika

Önceki bölümde, ajanımız yönlendirilmemişti — rastgele eylemler yapıyordu. Sürücüye bir Epsilon-Greedy stratejisi vererek bunu değiştirmek istiyoruz:

def epsilon_greedy(state, epsilon):
   if np.random.random() < epsilon:
       # Take random action - explore
       return env.action_space.sample()
   else:
       # Take action with the highest Q-value - exploit
       return np.argmax(Q_table[state])

Bu strateji, keşif ve sömürü arasındaki kritik dengeyi kontrol etmeyi amaçlar. epsilon olasılığıyla ajan, rastgele bir eylem yaparak ortamı keşfeder; 1-epsilon olasılığıyla ise Q-tablosundan en yüksek Q-değerine sahip eylemi seçerek mevcut bilgisini kullanır. Bu yaklaşım, ajanın yeni ve potansiyel olarak daha iyi stratejiler keşfetmesini sağlarken aynı zamanda öğrendiklerini kullanmasına olanak tanır.

6. SARSA eğitim döngüsünü yazma

Son olarak SARSA eğitim döngüsünü yazarız. Döngünün başlangıcı bize zaten tanıdık. Tek fark, mevcut durumda hangi eylemi yapacağımızı söylemesi için epsilon_greedy() fonksiyonunu kullanmamızdır:

# SARSA training loop
for episode in range(n_episodes):
   state, _ = env.reset()
   action = epsilon_greedy(state, epsilon)
   done = False
   total_reward = 0
   steps = 0
   ...

Ardından, bir sonlanma durumuna ulaşana kadar etkileşim döngüsünü çalıştırmamıza olanak tanıyan while döngüsünü başlatırız:

# SARSA training loop
for episode in range(n_episodes):
   ...
   while not done:
       next_state, reward, terminated, truncated, _ = env.step(action)
       done = terminated or truncated
       next_action = epsilon_greedy(next_state, epsilon)

while döngüsünün içinde, epsilon_greedy() tarafından döndürülen eylemi yapar, bir sonraki durumu, eylemimizin ödülünü ve bölümün bitip bitmediğine dair bir mantıksal değeri alırız.

Sonra kritik kısma — SARSA'nın güncelleme kuralına — geliriz:

# SARSA training loop
for episode in range(n_episodes):
   ...
   while not done:
       next_state, reward, terminated, truncated, _ = env.step(action)
       done = terminated or truncated
       next_action = epsilon_greedy(next_state, epsilon)
       # SARSA update rule
       Q_table[state, action] += alpha * (
           reward + gamma * Q_table[next_state, next_action] - Q_table[state, action]
       )

Güncelleme kuralı şu formülden gelir:

SARSA'nın güncelleme kuralının formülü

[Kaynak]

Bu formülün sezgisini bir sonraki bölümde öğreneceğiz. Şimdilik, formülü, Q-tablosundaki Q-değerlerini SARSA kurallarına göre güncelleyen bir matematik sihri olarak düşünün.

Q güncellemesini yaptıktan sonra, ortaya çıkan duruma ve eyleme göre state ve action değişkenlerini değiştirir, alınan ödülü bölümün toplam ödülüne ekler ve zaman adımı sayısını artırırız.

# SARSA training loop
for episode in range(n_episodes):
   ...
   while not done:
       ...
       state = next_state
       action = next_action
       total_reward += reward
       steps += 1

while döngüsü, maksimum zaman adımına (taksi ortamı için 200) ulaşana kadar veya taksi yolcusunu doğru konumda başarıyla bırakana kadar devam eder.

Döngü sonlandığında, bölümün toplam ödülünü ve bölüm uzunluğunu kaydederiz. Ayrıca her 1000 bölümde ortalama bölüm ödülünü ve uzunluğunu yazdırırız:

# SARSA training loop
for episode in range(n_episodes):
   ...
   while not done:
       ...
   episode_rewards.append(total_reward)
   episode_lengths.append(steps)
   if episode % 1000 == 0:
       avg_reward = np.mean(episode_rewards[-1000:])
       avg_length = np.mean(episode_lengths[-1000:])
       print(f"Episode {episode}, Avg Reward: {avg_reward:.2f}, Avg Length: {avg_length:.2f}")

İşte çalıştırılmış tam etkileşim döngüsü:

# SARSA training loop
for episode in range(n_episodes):
   state, _ = env.reset()
   action = epsilon_greedy(state, epsilon)
   done = False
   total_reward = 0
   steps = 0
   while not done:
       next_state, reward, terminated, truncated, _ = env.step(action)
       done = terminated or truncated
       next_action = epsilon_greedy(next_state, epsilon)
       Q_table[state, action] += alpha * (
           reward + gamma * Q_table[next_state, next_action] - Q_table[state, action]
       )
       state = next_state
       action = next_action
       total_reward += reward
       steps += 1
   episode_rewards.append(total_reward)
   episode_lengths.append(steps)
   if episode % 2000 == 0:
       avg_reward = np.mean(episode_rewards[-1000:])
       avg_length = np.mean(episode_lengths[-1000:])
       print(f"Episode {episode}, Avg Reward: {avg_reward:.2f}, Avg Length: {avg_length:.2f}")
Episode 0, Avg Reward: -551.00, Avg Length: 185.00
Episode 2000, Avg Reward: -4.37, Avg Length: 19.47
Episode 4000, Avg Reward: 1.98, Avg Length: 15.09
Episode 6000, Avg Reward: 2.29, Avg Length: 14.79
Episode 8000, Avg Reward: 2.06, Avg Length: 14.80
Episode 10000, Avg Reward: 2.16, Avg Length: 14.78
Episode 12000, Avg Reward: 2.06, Avg Length: 14.89
Episode 14000, Avg Reward: 2.33, Avg Length: 14.81
Episode 16000, Avg Reward: 2.36, Avg Length: 14.66
Episode 18000, Avg Reward: 2.53, Avg Length: 14.72

Çıktıdan da gördüğünüz gibi, çalıştırılan bölüm sayısı arttıkça ortalama ödül ve bölüm başına zaman adımı sayısı dramatik biçimde azaldı.

Bunu, episode_rewards ve episode_lengths dizilerini çizerek görsel olarak da görebiliriz:

# Plot the learning curve
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(episode_rewards)
plt.title("Episode Rewards")
plt.xlabel("Episode")
plt.ylabel("Total Reward")
plt.subplot(1, 2, 2)
plt.plot(episode_lengths)
plt.title("Episode Lengths")
plt.xlabel("Episode")
plt.ylabel("Number of Steps")
plt.tight_layout()
plt.show()

SARSA için iki performans metriğini gösteren iki grafik: toplam bölüm ödülü ve bölüm uzunlukları

Sol grafik, her bölümde elde edilen toplam ödülü gösterir. Şunları gözlemleyebiliriz:

  • Başlangıçta ödüller düşüktür ve oldukça değişkendir; bu durum, ajanın keşif yaptığını ve öğrendiğini gösterir.
  • Zamanla, ödüllerde yukarı yönlü bir eğilim vardır; bu da ajanın politikasını iyileştirdiğini gösterir.
  • Sonlara doğru ödüller daha yüksek bir seviyede istikrar kazanıyor gibi görünür; bu da ajanın makul derecede iyi bir politika öğrendiğine işaret eder.

Sağdaki grafik, her bölümde atılan adım sayısını gösterir. Şunları görebiliriz:

  • Başlangıçta bölümler daha uzundur; çünkü ajan optimal olmayan eylemler yapmaktadır.
  • Öğrenme ilerledikçe bölüm uzunluklarında genel bir azalma eğilimi vardır.
  • Bölüm uzunlukları nihayetinde istikrar kazanır; bu da ajanın görevi daha verimli tamamlamayı öğrendiğini gösterir.

Kodu Fonksiyonlara Düzenleme

Kısa sürede epey yol katettik. Şimdi bir adım geri çekilip yazdığımız her şeyi düzenleyelim. SARSA'yı uygulamak için attığımız her adım için fonksiyonlar oluşturacağız.

Önce, bir ortam oluşturan fonksiyon:

import gymnasium as gym
import numpy as np
import matplotlib.pyplot as plt
from moviepy.editor import ImageSequenceClip
def create_environment(env_name="Taxi-v3", render_mode="rgb_array"):
   """Create and return a Gymnasium environment."""
   return gym.make(env_name, render_mode=render_mode)

Sonra, bir ortam verildiğinde bir Q-tablosu başlatan fonksiyon:

def initialize_q_table(env):
   """Initialize and return a Q-table for the given environment."""
   n_states = env.observation_space.n
   n_actions = env.action_space.n
   return np.zeros((n_states, n_actions))

Bir ortamı, Q-tablosunu, mevcut durumu ve epsiloni kabul eden Epsilon-greedy stratejisi:

def epsilon_greedy(env, Q_table, state, epsilon=0.1):
   """Epsilon-greedy action selection."""
   if np.random.random() < epsilon:
       return env.action_space.sample()
   else:
       return np.argmax(Q_table[state])

SARSA'nın güncelleme kuralı; bir Q-tablosu, mevcut durum, o durumdaki alınan eylem, eylemin ödülü, sonraki durum ve eylem gerektirir:

def sarsa_update(Q_table, state, action, reward, next_state, next_action, alpha, gamma):
   """Perform SARSA update on Q-table."""
   Q_table[state, action] += alpha * (
       reward + gamma * Q_table[next_state, next_action] - Q_table[state, action]
   )

Son olarak, bir ortam, çalıştırılacak bölüm sayısı, alpha, gamma ve epsilon parametreleri gerektiren SARSA ile ajanı eğitecek büyük bir fonksiyon:

def train_sarsa(env, n_episodes=20000, alpha=0.1, gamma=0.99, epsilon=0.1):
   """Train the agent using SARSA algorithm."""
   Q_table = initialize_q_table(env)
   episode_rewards = []
   episode_lengths = []
   for episode in range(n_episodes):
       state, _ = env.reset()
       action = epsilon_greedy(env, Q_table, state, epsilon)
       done = False
       total_reward = 0
       steps = 0
       while not done:
           next_state, reward, terminated, truncated, _ = env.step(action)
           done = terminated or truncated
           next_action = epsilon_greedy(env, Q_table, next_state, epsilon)
           sarsa_update(
               Q_table, state, action, reward, next_state, next_action, alpha, gamma
           )
           state = next_state
           action = next_action
           total_reward += reward
           steps += 1
       episode_rewards.append(total_reward)
       episode_lengths.append(steps)
   return Q_table, episode_rewards, episode_lengths

Ayrıca, performans metriklerini çizecek bir fonksiyon:

def plot_learning_curve(episode_rewards, episode_lengths):
   """Plot the learning curve."""
   plt.figure(figsize=(12, 5))
   plt.subplot(1, 2, 1)
   plt.plot(episode_rewards)
   plt.title("Episode Rewards")
   plt.xlabel("Episode")
   plt.ylabel("Total Reward")
   plt.subplot(1, 2, 2)
   plt.plot(episode_lengths)
   plt.title("Episode Lengths")
   plt.xlabel("Episode")
   plt.ylabel("Number of Steps")
   plt.tight_layout()
   plt.show()

Önceki create_gif() fonksiyonumuz:

def create_gif(frames, filename, fps=5):
   """Creates a GIF animation from a list of frames."""
   clip = ImageSequenceClip(frames, fps=fps)
   clip.write_gif(filename, fps=fps)

Ve tamamlanmış, öğrenilmiş bir Q-tablosu kullanarak (animasyon amaçlı) görselleştirme ile tek bir bölümü çalıştıracak bir başka fonksiyon:

def run_episode(env, Q_table, epsilon=0):
   """Run a single episode using the learned Q-table."""
   state, _ = env.reset()
   done = False
   total_reward = 0
   frames = [env.render()]
   while not done:
       action = epsilon_greedy(env, Q_table, state, epsilon)
       next_state, reward, terminated, truncated, _ = env.step(action)
       done = terminated or truncated
       frames.append(env.render())
       total_reward += reward
       state = next_state
   return frames, total_reward

Şimdi, her şeyi çalıştıralım:

if __name__ == "__main__":
   env = create_environment()
  
   Q_table, episode_rewards, episode_lengths = train_sarsa(env, n_episodes=20000)
   plot_learning_curve(episode_rewards, episode_lengths)
  
   frames, total_reward = run_episode(env, Q_table)
   create_gif(frames, "images/sarsa_final_animation.gif", fps=1)

SARSA için iki performans metriğini gösteren iki grafik: toplam bölüm ödülü ve bölüm uzunlukları

Performans grafikleri iyi görünüyor. Şimdi oluşturulan GIF'e bakalım:

SARSA algoritmasıyla eğitilmiş bir pekiştirmeli öğrenme ajanı taksinin, yolcuyu başarıyla alıp bırakmasını gösteren GIF

Yaşasın! Taksi, yolcuyu mavi kareden doğru şekilde alıp sarı karede bırakıyor.

SARSA'nın uygulanmasına ilişkin tam düzenlenmiş kodun tamamını bir GitHub gist olarak yapıştırdım; istediğiniz zaman geri dönebilirsiniz.

SARSA'nın Güncelleme Kuralının Sezgisi

SARSA'nın kalbinde, ajanın deneyimlerinden nasıl öğrendiğini kontrol eden güncelleme kuralı yatar. Bu kuralı parçalarına ayıralım ve arkasındaki sezgiyi keşfedelim:

Q(s, a) = Q(s, a) + α [R + γ Q(s', a') - Q(s, a)]

Bileşenleri anlama

  • Q(s, a): ‘s’ durumunda ‘a’ eylemini yapmanın mevcut Q-değeri.
  • α (alfa): Yeni bilginin ne kadar hızlı dahil edileceğini kontrol eden öğrenme oranı.
  • R: Eylemden sonra alınan ödül.
  • γ (gama): Gelecekteki ödüllerin önemini belirleyen iskonto faktörü.
  • Q(s’, a’): Bir sonraki durum-eylem çiftinin tahmini Q-değeri.

Öğrenme süreci

SARSA'nın güncelleme kuralı, yeni deneyimlere dayanarak Q-değerlerini ayarlayarak ajanın ortamı anlama biçimini iyileştirmeyi amaçlar. İşleyişi şöyledir:

  1. Ajan bir eylem yapar ve ödülü ile bir sonraki durumu gözlemler.
  2. Mevcut Q-değeri ile, gözlemlenen ödül ve bir sonraki durum-eylem çiftinin değeri temel alınarak hesaplanan yeni Q-değeri tahmini arasındaki farkı hesaplar.
  3. Bu fark, öğrenme oranı ile ölçeklendirilerek Q-değerini güncellemekte kullanılır.

Zamansal fark hatası

[R + γ * Q(s’, a’) — Q(s, a)] terimi zamansal fark (TD) hatası olarak bilinir. Bunu bir şaşkınlık ölçüsü olarak düşünün:

  • Pozitif TD hatası: Sonuç beklenenden daha iyiydi.
  • Negatif TD hatası: Sonuç beklenenden daha kötüydü.
  • Sıfır TD hatası: Sonuç mevcut tahminle tam olarak uyuştu.

Bu hata, ajanın tahminlerini sürekli olarak iyileştirmesine yardımcı olur ve öğrenme sürecini optimal bir politikaya doğru yönlendirir.

Hiperparametrelerin rolü

1. Öğrenme oranı (α):

  • Öğrenme hızını kontrol eder.
  • Yüksek α: Daha hızlı öğrenme, ancak potansiyel olarak dengesiz.
  • Düşük α: Daha yavaş, daha istikrarlı öğrenme.

2. İskonto faktörü (γ):

  • Anlık ve gelecekteki ödüller arasındaki dengeyi kurar.
  • 1'e yakın γ: Ajan gelecekteki ödülleri anlık ödüller kadar değerli görür.
  • Düşük γ: Ajan daha çok anlık ödüllere odaklanır.

3. Keşif oranı (ε):

  • Güncelleme kuralının içinde değilse de epsilon-greedy politika için kritik önemdedir.
  • Keşif (yeni eylemler deneme) ve sömürü (bilinen iyi eylemleri kullanma) dengesini kurar.
  • Yüksek ε: Kısa vadeli performans pahasına daha fazla keşif ve potansiyel olarak daha iyi stratejiler keşfi.

Büyük resim

SARSA'nın güncelleme kuralı, ajanın durum-eylem değerlerine ilişkin tahminlerini sürekli ayarlayarak deneyimlerinden öğrenmesini sağlar. Her etkileşimle birlikte ajan, ortamı hakkında biraz daha bilgeleşir. Çok sayıda bölüm boyunca bu süreç, ortamda gezinmek için optimal bir politikanın geliştirilmesine yol açar.

Hiperparametreleri ayarlayarak öğrenme sürecinin çeşitli yönlerini kontrol edebilir, SARSA'yı farklı problem ve ortamlara uyarlayabiliriz. Bu esneklik, sezgisel güncelleme kuralı ile birleştiğinde SARSA'yı güçlü ve yaygın kullanılan bir pekiştirmeli öğrenme algoritması yapar.

SARSA ve Q-learning: Temel Farklar

SARSA ve Q-learning yaygın kullanılan pekiştirmeli öğrenme algoritmaları olsalar da aralarında bazı önemli farklar vardır. Bu farklar, her bir algoritmanın ne zaman ve nasıl kullanılacağını anlamak açısından kritiktir.

1. On-policy vs. off-policy

On-policy bir algoritma olan SARSA, keşif sırasında atılan adımlar dahil takip ettiği politikanın değerini öğrenir. Q-learning ise off-policy'dir; optimal politikanın değerini öğrenir — hatta o politikayı takip etmiyor olsa bile, bölümün sonunda optimal politikanın değerini öğrenir. Bu yüzden başta, öğrenme yolculuğunun sonucun kendisi kadar önemli olduğu durumlarda SARSA'nın kullanılmasının gerektiğini söylemiştik. Q-learning, öğrenme yolculuğuna o kadar önem vermez.

2. Güncelleme kuralı

  • SARSA: Q(s, a) = Q(s, a) + α * [R + γ * Q(s’, a’) — Q(s, a)]
  • Q-learning: Q(s, a) = Q(s, a) + α * [R + γ * max(Q(s’, a’)) — Q(s, a)]

Temel fark, SARSA'nın, gerçekten yapılan bir sonraki eylemin (a’) Q-değerini kullanması; Q-learning'in ise bir sonraki durumun maksimum Q-değerini (max(Q(s’, a’))) kullanmasıdır.

3. Keşif dikkate alma

Q-değerleri güncellenirken SARSA keşif politikasını göz ardı etmez ve bu nedenle daha muhafazakârdır. Buna karşılık Q-learning, ajanın gelecekte optimal eylemi yapacağını varsayar ve bu nedenle daha agresiftir.

4. Yakınsama

Her iki algoritma da sonunda optimal politikaya ulaşsa da Q-learning, özellikle deterministik ortamlarda sıklıkla daha hızlı öğrenebilir.

5. Güvenlik

Keşfin kötü sonuçlara yol açabileceği ortamlarda, SARSA genellikle daha güvenli politikalar öğrenir; çünkü gerçekten izlenen politikayı hesaba katar.

Klasik “uçurum boyunca yürüme” probleminde, SARSA tipik olarak uçurum kenarından uzak duran daha güvenli bir yol öğrenirken, Q-learning kenar boyunca ilerleyen daha riskli bir yol öğrenebilir.

6. İstikrar

Bazı rastlantısal ortamlarda, SARSA daha istikrarlı olabilir; çünkü gerçekten yapılan bir sonraki eylemi — bu eylem optimal olmayan bir keşif eylemi olsa bile — dikkate alır.

7. Hiperparametre duyarlılığı

Özellikle belirgin stokastisiteye sahip ortamlarda, Q-learning öğrenme oranı ve keşif oranı seçimlerine daha duyarlı olabilir.

8. Gerçek dünya uygulamaları

Keşfin maliyetli olduğu robotik veya diğer fiziksel sistemlerde ve daha güvenli politikalar uygulamak istediğimizde, on-policy algoritma SARSA'yı tercih edebiliriz. Simüle edilmiş dünyada, oyunlarda veya çok fazla geri bildirim alabildiğimiz, optimal politikayı hızlı ve nispeten güvenli biçimde bulmak istediğimiz ortamlarda ise Q-learning'i çok daha fazla sevebiliriz.

Dolayısıyla SARSA'yı daha güvenli bölge, Q-learning'i ise daha hızlı ve daha gözü kara yaklaşım olarak düşünebilirsiniz. Pratikte çoğu zaman spesifik probleme bağlıdır. Her iki algoritma da çok güçlüdür ve aralarındaki farkları bilmek her zaman anlamlıdır.

Sonuç

Bu kapsamlı rehberde, Taxi-v3 ortamını kullanarak SARSA algoritmasını; temel kavramları, uygulama detayları ve pratik kullanım alanlarıyla birlikte inceledik.

Öne çıkan noktalar şunlardır:

  1. SARSA'nın on-policy doğası ve güncelleme kuralının sezgisi
  2. Python uygulaması ve görselleştirme teknikleri
  3. Q-learning ile karşılaştırma: güçlü yönler ve kullanım senaryaları

SARSA'nın daha güvenli politikalar öğrenme becerisi, keşif maliyetlerinin yüksek olduğu gerçek dünya uygulamaları için onu uygun kılar; ancak bazı senaryolarda Q-learning gibi off-policy algoritmalara kıyasla daha yavaş yakınsayabilir.

Unutmayın, SARSA ve diğer algoritmalar arasındaki seçim, spesifik probleminize ve ortamınıza bağlıdır. Optimal sonuçlar için denemeler ve ince ayar kritik önemdedir.

Bu temelin üzerine daha karmaşık ortamları keşfederek, fonksiyon yaklaşımı ile SARSA'yı uygulayarak veya diğer pekiştirmeli öğrenme algoritmalarına dalarak inşa etmenizi öneririz.

Yol boyunca size yardımcı olacak ilgili bazı kaynaklar:

SARSA SSS

Pekiştirmeli öğrenmede SARSA algoritması nedir?

SARSA (State-Action-Reward-State-Action), ajanın keşif eylemleri de dahil olmak üzere gerçek deneyimlerine dayanarak Q-değerlerini güncelleyen on-policy bir pekiştirmeli öğrenme algoritmasıdır.

SARSA, Q-learning'den nasıl farklıdır?

SARSA on-policy'dir ve Q-değerlerini gerçekten yapılan bir sonraki eylemi kullanarak günceller; Q-learning ise off-policy'dir ve bir sonraki durumun maksimum Q-değerini kullanır; bu da farklı öğrenme davranışlarına yol açar.

SARSA'daki temel hiperparametreler nelerdir ve öğrenmeyi nasıl etkilerler?

Temel hiperparametreler öğrenme oranı (α), iskonto faktörü (γ) ve keşif oranıdır (ε). Sırasıyla öğrenme hızını, gelecekteki ödüllerin önemini ve keşif-sömürü dengesini kontrol ederler.

SARSA algoritmasını Python'da nasıl uygulayabilirim?

Makale, ortamı kurma, Q-tablosunu başlatma ve eğitim döngüsünü yazma dahil olmak üzere Python'da SARSA'yı uygulamak için adım adım bir rehber sunar.

SARSA'nın gerçek dünya senaryolarındaki pratik uygulamaları nelerdir?

SARSA, keşfin maliyetli olduğu robotik ve fiziksel sistemler gibi uygulamalar için uygundur; çünkü Q-learning gibi off-policy algoritmalara kıyasla daha güvenli politikalar öğrenme eğilimindedir.

Bexruz (Bex) Tuychiev

Yazar

Bexruz (Bex) Tuychiev

2 yılı aşkın deneyime sahip bir veri bilimi içerik üreticisiyim ve Medium’da en büyük takipçi kitlelerinden birine sahibim. Yapay zekâ ve makine öğrenimi üzerine, biraz da alaycı bir üslupla yazılmış, ayrıntılı makaleler kaleme almayı seviyorum; çünkü onları biraz daha az sıkıcı hale getirmek için bir şeyler yapmak gerekiyor. 130’un üzerinde makale ve bir de DataCamp kursu ürettim; bir diğeri de hazırlık aşamasında. İçeriklerim 5 milyondan fazla kişi tarafından görüldü; bunların 20 bini Medium ve LinkedIn’de takipçim oldu. 

Konular

İlgili