Con l'equazione di Bellman abbiamo uno strumento potente: dato un MDP, calcoliamo che cosa conviene fare. Ma abbiamo dato per scontata una cosa enorme: che la ricompensa sia quella giusta. Nei problemi veri la ricompensa è scritta da una persona, e scriverla bene è difficile: il segnale può arrivare molto tardi (hai vinto la partita dopo 200 mosse: quale mossa ha contato?), può non arrivare quasi mai, e quando proviamo a guidare l'agente con ricompense intermedie rischiamo di fargli scoprire una scorciatoia che non volevamo. Questo articolo ha tre parti, ciascuna con un esperimento: come γ decide fra un premio vicino e uno lontano; perché trovare un premio lontano è difficile per un agente che esplora a caso; il reward hacking, cioè un agente che ottiene moltissima ricompensa senza fare ciò che volevi, e il rimedio teoricamente fondato, il potential-based shaping.
Il vocabolario, in poche righe
Una ricompensa ritardata è un segnale utile che arriva molti passi dopo l'azione che lo ha causato; una ricompensa sparsa è quasi sempre zero e diversa da zero in pochi momenti, mentre una densa dà un segnale a quasi ogni passo. L'assegnazione del merito (credit assignment) è capire quale, fra le azioni passate, ha causato la ricompensa di adesso. Il reward shaping aggiunge ricompense intermedie per guidare l'agente quando quella vera è troppo rara. Il reward hacking (o specification gaming) è lo sfruttamento di una scappatoia della ricompensa: la definizione che ne danno Krakovna e colleghi di DeepMind è un comportamento che soddisfa la specifica letterale di un obiettivo senza raggiungere il risultato voluto. La legge di Goodhart («quando una misura diventa un obiettivo smette di essere una buona misura») ne è il principio generale in forma di proverbio. Infine un potenziale Φ(s) è una funzione che assegna un numero a ogni stato (ad esempio «quanto sono avanzato») e serve a costruire uno shaping che lascia invariata la politica ottima.
L'analogia: pagare gli sviluppatori a righe di codice
Un'azienda decide di premiare gli sviluppatori in base alle righe di codice scritte, perché «più righe significa più lavoro». Nel giro di un mese il codice è pieno di righe inutili, commenti ripetuti e funzioni spezzate in dieci. Gli sviluppatori non hanno barato: hanno fatto esattamente ciò per cui erano pagati. La misura (righe) era un sostituto imperfetto dell'obiettivo (software che funziona), e un'ottimizzazione abbastanza forte trova sempre la distanza fra le due. Un agente di RL è un ottimizzatore molto forte e molto letterale: non intuisce le intenzioni, massimizza il numero che gli dai.
Parte 1: ricompense ritardate e il peso di γ
Costruiamo il caso più semplice possibile, con un conto che si può fare a mano. Un corridoio di 13 celle numerate da 0 a 12. L'agente parte dalla cella 3. Entrando nella cella 0 incassa un premio piccolo (+1, a 3 passi di distanza); entrando nella cella 12 incassa un premio grande (+10, a 9 passi). Entrambe le celle terminano l'episodio. Che cosa converrà all'agente?
Se prendo il premio piccolo, lo ricevo al terzo passo: il suo valore scontato è 1 · γ² (la ricompensa del primo passo vale γ⁰, quella del terzo γ²). Il grande lo ricevo al nono passo: 10 · γ⁸. Il grande conviene quando 10γ⁸ > γ², cioè γ⁶ > 0.1, cioè γ > 0.1^(1/6) ≈ 0.6813. Sotto questa soglia l'agente è troppo miope per aspettare. Verifichiamo con la value iteration dell'articolo su Bellman, adattata al corridoio. I listati di questo articolo si eseguono in sequenza nello stesso file.
import numpy as np
class Corridor:
"""A line of cells 0..n-1. Actions: 0 = left, 1 = right. Same interface as GridWorld: P[s][a] = [(prob, next_state, reward, done)].
`rewards` maps a cell to the reward received when ENTERING it; `terminals` ends the episode."""
def __init__(self, n, rewards, terminals, step_reward=0.0):
self.n_states, self.n_actions, self.terminals = n, 2, set(terminals)
self.P = {}
for s in range(n):
self.P[s] = {}
for a in range(2):
if s in self.terminals:
self.P[s][a] = [(1.0, s, 0.0, True)]
continue
s2 = min(max(s + (1 if a == 1 else -1), 0), n - 1)
self.P[s][a] = [(1.0, s2, step_reward + rewards.get(s2, 0.0), s2 in self.terminals)]
def q_values(env, V, s, gamma):
return [sum(p * (r + gamma * V[s2] * (not done)) for p, s2, r, done in env.P[s][a]) for a in range(env.n_actions)]
def value_iteration(env, gamma, tol=1e-12):
V = np.zeros(env.n_states)
while True:
delta = 0.0
for s in range(env.n_states):
v = max(q_values(env, V, s, gamma))
delta = max(delta, abs(v - V[s])); V[s] = v
if delta < tol:
return V
# Cells 0..12. Start at cell 3: a SMALL prize (+1) 3 steps to the left, a BIG prize (+10) 9 steps to the right.
env = Corridor(13, rewards={0: 1.0, 12: 10.0}, terminals=(0, 12))
start = 3
print("distance to the small prize:", start - 0, " to the big prize:", 12 - start)
print("break-even: 1 * g^2 = 10 * g^8 -> g =", round(0.1 ** (1 / 6), 4))
for gamma in (0.5, 0.65, 0.68, 0.69, 0.8, 0.99):
V = value_iteration(env, gamma)
q = q_values(env, V, start, gamma)
print(f"gamma={gamma:4.2f} value of the start {V[start]:7.4f} best first move: {'RIGHT (big prize)' if q[1] > q[0] else 'LEFT (small prize)'}")
# Output (due esecuzioni consecutive, stessa uscita):
# distance to the small prize: 3 to the big prize: 9
# break-even: 1 * g^2 = 10 * g^8 -> g = 0.6813
# gamma=0.50 value of the start 0.2500 best first move: LEFT (small prize)
# gamma=0.65 value of the start 0.4225 best first move: LEFT (small prize)
# gamma=0.68 value of the start 0.4624 best first move: LEFT (small prize)
# gamma=0.69 value of the start 0.5138 best first move: RIGHT (big prize)
# gamma=0.80 value of the start 1.6777 best first move: RIGHT (big prize)
# gamma=0.99 value of the start 9.2274 best first move: RIGHT (big prize)
La classe Corridor ha la stessa interfaccia della GridWorld (tabella P[s][a] di tuple probabilità-stato-ricompensa-fine); il dizionario rewards dice quale ricompensa si incassa entrando in una cella. q_values calcola il valore di ogni azione con la formula di Bellman e value_iteration la applica finché non cambia più. Poi, per ogni γ, si guarda quale prima mossa ha il q più alto. Il risultato conferma il conto a mano: la scelta passa da «sinistra» (premio piccolo) a «destra» (premio grande) fra 0.68 e 0.69, con la soglia a 0.6813. Il valore di partenza con γ = 0.5 è 0.25 (1·0.5²); con γ = 0.99 è 9.23, vicino a 10·0.99⁸.
La lezione è che γ non è solo un parametro numerico: decide le preferenze dell'agente. Un agente con γ = 0.5 sceglierà sempre il premio piccolo e vicino, anche se il grande vale dieci volte tanto. Se il tuo problema richiede pazienza (investire oggi per guadagnare fra mille passi) serve γ alto, con il costo già visto: calcolo più lento e apprendimento più instabile.
Parte 2: un premio lontano è difficile da trovare
La value iteration dava la risposta esatta perché conosceva la mappa. Un agente che impara dall'esperienza (come nel Q-Learning) deve prima scoprire dove sta il premio, e se la ricompensa è nulla ovunque tranne in un punto lontano non riceve nessun segnale finché non ci inciampa per caso. Misuriamo quanto è probabile con un agente completamente casuale (passo a sinistra o a destra con probabilità ½, partenza dal bordo sinistro) in un corridoio con il premio all'estremità opposta.
# Delayed reward and EXPLORATION: how often does a purely random agent stumble on a far-away prize?
def random_agent_success(n_cells, horizon=300, episodes=3000, seed=0):
rng = np.random.default_rng(seed)
hits = 0
for _ in range(episodes):
pos = 0 # start at the left end, the single reward sits at the right end
for _ in range(horizon):
pos = max(pos + (1 if rng.random() < 0.5 else -1), 0)
if pos == n_cells - 1:
hits += 1
break
return hits / episodes
for n in (5, 10, 20, 40, 80):
print(f"corridor of {n:2d} cells: random agent reaches the prize within 300 steps in {100 * random_agent_success(n):5.1f}% of episodes")
# Output (due esecuzioni consecutive, stessa uscita):
# corridor of 5 cells: random agent reaches the prize within 300 steps in 100.0% of episodes
# corridor of 10 cells: random agent reaches the prize within 300 steps in 97.8% of episodes
# corridor of 20 cells: random agent reaches the prize within 300 steps in 52.5% of episodes
# corridor of 40 cells: random agent reaches the prize within 300 steps in 4.7% of episodes
# corridor of 80 cells: random agent reaches the prize within 300 steps in 0.0% of episodes
La funzione random_agent_success simula 3000 episodi da 300 passi: a ogni passo l'agente si muove di una cella a caso (il bordo sinistro lo respinge, max(pos + …, 0)) e si registra se tocca la cella del premio. Il seme fisso default_rng(0) rende l'uscita riproducibile. Fino a 10 celle l'agente casuale ce la fa quasi sempre, a 20 celle in circa metà dei casi, a 40 celle solo nel 4.7% e a 80 celle mai in 3000 tentativi. È la fisica della camminata casuale: la distanza tipica percorsa in n passi cresce come la radice di n, non come n; per percorrere 80 celle servono nell'ordine di 6400 passi, ben oltre l'orizzonte di 300.
Se l'unico segnale è alla fine di un percorso lungo, l'agente non vede mai una ricompensa diversa da zero e non ha nulla da cui imparare: è il problema dell'esplorazione con ricompensa sparsa. Le strade principali sono le ricompense intermedie (reward shaping), l'esplorazione guidata (curiosità, bonus per stati nuovi), le dimostrazioni di esperti e i curricula (partire da problemi vicini al traguardo). Sutton e Barto, nella sezione 17.4 del loro testo, discutono proprio la tentazione di risolvere il problema della ricompensa sparsa premiando i sotto-obiettivi, con il rischio che l'agente ottimizzi i sotto-obiettivi e non l'obiettivo. Ed è qui la trappola: il reward shaping fatto a occhio produce reward hacking.
Parte 3: il reward hacking, misurato
Vogliamo guidare l'agente verso il traguardo in un corridoio di 8 celle (traguardo nella cella 7, +1, terminale; costo −0.01 per passo; partenza dalla cella 3). Proviamo tre modi di aggiungere ricompense «per aiutare» e li confrontiamo con il compito onesto.
# Reward hacking: we add rewards to "encourage progress", and the agent finds a loophole.
def describe(env, gamma, start=3, max_steps=60):
V = value_iteration(env, gamma)
s, total, path = start, 0.0, [start]
for _ in range(max_steps):
a = int(np.argmax(q_values(env, V, s, gamma)))
p, s2, r, done = env.P[s][a][0]
total += r; s = s2; path.append(s)
if done:
return V, path, total, True
return V, path, total, False
gamma = 0.95
# 0) The honest task: +1 at the goal (cell 7, terminal), a small cost per step.
honest = Corridor(8, rewards={7: 1.0}, terminals=(7,), step_reward=-0.01)
V0, path, total, reached = describe(honest, gamma)
print(f"HONEST reached goal: {reached} | path {path} | reward {total:.2f}")
# 1) A "bonus tile": +0.3 EVERY time the agent enters cell 2 (the designer wanted to guide it).
bonus = Corridor(8, rewards={2: 0.3, 7: 1.0}, terminals=(7,), step_reward=-0.01)
V1, path, total, reached = describe(bonus, gamma)
print(f"BONUS TILE reached goal: {reached} | path {path[:12]}... | reward in 60 steps {total:.2f}")
# 2) "Reward every step to the right": +0.3 whenever the agent moves right
progress = Corridor(8, rewards={7: 1.0}, terminals=(7,), step_reward=-0.01)
for s in range(7):
p, s2, r, d = progress.P[s][1][0]
progress.P[s][1] = [(p, s2, r + 0.3, d)]
V2, path, total, reached = describe(progress, gamma)
print(f"RIGHT-MOVE BONUS reached goal: {reached} | path {path[:12]}... | reward in 60 steps {total:.2f}")
# 3) The same intention written as POTENTIAL-BASED shaping (Ng, Harada & Russell, 1999): F = gamma*Phi(s') - Phi(s), Phi(s) = 0.3*s
phi = lambda s: 0.3 * s
shaped = Corridor(8, rewards={7: 1.0}, terminals=(7,), step_reward=-0.01)
for s in range(7):
for a in range(2):
p, s2, r, d = shaped.P[s][a][0]
shaped.P[s][a] = [(p, s2, r + gamma * phi(s2) * (not d) - phi(s), d)]
V3, path, total, reached = describe(shaped, gamma)
print(f"POTENTIAL-BASED reached goal: {reached} | path {path}")
print(f"value at the start: honest {V0[3]:.3f} potential-based {V3[3]:.3f} difference {V3[3] - V0[3]:.3f} = -Phi(start) = {-phi(3):.3f}")
# Output (due esecuzioni consecutive, stessa uscita):
# HONEST reached goal: True | path [3, 4, 5, 6, 7] | reward 0.96
# BONUS TILE reached goal: False | path [3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2]... | reward in 60 steps 8.40
# RIGHT-MOVE BONUS reached goal: False | path [3, 4, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6]... | reward in 60 steps 8.70
# POTENTIAL-BASED reached goal: True | path [3, 4, 5, 6, 7]
# value at the start: honest 0.820 potential-based -0.080 difference -0.900 = -Phi(start) = -0.900
describe calcola la politica ottima per la ricompensa data (value iteration) e poi la esegue per al più 60 passi dallo stato di partenza, restituendo il percorso, la ricompensa totale incassata e se ha raggiunto il traguardo. Nel compito onesto c'è solo +1 al traguardo e l'agente va dritto: 3 → 4 → 5 → 6 → 7, ricompensa totale 0.96 (+1 meno quattro passi da 0.01). Nessuna sorpresa.
Con la piastrella bonus il progettista aggiunge +0.3 ogni volta che l'agente entra nella cella 2 (voleva «attirarlo» verso un punto intermedio). Risultato: l'agente non arriva mai al traguardo. Percorre 3 → 2 → 3 → 2 → … all'infinito, incassando 0.3 ogni due passi: 8.40 in 60 passi, contro 0.96 del percorso onesto. Con il ritorno scontato il ciclo vale circa 0.2805 / (1 − γ²) ≈ 2.88 (il passo di andata dà +0.29, quello di ritorno −0.01 scontato di γ) contro circa 0.82 del traguardo: per l'agente il ciclo è la scelta ottima. Ha ragione lui: è la ricompensa a essere sbagliata.
Il premio a ogni passo verso destra è più sottile: +0.3 ogni volta che ci si sposta a destra, «per incoraggiare il progresso». L'agente avanza 3 → 4 → 5 → 6 e poi oscilla 5 → 6 → 5 → 6, incassando +0.3 a ogni passo a destra. Non entra mai nel traguardo, perché il passo finale (+1 e +0.3, poi l'episodio termina) vale meno del flusso perpetuo di piccoli premi: 8.70 in 60 passi. Nel quarto caso, il potential-based shaping, la stessa intenzione («premia il progresso verso destra») scritta in un modo particolare ripara il problema: l'agente va dritto al traguardo. Vediamo perché.
Il potential-based shaping, con la dimostrazione
Ng, Harada e Russell (1999) hanno dimostrato che c'è un modo di aggiungere ricompense intermedie senza cambiare la politica ottima, e che quella forma è anche necessaria: ogni altra forma può cambiarla. Si sceglie un potenziale Φ(s) (qui 0.3·s: più a destra, più «avanzato») e si aggiunge a ogni transizione s → s′ il termine
F(s, s′) = γ · Φ(s′) − Φ(s)
Perché funziona? Guardiamo che cosa succede alla somma scontata dei termini F lungo una traiettoria s_0, s_1, s_2, …, s_T:
Σ_{t=0..T−1} γ^t · F(s_t, s_{t+1}) = Σ γ^t · ( γ Φ(s_{t+1}) − Φ(s_t) )
= Σ ( γ^{t+1} Φ(s_{t+1}) − γ^t Φ(s_t) )
= γ^T Φ(s_T) − Φ(s_0)
La somma è «telescopica»: tutti i termini intermedi si cancellano. Se si pone Φ = 0 sugli stati terminali (come facciamo), γ^T Φ(s_T) = 0 e il bonus totale dipende solo dallo stato di partenza: −Φ(s_0). Un valore che non dipende da come si è camminato non può essere sfruttato in nessun ciclo: ogni ritorno viene alterato dalla stessa costante, quindi l'ordinamento delle politiche non cambia. Formalmente il valore ottimo diventa v′*(s) = v*(s) − Φ(s). L'ultima riga dell'uscita lo conferma: il valore di partenza passa da 0.820 a −0.080, differenza −0.900 = −Φ(3) = −0.3·3, e il percorso resta 3 → 4 → 5 → 6 → 7.
Si vede anche perché le due «scorciatoie» fallivano: un premio per ogni passo a destra non è della forma γΦ(s′) − Φ(s), quindi non si telescopizza, e un ciclo destra-sinistra-destra incassa il premio più volte senza che nulla lo compensi. In forma potential-based andare a destra dà un termine positivo e tornare a sinistra uno negativo più grande in valore assoluto: un ciclo costa circa 0.3(γ − 1) < 0 in termini di shaping. Il profitto ciclico è impossibile per costruzione.
Come progettare una ricompensa (e come accorgersi che è sbagliata)
- Scrivi prima l'obiettivo vero, poi la ricompensa. Se non sai dire a parole che cosa significa «successo», non puoi scrivere un numero che lo misuri.
- Preferisci poche ricompense legate all'esito (vinto/perso, tempo impiegato) a molte legate ai mezzi (passi percorsi, oggetti toccati).
- Se devi aggiungere ricompense intermedie, usa il potential-based shaping. È la famiglia con la garanzia teorica di non cambiare la politica ottima.
- Misura il comportamento, non solo la ricompensa. Una ricompensa che sale è compatibile con un agente che sta barando (8.4 contro 0.96): valuta con una metrica separata, qui «ha raggiunto il traguardo?».
- Guarda le traiettorie. Cicli, oscillazioni e agenti fermi in un angolo sono segnali tipici di hacking.
- Togli il carburante dei cicli. Un limite di tempo, un costo per passo o bonus non ripetibili (si incassano una volta per episodio) tolgono ciò che alimenta il ciclo.
L'ordine non è una graduatoria, ma c'è un filo: i punti 1 e 2 evitano di creare la scappatoia, il 3 ti dà un modo sicuro di aiutare l'apprendimento, i punti 4 e 5 la scoprono quando c'è comunque, il 6 la chiude. Se devi sceglierne uno solo, scegli il quarto: senza una metrica indipendente dalla ricompensa non puoi nemmeno accorgerti di avere un problema.
Esempi pubblici di questo fenomeno non mancano. Uno molto citato è l'agente del gioco di barche Coast Runners, descritto da Amodei e Clark nel 2016 e ripreso dal post di DeepMind sullo specification gaming: l'agente riceveva una ricompensa di shaping per i blocchi verdi lungo il percorso, e imparò a girare in tondo colpendo sempre gli stessi blocchi invece di finire la gara. Il post di DeepMind nota proprio che lo shaping cambia la politica ottima se non è potential-based. Non ho riprodotto quel caso, ma il meccanismo è lo stesso della nostra piastrella bonus.
I compromessi e le trappole
Una ricompensa densa dà apprendimento rapido ma più rischio di hacking, una sparsa è fedele all'obiettivo ma spesso impossibile da imparare per esplorazione. Un γ alto dà pazienza ma apprendimento più difficile, un γ basso è veloce ma miope (soglia 0.68 nel nostro esempio). Lo shaping fatto a mano è flessibile e pericoloso, quello potential-based è sicuro ma richiede di saper scrivere un potenziale sensato; la teoria garantisce che il potenziale non cambia la politica ottima, e la sua qualità influenza solo la velocità di apprendimento.
Le trappole ricorrenti discendono da qui. Premiare ciò che si può ripetere è un invito al ciclo. Valutare solo con la ricompensa di addestramento nasconde l'hacking, perché l'agente hacker ha la ricompensa più alta. Pensare che l'hacking sia un bug dell'algoritmo è un errore: qui la value iteration è esatta e fa il suo mestiere, il difetto è nella ricompensa. Con γ troppo basso un premio a 9 passi vale 10·0.5⁸ ≈ 0.04, praticamente invisibile. Un potenziale che non si annulla ai terminali rompe la dimostrazione, perché resta il termine γ^T Φ(s_T) che dipende dal finale e può cambiare la scelta. E con ricompensa sparsa e ε piccolo un risultato di apprendimento «nullo» non vuol dire che il problema sia impossibile, ma che l'esplorazione non ha trovato il premio.
Un esempio svolto a mano: le ricompense «a ogni passo» e lo shaping, transizione per transizione
Riprendiamo il corridoio con γ = 0.95 e il potenziale Φ(s) = 0.3·s, cioè Φ(3) = 0.9, Φ(4) = 1.2. Il costo di un passo è −0.01.
Premio «a ogni passo verso destra» (non potential-based). Il passo 3 → 4 vale −0.01 + 0.3 = +0.29; il passo indietro 4 → 3 vale −0.01. Un ciclo andata-e-ritorno incassa 0.29 − 0.01 = +0.28 senza sconto: positivo e ripetibile all'infinito. Con lo sconto (0.29 − 0.95·0.01 = 0.2805 per ciclo di due passi) il valore del ciclo infinito è 0.2805/(1 − 0.95²) ≈ 2.88, più di quanto valga il traguardo (0.82). L'agente fa il ciclo.
La stessa intenzione, potential-based. Il termine di shaping per 3 → 4 è F = γΦ(4) − Φ(3) = 0.95·1.2 − 0.9 = +0.24, quindi la ricompensa vale −0.01 + 0.24 = +0.23. Per il passo indietro, F = γΦ(3) − Φ(4) = 0.95·0.9 − 1.2 = −0.345, quindi −0.01 − 0.345 = −0.355. Il ciclo vale 0.23 − 0.355 = −0.125 senza sconto (e 0.23 + 0.95·(−0.355) = −0.107 con lo sconto): negativo. Ogni passo avanti è premiato, ogni ritorno indietro è punito di più: il ciclo non conviene mai, qualunque sia il numero di giri. Nel primo caso il premio dipende solo dalla mossa e non ricorda dove sei stato; nel secondo dipende dalla differenza di potenziale fra dove sei e dove vai, ed è per questo «a somma zero» sui cicli.
Una verifica a mano anche per la parte sull'esplorazione. Una camminata casuale simmetrica con una parete riflettente in 0 impiega in media n² passi per raggiungere la distanza n (risultato classico, non ricavato qui). Per n = 20: 400 passi; per 40: 1600; per 80: 6400. Con un limite di 300 passi ci si aspetta di arrivare a 20 celle circa metà delle volte (misurato: 52.5%) e di non arrivare quasi mai a 80. La distanza tipica raggiunta in 300 passi è dell'ordine di √(2·300/π) ≈ 14 celle per lo spostamento medio assoluto di una camminata libera (√300 ≈ 17 come scala di deviazione standard), non 80.
Esercizi con soluzione
- Premio +2 a 4 passi, premio +8 a 10 passi. A quale γ si pareggiano?
Soluzione:2γ³ = 8γ⁹ → γ⁶ = 0.25 → γ = 0.25^(1/6) ≈ 0.794(ricalcolato: 0.7937). - Perché la somma dei termini di shaping potential-based è «telescopica»?
Soluzione: perché ogniΦ(s_t)compare una volta con segno positivo e una con segno negativo, moltiplicato per la stessa potenza di γ, e si cancella con il termine adiacente. - Un agente riceve +1 ogni volta che raccoglie una moneta che riappare dopo 5 passi. Qual è il rischio?
Soluzione: che impari a restare vicino alla moneta per sempre invece di completare l'obiettivo vero (reward hacking per bonus ripetibile). - Perché un agente casuale non trova un premio a 80 celle di distanza in 300 passi?
Soluzione: perché la distanza tipica di una camminata casuale cresce come la radice del numero di passi: servono nell'ordine di 80² = 6400 passi, non 300.
Il primo esercizio ripete il conto della soglia di γ con numeri diversi, il secondo è la dimostrazione in una frase. Gli ultimi due sono i casi da portare in pratica: riconoscere un bonus ripetibile prima di addestrare e stimare a priori se un premio lontano è raggiungibile a caso. Se rispondi senza rileggere, puoi passare al Q-Learning.
Domande frequenti
Come faccio a sapere se la mia ricompensa è «bucata»?
Guardando le traiettorie dell'agente addestrato: cicli, oscillazioni e ripetizione dello stesso gesto sono i segni. Confronta la ricompensa totale con quella di una politica «onesta» scritta a mano: se l'agente guadagna molte volte tanto senza raggiungere l'obiettivo (qui 8.40 contro 0.96), hai un hacking.
Il potential-based shaping aiuta davvero l'apprendimento?
Può aiutare (dà un segnale a ogni passo), ma la teoria garantisce soltanto che non cambia la politica ottima; la velocità di apprendimento dipende da quanto il potenziale è informativo. Un potenziale scadente non cambia l'ottimo, ma non aiuta.
Come scelgo il potenziale?
Spesso come una stima del valore dello stato (distanza dall'obiettivo, quota raggiunta). Se conoscessi esattamente v* e ponessi Φ = v*, il valore ottimo trasformato v* − Φ sarebbe zero ovunque: vale la pena di avvicinarcisi.
Posso correggere l'hacking aggiungendo una penalità?
A volte (un costo per passo scoraggia i cicli), ma è un gioco del gatto con il topo: ogni penalità è una nuova ricompensa che l'agente può sfruttare in modo imprevisto. Meglio rendere la ricompensa fedele all'obiettivo.
E se non posso aggiungere ricompense?
Si possono usare dimostrazioni (imitazione), un curriculum di problemi via via più difficili o una ricompensa intrinseca di curiosità. Ognuna ha costi e rischi suoi: nessuna è gratis.
Che cosa sai fare adesso
Sai calcolare quando un premio lontano batte uno vicino (soglia 0.6813 nel corridoio, verificata con value iteration) e sai prevedere se un agente casuale ha una probabilità ragionevole di trovarlo (100% a 5 celle, 52.5% a 20, 4.7% a 40, 0% a 80 su 3000 episodi). Sai riconoscere un reward hacking dalle traiettorie e dalla distanza fra ricompensa e obiettivo (8.40 girando in tondo contro 0.96 raggiungendo il traguardo), e sai applicare il potential-based shaping F = γΦ(s′) − Φ(s), che sposta il valore di una costante (−0.900 nel nostro caso) senza toccare la politica ottima. Nel prossimo articolo smettiamo di conoscere il modello: l'agente impara dall'esperienza con il Q-Learning.
Fonti
- Ng, Harada e Russell, Policy invariance under reward transformations: Theory and application to reward shaping (ICML 1999) - il reward shaping basato su potenziale preserva la politica ottima ed è condizione necessaria; PDF scaricato, abstract letto, consultato il 2026-11-12
- Krakovna et al., Specification gaming: the flip side of AI ingenuity (DeepMind, 2020-04-21) - definizione di specification gaming, caso Coast Runners e nota sullo shaping non potential-based; pagina scaricata e letta, consultata il 2026-11-12
- Sutton e Barto, Reinforcement Learning: An Introduction, seconda edizione (PDF dell'autore) - sezione 17.4, progettare i segnali di ricompensa e il problema della ricompensa sparsa; PDF scaricato, passi letti, consultato il 2026-11-12
Commenti
Caricamento commenti...
Accedi per commentare