Salta al contenuto principale

07 - Reinforcement Learning: Q-Learning, DQN e PPO

Introduzione: Apprendere dalle Ricompense Il Reinforcement Learning (RL) e un paradigma di apprendimento fondamentalmente diverso dal supervised e...

Cosa ne pensi di questo articolo?

07 - Reinforcement Learning: Q-Learning, DQN e PPO
Deep LearningFoto:Christina MorilloPexels

In Breve

Introduzione: Apprendere dalle Ricompense Il Reinforcement Learning (RL) e un paradigma di apprendimento fondamentalmente diverso dal supervised e...

Introduzione: Apprendere dalle Ricompense

Il Reinforcement Learning (RL) e un paradigma di apprendimento fondamentalmente diverso dal supervised e unsupervised learning. Invece di apprendere da dati etichettati, un agente interagisce con un ambiente, esegue azioni e riceve ricompense. L'obiettivo e imparare una policy (strategia) che massimizzi la ricompensa cumulativa nel tempo.

Dal gioco degli scacchi (AlphaZero) alla robotica (manipolazione oggetti), dal trading algoritmico alla guida autonoma, il reinforcement learning e alla base di alcune delle applicazioni AI più impressionanti. In questo articolo esploreremo i concetti fondamentali, dal Q-Learning classico fino agli algoritmi deep RL moderni come DQN e PPO.

Cosa Imparerai

  • Markov Decision Process (MDP): stato, azione, ricompensa, transizione
  • Q-Learning: la tabella dei valori azione-stato
  • Deep Q-Network (DQN): approssimare Q con reti neurali
  • Policy Gradient: ottimizzare direttamente la policy
  • Proximal Policy Optimization (PPO): stabilità e performance
  • Exploration vs exploitation: bilanciare scoperta e sfruttamento
  • Implementazione pratica con Gymnasium (OpenAI)

Markov Decision Process (MDP)

Il framework formale del RL e il Markov Decision Process, definito da quattro componenti:

  • Stati (S): le possibili situazioni in cui l'agente si può trovare (es. posizione in una griglia, frame di un gioco)
  • Azioni (A): le mosse disponibili in ogni stato (es. su, giù, sinistra, destra)
  • Ricompense (R): il feedback numerico ricevuto dopo ogni azione (es. +1 per vincere, -1 per perdere)
  • Transizioni (P): la probabilità di passare a uno stato successivo data un'azione. La proprietà di Markov stabilisce che il futuro dipende solo dallo stato corrente, non dalla storia

L'agente cerca una policy ottimale che massimizza la somma scontata delle ricompense future. Il discount factor gamma (tra 0 e 1) bilancia ricompense immediate vs future: gamma vicino a 0 rende l'agente miope, vicino a 1 lo rende lungimirante.

Q-Learning: Valori Azione-Stato

Il Q-Learning e un algoritmo off-policy che impara la funzione Q(s, a): il valore atteso della ricompensa cumulativa scegliendo l'azione a nello stato s e seguendo la policy ottimale da quel momento in poi. La regola di aggiornamento e:

