Finora ogni modello di questa serie imparava da esempi: «ecco l'ingresso, ecco la risposta giusta». Il reinforcement learning (apprendimento per rinforzo, RL) cambia il quadro: nessuno ti dà la risposta giusta. C'è un agente che agisce in un ambiente, e dopo ogni azione riceve un solo numero, una ricompensa, che dice quanto è andata bene adesso. Dall'esperienza accumulata l'agente deve scoprire da solo che cosa fare. È il modo in cui si impara a giocare, a camminare, a guidare un robot. In questo articolo fissi il vocabolario, vedi in che cosa l'RL differisce dall'apprendimento supervisionato e affronti la difficoltà più caratteristica del campo, il dilemma esplorazione-sfruttamento, nel caso più piccolo possibile: il banditore a più bracci, misurato su 2000 problemi con un listato che puoi rieseguire.
Il vocabolario, con un esempio per ogni parola
Prendi un robot aspirapolvere. L'agente è chi decide, cioè il programma che governa il robot; l'ambiente è tutto ciò che sta fuori da questa decisione e reagisce alle azioni: la casa, i mobili, il pavimento. Lo stato S è la descrizione della situazione così come l'agente la vede (le letture dei sensori, la carica residua), l'azione A è una scelta fra quelle disponibili (gira a sinistra, vai avanti, torna alla base). Dopo ogni azione l'ambiente restituisce una ricompensa R (reward), un numero: positivo se è andata bene, negativo se è andata male. È l'unico segnale di apprendimento, e l'agente non sa perché ha preso quel numero.
L'interazione procede a passi temporali t; una sequenza completa dall'inizio alla fine (una pulizia, una partita, un tentativo) è un episodio. La politica π è la regola con cui l'agente sceglie l'azione dato lo stato, deterministica («in questo stato, vai a destra») o probabilistica, ed è ciò che l'RL vuole imparare. L'obiettivo non è la ricompensa immediata ma il ritorno G (return), la somma delle ricompense future: un robot che per prendere un punto subito spinge un vaso giù dal tavolo ha massimizzato la ricompensa e perso il ritorno. Infine sfruttare significa scegliere l'azione che, secondo ciò che sai, è la migliore, ed esplorare significa provarne altre per scoprire se ce n'è una migliore. Fare solo l'uno o solo l'altro è un errore, e il resto dell'articolo misura quanto.
L'analogia: scegliere un ristorante in una città nuova
Sei in una città che non conosci e ogni sera devi scegliere un ristorante. Dopo la cena sai solo quanto ti è piaciuta (la ricompensa); nessuno ti dice quale fosse il migliore della città. Il primo ristorante che ti è piaciuto è un buon candidato, ma è davvero il migliore, o semplicemente il primo che hai provato? Se vai sempre lì (solo sfruttamento) potresti non scoprire mai il ristorante eccezionale all'angolo. Se provi sempre ristoranti nuovi (solo esplorazione) non godi mai di ciò che hai imparato. La strategia sensata è una via di mezzo: per lo più torni dove ti sei trovato bene, ogni tanto provi un posto nuovo. E il tuo gusto è rumoroso: una serata storta in un ristorante ottimo non deve farti scartare il posto. Tutto questo è, quasi parola per parola, il banditore a più bracci.
Il ciclo agente-ambiente
Il disegno mostra l'unica cosa che c'è in tutto l'RL. A ogni istante t: l'agente osserva lo stato S_t, sceglie un'azione A_t secondo la sua politica, l'ambiente risponde con una ricompensa R_{t+1} e un nuovo stato S_{t+1}. Una traiettoria è la sequenza S_0, A_0, R_1, S_1, A_1, R_2, S_2, …. Il ritorno dal tempo t, nella forma più semplice, è la somma delle ricompense successive:
G_t = R_{t+1} + R_{t+2} + R_{t+3} + … + R_T
Nelle prossime pagine della serie lo modificheremo con uno sconto, e capiremo perché serve (lo trovi in l'equazione di Bellman derivata per intero). Qui conta l'idea: l'agente non massimizza l'ultima ricompensa, ma la somma di tutte quelle che verranno. Lo stesso ciclo è quello che la libreria Gymnasium espone nel codice, con un ambiente che restituisce nuova osservazione e ricompensa a ogni azione: ci arrivi in Gymnasium in pratica.
In che cosa l'RL è diverso dall'apprendimento supervisionato
Supervisionato Reinforcement learning
cosa riceve l'ingresso e la risposta giusta uno stato e una ricompensa
chi dà il "giusto" un'etichetta, subito nessuno: solo un numero, a volte tardi
i dati dipendono da dal dataset (fissi) dalle azioni dell'agente
feedback completo (so cosa avrei dovuto parziale (so com'è andata, non cosa
fare) sarebbe successo con un'altra azione)
errore tipico overfitting esplorare troppo poco, ricompensa mal
progettata, instabilità
Tre difficoltà discendono da questa tabella. La prima è l'assegnazione del merito (credit assignment): se vinci una partita dopo 200 mosse, quale mossa è stata decisiva? La seconda è che i dati non sono indipendenti: ciò che l'agente osserva dipende da ciò che ha già fatto, quindi una politica cattiva produce dati poveri su ciò che non ha mai provato. La terza è l'esplorazione: bisogna accettare di fare azioni che sembrano peggiori, per imparare. Nel supervisionato (per esempio la regressione lineare) il dataset resta fermo mentre il modello cambia; qui è il modello a decidere quali dati vedrà. Il banditore che segue isola solo la terza difficoltà, la più facile da misurare.
Il caso più semplice: il banditore a 10 bracci
Togliamo tutto tranne l'essenziale. Non c'è stato (o ce n'è uno solo): una slot machine con k = 10 leve, ciascuna con una ricompensa media nascosta diversa. A ogni passo tiri una leva e ricevi una ricompensa casuale intorno a quella media. Obiettivo: ottenere la ricompensa totale più alta in 1000 tiri, senza sapere quale sia la leva migliore. È il problema del multi-armed bandit, trattato nel secondo capitolo di Sutton e Barto, Reinforcement Learning: An Introduction (seconda edizione), il testo di riferimento. Il setup del listato qui sotto è quello del loro «10-armed testbed» (sezione 2.3): 2000 problemi generati a caso, medie vere estratte da una normale di media 0 e varianza 1, ricompense con varianza 1 intorno alla media. I numeri che leggi sono però quelli prodotti dal listato su questa macchina, non quelli del libro, che non ho confrontato.
Stimare il valore di un'azione e aggiornarlo senza memorizzare tutto
Chiamiamo q(a) la ricompensa media vera della leva a (che non conosciamo) e Q_n(a) la nostra stima dopo n tiri di quella leva: la media delle ricompense viste. Per non dover conservare tutte le ricompense si ricava un aggiornamento incrementale, la stessa idea della sezione 2.4 del libro: memoria costante e un calcolo per passo.
Q_{n+1} = (1/(n+1)) · Σ_{i=1..n+1} R_i
= (1/(n+1)) · ( R_{n+1} + Σ_{i=1..n} R_i )
= (1/(n+1)) · ( R_{n+1} + n · Q_n ) perché Σ_{i=1..n} R_i = n · Q_n
= (1/(n+1)) · ( R_{n+1} + (n+1) · Q_n − Q_n )
= Q_n + (1/(n+1)) · ( R_{n+1} − Q_n )
Il passaggio che regge la derivazione è il terzo: la somma delle prime n ricompense non serve più, perché è già contenuta in n · Q_n. La forma finale ha la struttura che incontreremo per tutta la serie:
nuova stima = vecchia stima + passo × ( bersaglio − vecchia stima )
Il termine fra parentesi, R_{n+1} − Q_n, è un errore: quanto la ricompensa appena ricevuta differisce da ciò che ci aspettavamo. Se è positivo alziamo la stima, se negativo la abbassiamo, e il passo 1/(n+1) dice di quanto. Verifica con numeri: tiri una leva tre volte e ricevi 1.0, 0.0 e 2.0. Dopo il primo tiro Q = 0 + 1·(1.0 − 0) = 1.0; dopo il secondo Q = 1.0 + ½·(0.0 − 1.0) = 0.5; dopo il terzo Q = 0.5 + ⅓·(2.0 − 0.5) = 1.0, proprio la media di (1.0, 0.0, 2.0). Se il passo fosse costante (α fisso al posto di 1/n) si otterrebbe una media «che dimentica», utile quando le ricompense cambiano nel tempo; per le medie campionarie e le stime in generale vedi anche Statistica: stimare, confrontare, decidere.
La strategia ε-greedy
Come scegliere la leva? Due estremi. Greedy («avido»): sempre la leva con il Q più alto. Casuale: sempre a caso. La strategia ε-greedy è il compromesso più semplice: con probabilità ε si esplora (leva a caso), con probabilità 1 − ε si sfrutta (leva con Q massimo). Il listato usa numpy.random.default_rng(seed), il generatore consigliato dalla documentazione NumPy, con un seme per il bandit e uno diverso per l'agente, così due esecuzioni danno la stessa uscita.
import numpy as np
class Bandit:
"""k-armed bandit: arm a pays a reward drawn from N(true_value[a], 1). The agent does not know true_value."""
def __init__(self, k=10, seed=0):
self.rng = np.random.default_rng(seed)
self.true_value = self.rng.normal(0, 1, k) # hidden mean reward of each arm
self.best = int(self.true_value.argmax())
def pull(self, a):
return self.rng.normal(self.true_value[a], 1.0)
def run_epsilon_greedy(bandit, steps=1000, epsilon=0.1, seed=0):
rng = np.random.default_rng(seed)
k = len(bandit.true_value)
Q = np.zeros(k) # estimated value of each arm
N = np.zeros(k) # how many times each arm was pulled
rewards, optimal = [], []
for t in range(steps):
if rng.random() < epsilon:
a = int(rng.integers(k)) # EXPLORE: a random arm
else:
a = int(Q.argmax()) # EXPLOIT: the best arm so far
r = bandit.pull(a)
N[a] += 1
Q[a] += (r - Q[a]) / N[a] # incremental sample average (derived in the text)
rewards.append(r); optimal.append(a == bandit.best)
return np.array(rewards), np.array(optimal)
La classe Bandit nasconde un vettore true_value di 10 medie estratte da una normale standard; pull(a) restituisce una ricompensa casuale intorno alla media della leva a (deviazione 1). best è l'indice della leva migliore, che l'agente non conosce: serve solo a misurare. In run_epsilon_greedy l'agente tiene Q (stime, tutte a zero all'inizio) e N (quante volte ha tirato ogni leva). A ogni passo, con probabilità epsilon sceglie a caso (rng.integers(k)), altrimenti prende Q.argmax(); poi tira la leva, incrementa il contatore e applica l'aggiornamento derivato sopra, Q[a] += (r - Q[a]) / N[a]. Due dettagli contano. Se togli la riga che incrementa N[a] prima dell'aggiornamento, la divisione per N[a] avviene con N[a] = 0 alla prima volta che tocchi una leva: Q diventa infinito e NumPy lo segnala solo con un avviso, non con un errore. E argmax in caso di parità restituisce la prima occorrenza, come dichiara la documentazione NumPy: all'inizio, con tutte le stime a zero, l'agente greedy parte quindi sempre dalla leva 0.
L'esperimento: tre valori di ε su 2000 problemi
Un solo problema è troppo rumoroso per concludere: la leva migliore potrebbe essere stata trovata per fortuna. Mediamo su 2000 bandit diversi (ognuno con le proprie 10 medie), 1000 tiri ciascuno, per ε = 0 (greedy puro), 0.01 e 0.1. Il codice di experiment accumula la ricompensa e la frequenza di scelta ottima passo per passo, sommando su 2000 problemi (ognuno con un seme diverso, quindi medie nascoste diverse) e dividendo. L'esecuzione richiede circa mezzo minuto su una CPU ordinaria (28 s in una prova su questa macchina: è un ordine di grandezza, non un benchmark).
# Average over 2000 independent bandit problems, so that luck washes out
def experiment(epsilon, n_problems=2000, steps=1000):
R = np.zeros(steps); O = np.zeros(steps)
for p in range(n_problems):
bandit = Bandit(seed=p)
r, o = run_epsilon_greedy(bandit, steps, epsilon, seed=10_000 + p)
R += r; O += o
return R / n_problems, O / n_problems
for eps in (0.0, 0.01, 0.1):
R, O = experiment(eps)
print(f"epsilon={eps:4.2f} mean reward: first 10 steps {R[:10].mean():.2f}, steps 100-199 {R[100:200].mean():.2f}, last 100 steps {R[-100:].mean():.2f}"
f" | optimal arm chosen in the last 100 steps: {100 * O[-100:].mean():.0f}%")
# Output (due esecuzioni consecutive, stessa uscita):
# epsilon=0.00 mean reward: first 10 steps 0.59, steps 100-199 1.02, last 100 steps 1.02 | optimal arm chosen in the last 100 steps: 36%
# epsilon=0.01 mean reward: first 10 steps 0.59, steps 100-199 1.08, last 100 steps 1.30 | optimal arm chosen in the last 100 steps: 59%
# epsilon=0.10 mean reward: first 10 steps 0.54, steps 100-199 1.24, last 100 steps 1.36 | optimal arm chosen in the last 100 steps: 80%
Prima di leggere servono due punti di riferimento, calcolati con la stessa procedura: che cosa otterrebbe un agente perfetto e che cosa uno che sceglie a caso.
# Reference points: what would a perfect agent get? what does a random one get?
best = np.mean([Bandit(seed=p).true_value.max() for p in range(2000)])
average_arm = np.mean([Bandit(seed=p).true_value.mean() for p in range(2000)])
print(f"oracle (always the best arm): {best:.2f} random arm: {average_arm:.2f}")
# Output:
# oracle (always the best arm): 1.53 random arm: -0.00
Un agente onnisciente, che tira sempre la leva migliore, otterrebbe in media 1.53 per tiro; uno che sceglie a caso circa 0 (il segno meno dell'uscita è l'arrotondamento di un valore minuscolo). Ora leggiamo i tre risultati.
- ε = 0 (solo sfruttamento). Nei primi 10 passi è alla pari degli altri (0.59), ma si ferma a circa 1.02 e non migliora più: negli ultimi 100 passi sceglie la leva migliore solo nel 36% dei casi. All'inizio tutte le stime sono 0; la prima leva che dà una ricompensa positiva diventa la favorita e l'agente continua a tirarla, senza mai verificare le altre. Se non ha trovato la migliore nei primi tentativi, non la troverà mai.
- ε = 0.1 (10% di esplorazione). Parte più lentamente (0.54 nei primi 10 passi: sta sprecando tiri a esplorare), ma poi sale di più: 1.24 fra i passi 100-199 e 1.36 alla fine, con la leva ottima scelta nell'80% dei casi. Siamo vicini al massimo di 1.53, e il 10% di esplorazione residua è parte del motivo per cui non lo raggiunge.
- ε = 0.01. Un compromesso: esplora troppo poco per trovare in fretta la leva giusta (1.08 nei passi 100-199) ma alla fine arriva a 1.30 e 59%. Ha bisogno di più tempo per scoprire la leva migliore.
Le tre voci vanno lette come un compromesso nel tempo, non come una classifica. Un'esplorazione elevata costa all'inizio (rinunci a ricompense) ma ripaga dopo (ne trovi di migliori); un'esplorazione bassa guadagna subito e si blocca. Con un orizzonte lungo l'esplorazione vale di più; con un orizzonte di soli 10 tiri non conviene: nei primi 10 passi ε = 0 e ε = 0.01 fanno persino meglio di ε = 0.1 (0.59 e 0.59 contro 0.54). Il dato che il listato non mostra è dove si fermerebbe ε = 0.01 con più di 1000 tiri: la curva è ancora in salita, e non l'ho estesa.
Strategie oltre ε-greedy
ε-greedy esplora a caso, anche quando una leva è già evidentemente pessima. Le alternative correggono proprio questo. I valori iniziali ottimistici partono con Q molto alti, e l'agente prova ogni leva per «disilludersi» (sezione 2.6 del libro). L'UCB (upper confidence bound) sceglie la leva con la stima più alta più un bonus per l'incertezza, quindi le leve poco provate hanno un bonus maggiore (sezione 2.7). I gradient bandit imparano una preferenza per ogni leva invece di un valore (sezione 2.8), e il Thompson sampling campiona da una distribuzione sul valore di ogni leva; il libro ne parla nel riepilogo del capitolo e nelle note bibliografiche. Non ho riprodotto nessuna di queste strategie, quindi non dico quale sia migliore.
Dal banditore a un vero problema di RL
Il banditore ha un limite che lo rende «troppo facile»: la scelta di oggi non cambia la situazione di domani. Tirare la leva 3 non cambia quali leve ci sono né quanto pagano. Nei problemi veri le azioni cambiano lo stato: spostare un pezzo sulla scacchiera cambia la scacchiera, muoversi in un labirinto cambia dove sei. Allora la ricompensa di un'azione può arrivare molti passi dopo, e l'assegnazione del merito diventa il problema centrale. Per descrivere questa situazione serve il processo decisionale di Markov, tema di Processo decisionale di Markov: il linguaggio in cui si scrive ogni problema di RL. Poi viene l'equazione di Bellman (derivata per intero), che permette di calcolare il valore di uno stato, e gli algoritmi che lo imparano dall'esperienza: Q-Learning e, quando gli stati sono troppi per una tabella, Deep Q-Learning. Il rischio di premiare la cosa sbagliata lo affronta Ricompense ritardate, reward shaping e reward hacking.
I compromessi da ricordare
Il primo compromesso è che esplorare costa ora e paga dopo: il valore dell'esplorazione dipende dall'orizzonte, e più è lungo più conviene esplorare. Il secondo riguarda ε: un valore fisso esplora sempre, anche quando ha già imparato, mentre uno decrescente esplora molto all'inizio e poco a regime (lo useremo nel Q-learning). Il terzo riguarda il passo dell'aggiornamento: 1/n converge alla media vera se le ricompense sono stazionarie, mentre un passo fisso segue meglio un mondo che cambia ma non converge esattamente, perché oscilla attorno al valore.
Trappole reali
- Giudicare un agente da un solo episodio o un solo problema. Con ricompense rumorose un risultato ottimo può essere fortuna: qui abbiamo mediato su 2000 problemi.
- Confrontare la ricompensa media con zero invece che con l'oracolo. 1.36 non dice nulla senza sapere che il massimo è 1.53 e il caso 0.
- Non esplorare abbastanza. Il greedy puro si «incastra» (36% di scelta ottima) e non dà nessun errore: sembra funzionare.
- Esplorare per sempre. Con ε = 0.1 il 10% dei tiri è dedicato all'esplorazione anche a regime; in un sistema reale la riduzione dell'esplorazione va pianificata.
- Dimenticare che la ricompensa è l'unico segnale. Ciò che premi è ciò che ottieni: se la ricompensa non cattura l'obiettivo vero, l'agente ottimizzerà un altro obiettivo.
L'ordine delle cinque trappole non è una graduatoria, ma le prime tre hanno una cosa in comune: danno un risultato che sembra buono senza segnalare nessun errore. Un agente greedy che resta al 36% non solleva eccezioni, e un confronto con zero fa sembrare brillante qualunque numero positivo. Le ultime due riguardano invece la progettazione: quanto esplorare e che cosa premiare sono scelte tue, e nessun algoritmo le fa al posto tuo.
Un esempio svolto a mano: cinque tiri di un bandit a tre leve
Seguiamo l'algoritmo ε-greedy con ε = 0.1 su tre leve, partendo da Q = [0, 0, 0] e N = [0, 0, 0]. Le ricompense qui sotto le ho scelte io per la dimostrazione (un caso, non un risultato sperimentale); l'aritmetica dei cinque aggiornamenti l'ho ricontrollata con un breve script che applica la formula incrementale.
tiro 1: sfrutta; tutti i Q sono 0 → pareggio, si sceglie la leva 0. Ricompensa 1.2
N0 = 1, Q0 = 0 + (1.2 − 0)/1 = 1.2
tiro 2: sfrutta; il Q massimo è la leva 0 (1.2). Ricompensa 0.4
N0 = 2, Q0 = 1.2 + (0.4 − 1.2)/2 = 0.8
tiro 3: ESPLORA (estrazione a caso): leva 2. Ricompensa 2.0
N2 = 1, Q2 = 0 + (2.0 − 0)/1 = 2.0
tiro 4: sfrutta; il Q massimo è la leva 2 (2.0). Ricompensa 1.0
N2 = 2, Q2 = 2.0 + (1.0 − 2.0)/2 = 1.5
tiro 5: sfrutta; leva 2 (1.5). Ricompensa 1.8
N2 = 3, Q2 = 1.5 + (1.8 − 1.5)/3 = 1.6
stato finale: Q = [0.8, 0, 1.6], N = [2, 0, 3]
Tre osservazioni. Primo, le stime si correggono a ogni tiro: dopo il secondo tiro la leva 0 vale 0.8 e non più 1.2. Secondo, la leva 1 non è mai stata provata: il suo Q = 0 è solo il valore iniziale, non una stima. Se l'agente fosse greedy puro e le altre leve avessero ricompense positive, non la proverebbe mai: è esattamente il blocco del caso ε = 0. Terzo, la scelta esplorativa del tiro 3 è stata fortunata e ha trovato la leva migliore fra quelle viste: ecco il valore dell'esplorazione.
Esercizi con soluzione
Prima di andare avanti, prova a risolvere questi esercizi con carta e matita; la soluzione è subito sotto ciascuno.
- Una leva ha dato 2, 4, 3, 7. Calcola
Qcon l'aggiornamento incrementale.
Soluzione: dopo 2: 2. Dopo 4:2 + (4 − 2)/2 = 3. Dopo 3:3 + (3 − 3)/3 = 3. Dopo 7:3 + (7 − 3)/4 = 4, cioè la media. - Con ε = 0.2 e 5 leve, se l'agente conosce la leva migliore, con che probabilità la sceglie?
Soluzione:0.8 + 0.2/5 = 0.84: il 20% di esplorazione sceglie a caso fra 5 leve, e in 1 caso su 5 cade sulla migliore. - Con ε = 0.1 e 10 leve, quale frazione massima di scelte può essere ottima, anche a problema risolto?
Soluzione:0.9 + 0.1/10 = 0.91. L'80% misurato nell'esperimento è sotto questo tetto, ma l'agente non ha ancora identificato la leva migliore in tutti i 2000 problemi. - Perché con ε = 0 un agente può restare bloccato?
Soluzione: sceglie sempre la leva con ilQpiù alto; se la prima con ricompensa positiva non è la migliore, non verifica mai le altre. - Se le ricompense cambiano nel tempo, che cosa cambi nell'aggiornamento?
Soluzione: sostituisci1/ncon un passo costanteα: le ricompense recenti pesano di più e le stime «dimenticano» il passato.
I primi due esercizi sono aritmetica da fare a mano e si ricontrollano con una riga di Python (le soluzioni qui sopra sono state ricalcolate così). Gli ultimi tre sono domande di interpretazione: se sai rispondere senza rileggere il testo hai capito la sostanza, cioè che l'esplorazione è un costo pagato per ottenere informazione, e che quel costo ha un tetto calcolabile.
Domande frequenti
Che cos'è il «rimpianto» (regret)?
La differenza fra la ricompensa che avresti ottenuto sempre con la leva migliore e quella ottenuta davvero. Nel nostro esperimento il massimo è 1.53 per tiro: un agente a 1.36 ha un rimpianto medio di circa 0.17 per tiro negli ultimi 100 passi. Misurare il rimpianto cumulativo è un modo usuale di confrontare algoritmi per bandit.
Perché non provare ogni leva 100 volte e poi sfruttare?
È la strategia «prima esplora, poi sfrutta». Funziona, ma spreca tiri sulle leve evidentemente cattive. L'ε-greedy distribuisce l'esplorazione nel tempo; strategie come UCB la concentrano sulle leve incerte.
Che cosa cambia se le ricompense variano nel tempo?
Si usa un passo costante α invece di 1/n, così le stime dimenticano il passato; la scelta di α bilancia reattività e rumore, come vedrai nel Q-Learning.
In che cosa il bandit è diverso da un test A/B?
Un test A/B assegna i partecipanti a gruppi fissi e decide alla fine; un bandit sposta gradualmente il traffico verso l'opzione migliore durante l'esperimento. Il compromesso è fra velocità del guadagno e affidabilità statistica della conclusione.
ε = 0.1 va bene per tutto?
No: è un valore tipico, non una regola. Dipende dall'orizzonte (più lungo, più esplorazione conviene) e dal costo di sbagliare; spesso ε decresce nel tempo.
Che cosa sai fare adesso
Sai descrivere un problema di RL con agente, ambiente, stato, azione, ricompensa e ritorno, e sai dire perché non è apprendimento supervisionato. Sai ricavare la media incrementale Q ← Q + passo·(bersaglio − Q), la forma che ritorna in ogni algoritmo successivo. E hai un esperimento riproducibile: nel banditore a 10 bracci, su 2000 problemi, ε = 0 si blocca al 36% di scelte ottime (ricompensa 1.02), ε = 0.01 arriva al 59% (1.30) e ε = 0.1 all'80% (1.36), contro un massimo di 1.53. Quando le azioni cambiano la situazione serve il formalismo di Processo decisionale di Markov, il prossimo passo della serie. Se ti serve il quadro sul rischio di valutare un modello da una sola prova, rileggi il processo di un progetto di ML.
Nella serie, la lezione precedente è VAE: il peso beta, il collasso del posterior e le instabilità dell'addestramento.
Fonti
- Sutton e Barto, Reinforcement Learning: An Introduction, seconda edizione (PDF dell'autore) - capitolo 2: setup del 10-armed testbed (sezione 2.3), aggiornamento incrementale (2.4), valori ottimistici, UCB e gradient bandit (2.6-2.8), Thompson sampling nel riepilogo; testo scaricato e letto nei passi citati, consultato il 2026-11-12
- NumPy, Random Generator -
default_rngcome costruttore consigliato del generatore, consultata il 2026-11-12 - NumPy, numpy.argmax - in caso di parità viene restituita la prima occorrenza, consultata il 2026-11-12
- Gymnasium, Basic Usage - descrizione del ciclo agente-ambiente e della classe
Env, consultata il 2026-11-12
Commenti
Caricamento commenti...
Accedi per commentare