Forstærkningslæring m / Keras + OpenAI: Det grundlæggende

Forstærkningslæring er af mange blevet indrømmet som en af ​​de gateway-teknologier / -koncepter, der er fremkommet fra de teoretiske studier af maskinlæring. Vi gennemgår et meget hurtigt overblik over forstærkningslæring, inden vi dykker ned i koden.

Hurtig baggrund

Forstærkningslæring (RL) er et generelt paraplybegrep for enhver algoritme, der ikke kræver eksplicitte datapar og deres tilsvarende ønskede mærker, som det er tilfældet i traditionel overvåget læring, men kræver en numerisk indikation af "hvordan en prøve er." Denne kvalitet af prøvenes "godhed" har ingen mening i en absolut forstand. Du kan forestille dig dette generisk som din score i et videospil. Hvis skærmen viser en score på “218”, der formodentlig ikke har nogen betydning for dig, spilleren, medmindre du er opmærksom på, hvor vanskeligt eller let det er at tjene et point, og hvilken score du starter med. Og det er dybest set omfanget af baggrund, vi vil gå i dybden med: der vil være mere dybdegående diskussioner af RL i fremtiden, men koden, vi gennemgår i dette indlæg, er et meget grundlæggende eksempel på RL, og det indebærer ikke nogen yderligere indblanding i fagteorien.

Keras-noter

Velkommen til alle, der lige er kommet i gang med AI / ML-programmering! Marken er vokset så meget i de sidste år, at det er ganske overvældende at hoppe ind lige nu. Men der er stadig masser af tid til at blive involveret og lære på dette massive felt! I tråd med det er Keras det bibliotek, jeg primært vil bruge til mine kommende tutorials, inklusive denne. Keras er i det væsentlige et indpakningsbibliotek til Tensorflow og Theano. Dens grænseflade er meget lig den, der er udsat af tflearn, men er lidt mere generisk i dens anvendelighed til Theano som backend. Vær dog opmærksom! Dimensionerne i Theano er lidt forskellige fra dem i Tensorflow. Jeg vil derfor anbefale at justere din Keras til at bruge TF som dens backend for at undgå frustrationer med dimensioner fremad (skal være standard, når du installerer, hvis du allerede har TF installeret).

Du kan også lige så let gøre dette med TF, men Keras giver os den dejlige fleksibilitet, når vi kommer i gang med ikke at skulle holde styr på dimensioner gennem vindinger og alt det skidt. I hvert fald nok ord: tid til at flytte til koden!

Kode

Vi vil udforske det mest basale OpenAI-miljø her: CartPole! Som en sidste hurtig note kan du finde vejledningen til at installere OpenAIs gymnastikpakke her: https://gym.openai.com/docs. Bare det at køre “sudo pip install gym” bør fungere på de fleste platforme.

CartPole-miljøet har en meget enkel forudsætning: afbalancer polens stang.

Dataindsamling

Den første del af ethvert maskinlæringsproblem er indsamling af data, og denne er ikke anderledes. Heldigvis giver OpenAIs gymmiljø en meget ligetil måde at indsamle data på: vi kan i det væsentlige bare køre gennem simuleringen mange gange og tage tilfældige trin hver gang. OpenAI Gym-miljøer er struktureret omkring to hoveddele: et observationsrum og et handlingsrum. Vi observerer førstnævnte fra miljøet og bruger det til at bestemme, hvordan man bedst kan opdatere den gennem en handling, dvs. baseret på polens aktuelle tilstand (observation), bestemme, om vognen skal flyttes til venstre eller højre (handling).

Som et resultat er vi nødt til at tage en handling, der passer til omfanget af de tilladte handlinger i handlingsrummet, som er i størrelse 2 i dette tilfælde (venstre eller højre). Vi tager udgangsrummet til at være kodet med en hot, grunden til at vi ønsker, at det neurale net til sidst forudsiger sandsynligheden for at bevæge sig mod venstre mod højre i betragtning af den aktuelle tilstand af miljøet. I dette tilfælde kunne vi slippe af med bare at have output en enkelt 1x1 flydende matrix (dvs. en skalar) og runde den for vores endelige resultat, men den en-hot kodningspraksis kan anvendes mere vidtgående.

Så for at samle handlingerne og de tilsvarende observationer kan en første tanke simpelthen være:

for _ inden for rækkevidde (10000):
    observation = env.reset ()
    training_sampleX, training_sampleY = [], []
    for trin inden for rækkevidde (sim_steps):
        handling = np.random.randint (0, 2)
        one_hot_action = np.zeros (2)
        one_hot_action [handling] = 1
        training_sampleX.append (observation)
        training_sampleY.append (one_hot_action)
        
        observation, belønning, udført, _ = env.step (handling)
        hvis gjort:
            pause
    trainingX + = training_sampleX
    trainingY + = training_sampleY

Men hvis vi skulle træne på dette, ville den endelige prediktor sandsynligvis ikke gøre bedre end tilfældig chance. Når alt kommer til alt "affald ind, affald ud": vi ville ikke gøre mere end at fodre det neurale net en samling af både gode og dårlige prøver og forvente, at det udelukkende lærer af det gode. Hvis vi tager et skridt tilbage, er dette imidlertid fuldstændig usandsynligt, da en enkelt prøve ikke kan skelnes fra andre endog sammenlignes dem, der kommer fra gode forsøg, og dem fra dårlige forsøg.

Så i stedet vil vi kun se på de prøver, der resulterer i forsøg med høj score. Det vil sige, vi ønsker at filtrere prøverne for kun at tillade de, der til sidst resulterer i høje score i deres forsøg. I dette tilfælde valgte vi vilkårligt 50 at være den "minimale cutoff" for at betragtes som en "god prøve" og kun vælge disse prøver:

def collect_data (env):
    min_score = 50
    sim_steps = 500
    trainingX, trainingY = [], []
    scoringer = []
    for _ inden for rækkevidde (10000):
        observation = env.reset ()
        score = 0
        training_sampleX, training_sampleY = [], []
        for trin inden for rækkevidde (sim_steps):
            handling = np.random.randint (0, 2)
            one_hot_action = np.zeros (2)
            one_hot_action [handling] = 1
            training_sampleX.append (observation)
            training_sampleY.append (one_hot_action)
            
            observation, belønning, udført, _ = env.step (handling)
            score + = belønning
            hvis gjort:
                pause
        hvis score> min_score:
            scores.append (score)
            trainingX + = training_sampleX
            trainingY + = training_sampleY
    trainingX, trainingY = np.array (trainingX), np.array (trainingY)
    print ("Gennemsnit: {}". format (np.mean (scores)))
    print ("Median: {}". format (np.median (scores)))
    returtræningX, træningY

Model Definition

Nu hvor vi har dataene, er vi nødt til at gå i gang med at definere modellen. Før du gør noget med maskinlæringsproblemer, er det altid værd at gå tilbage for at overveje, hvad det er, vi modellerer, nærmere bestemt hvad de forventede input og de ønskede resultater er. I vores tilfælde modtager vi den aktuelle miljøtilstand (dvs. "observationer" fra før) og ønsker at forudsige sandsynligheden for at bevæge sig i hver af de to retninger. Fra dette kan vi nemt finde ud af, hvilken af ​​de to der skal tages ved at tage max arg.

Modellen, vi bruger her, er en meget enkel model: flere fuldt forbundne lag (f.eks. Tætte lag i Keras). Dette er ofte de sidste lag, der bruges i dybe CNN'er (Convolution Neural Networks), da det er dem, der kombinerer alle funktionskort eller inputlag til de endelige skalarværdier. Helt tilsluttede lag udgør i det væsentlige rygraden i neurale netværk og er det, der giver dem mulighed for effektivt at kortlægge højdimensionelle funktioner, idet man ignorerer alle moderne forbedringer med vindinger, LSTM'er, dropout osv.….

Den eneste af disse forbedringer, der er relevant her, er Dropout, da det hjælper med at sikre, at vi ikke overpasser træningsdataene. Så vi sandwich grundlæggende et Dropout-lag mellem hver fuldt tilsluttet kortlægning for at sikre, at intet lag af kortlægningen bliver afhængig af noget lille undergruppe af forbindelser, der specifikt er synlige i træningsdataene.

Endelig er vi nødt til at bestemme den tabsfunktion, som vi træner mod. Da vi kodede outputrummet som en en-varm 2D-vektor, bliver det naturlige valg kategorisk krydsantropi, i betragtning af at vi ønsker at identificere resultatet som enten venstre ([1,0]) eller højre ([0,1]). Jeg vil ikke gå i dybden om, hvad krydsentropi indebærer, men det er en meget værdifuld funktion at forstå, i betragtning af dens udbredelse i disse slags problemer. Fra et højt niveau er tværantropien, givet to fordelinger (en sand underliggende distribution og vores model deraf), et mål for, hvor meget information vi har brug for for at overføre noget, der trækkes fra den rigtige distribution ved modelfordelingen.

Derfor definerer vi modellen som:

fra keras.models import Sequential
fra keras.layers importerer Dense, Dropout
def create_model ():
    model = Sekventiel ()
    model.add (Tæt (128, input_shape = (4,), aktivering = "relu"))
    model.add (Dropout (0,6))
    
    model.add (Tæt (256, aktivering = "relu"))
    model.add (Dropout (0,6))
    
    model.add (tæt (512, aktivering = "relu"))
    model.add (Dropout (0,6))
    
    model.add (Tæt (256, aktivering = "relu"))
    model.add (Dropout (0,6))
    
    model.add (Tæt (128, aktivering = "relu"))
    model.add (Dropout (0,6))
    model.add (Tæt (2, aktivering = "softmax"))
    
    model.compile (
        tab = "categorical_crossentropy",
        optimizer = "adam",
        metrics = [ "nøjagtighed"])
    retur model

Et par mere subtile tekniske punkter i modellen: Hvert af lagene i modellen har ReLU-aktiveringer, så modellen kan træne hurtigere end med mættede aktiveringsfunktioner, såsom tanh og sigmoid. Modellen vil sandsynligvis også træne i disse tilfælde, men det vil tage langt længere tid at konvergere end hvis man bruger ReLU-aktivering.

Forudsigelse

Derfra kan vi simpelthen få vores træningsdata, træne modellen og itereere gennem flere forsøg for at se, hvor godt vores model klarer sig!

import gym
import numpy som np
fra dataimport samle_data
fra modelimport create_model
def forudsige ():
    env = gym.make ("CartPole-v0")
    trainingX, trainingY = gather_data (env)
    model = create_model ()
    model.fit (træningX, træningY, epoker = 5)
    
    scoringer = []
    num_trials = 50
    sim_steps = 500
    til prøve inden for rækkevidde (antal prøver):
        observation = env.reset ()
        score = 0
        for trin inden for rækkevidde (sim_steps):
            handling = np.argmax (model.predict (
                observation.reshape (1,4)))
            observation, belønning, udført, _ = env.step (handling)
            score + = belønning
            hvis gjort:
                pause
        scores.append (score)
    
    print (np.mean (scores))

Fuld kode

Med det trin for trin, her er den komplette kildekode til OpenAI Cartpole Keras implementering!

Hold øje med den næste Keras + OpenAI-tutorial!

Kommenter og klik på det nedenfor for at vise support!