Q(s, a) = Q(s, a) + alpha * [R + gamma * max_a'(Q(s', a')) - Q(s, a)]


import numpy as np
import gymnasium as gym

class QLearningAgent:
    def __init__(self, n_states, n_actions, lr=0.1, gamma=0.99, epsilon=1.0):
        self.q_table = np.zeros((n_states, n_actions))
        self.lr = lr
        self.gamma = gamma
        self.epsilon = epsilon
        self.epsilon_min = 0.01
        self.epsilon_decay = 0.995

    def choose_action(self, state):
        """Epsilon-greedy: esplora con prob epsilon, sfrutta altrimenti"""
        if np.random.random() < self.epsilon:
            return np.random.randint(self.q_table.shape[1])
        return np.argmax(self.q_table[state])

    def learn(self, state, action, reward, next_state, done):
        """Aggiornamento Q-table"""
        target = reward
        if not done:
            target += self.gamma * np.max(self.q_table[next_state])
        self.q_table[state, action] += self.lr * (target - self.q_table[state, action])
        # Decay epsilon
        self.epsilon = max(self.epsilon_min, self.epsilon * self.epsilon_decay)

# Training su FrozenLake
env = gym.make('FrozenLake-v1', is_slippery=False)
agent = QLearningAgent(n_states=16, n_actions=4)

for episode in range(5000):
    state, _ = env.reset()
    total_reward = 0
    done = False

    while not done:
        action = agent.choose_action(state)
        next_state, reward, terminated, truncated, _ = env.step(action)
        done = terminated or truncated
        agent.learn(state, action, reward, next_state, done)
        state = next_state
        total_reward += reward

    if episode % 1000 == 0:
        print(f"Episode {episode}, Reward: {total_reward}, "
              f"Epsilon: {agent.epsilon:.3f}")
      

Exploration vs Exploitation

Il dilemma fondamentale del RL: l'agente deve esplorare (provare azioni nuove per scoprire ricompense migliori) e sfruttare (usare la conoscenza attuale per massimizzare la ricompensa). La strategia epsilon-greedy bilancia i due aspetti: con probabilità epsilon sceglie un'azione casuale, altrimenti l'azione migliore nota. Epsilon viene gradualmente ridotto durante il training.

Deep Q-Network (DQN)

Il Q-Learning con tabelle funziona solo per spazi di stato piccoli e discreti. Per ambienti complessi (immagini, stati continui), la Deep Q-Network (DQN) sostituisce la Q-table con una rete neurale che approssima la funzione Q. Due innovazioni chiave stabilizzano il training:

  • Experience Replay: le transizioni vengono memorizzate in un buffer e campionate casualmente per il training, rompendo la correlazione temporale tra campioni consecutivi
  • Target Network: una copia separata della rete, aggiornata periodicamente, calcola i target Q-values, stabilizzando l'apprendimento

import torch
import torch.nn as nn
from collections import deque
import random

class DQN(nn.Module):
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, 128),
            nn.ReLU(),
            nn.Linear(128, 128),
            nn.ReLU(),
            nn.Linear(128, action_dim)
        )

    def forward(self, x):
        return self.net(x)

class DQNAgent:
    def __init__(self, state_dim, action_dim, lr=1e-3, gamma=0.99):
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
        self.q_net = DQN(state_dim, action_dim).to(self.device)
        self.target_net = DQN(state_dim, action_dim).to(self.device)
        self.target_net.load_state_dict(self.q_net.state_dict())
        self.optimizer = torch.optim.Adam(self.q_net.parameters(), lr=lr)
        self.memory = deque(maxlen=100000)
        self.gamma = gamma
        self.batch_size = 64

    def store(self, state, action, reward, next_state, done):
        self.memory.append((state, action, reward, next_state, done))

    def learn(self):
        if len(self.memory) < self.batch_size:
            return
        batch = random.sample(self.memory, self.batch_size)
        states, actions, rewards, next_states, dones = zip(*batch)

        states = torch.FloatTensor(states).to(self.device)
        actions = torch.LongTensor(actions).to(self.device)
        rewards = torch.FloatTensor(rewards).to(self.device)
        next_states = torch.FloatTensor(next_states).to(self.device)
        dones = torch.FloatTensor(dones).to(self.device)

        q_values = self.q_net(states).gather(1, actions.unsqueeze(1))
        with torch.no_grad():
            next_q = self.target_net(next_states).max(1)[0]
            targets = rewards + (1 - dones) * self.gamma * next_q

        loss = nn.functional.mse_loss(q_values.squeeze(), targets)
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

    def update_target(self):
        self.target_net.load_state_dict(self.q_net.state_dict())
      

Policy Gradient e Actor-Critic

I metodi Policy Gradient ottimizzano direttamente la policy senza passare per la funzione Q. Il teorema del policy gradient stabilisce che il gradiente della ricompensa attesa rispetto ai parametri della policy e proporzionale alla ricompensa pesata per la log-probabilità delle azioni.

Actor-Critic

L'architettura Actor-Critic combina i due approcci: l'Actor (policy network) sceglie le azioni, il Critic (value network) stima quanto e buono lo stato corrente. Il Critic riduce la varianza degli aggiornamenti dell'Actor, rendendo il training più stabile.

PPO: Lo Standard Industriale

Proximal Policy Optimization (PPO), sviluppato da OpenAI, e l'algoritmo RL più usato nella pratica. La sua innovazione chiave e il clipped objective: limita quanto la nuova policy può discostarsi dalla vecchia ad ogni aggiornamento, prevenendo cambiamenti troppo drastici che destabilizzerebbero il training.

PPO e alla base di molti successi: InstructGPT e RLHF (allineamento di LLM), OpenAI Five (Dota 2), addestramento di agenti robotici e molti altri.

Prossimi Passi nella Serie

  • Nel prossimo articolo esploreremo il Transfer Learning Avanzato con BERT, GPT e Hugging Face
  • Vedremo fine-tuning, prompt engineering e RAG (Retrieval-Augmented Generation)
  • Confronteremo modelli open-source: Llama, Mistral, Falcon

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti