Metodi Monte Carlo basati su Dataproc e Apache Spark


Dataproc e Apache Spark forniscono infrastruttura e capacità che puoi utilizzare per eseguire le simulazioni Monte Carlo scritte in Java, Python o Scala.

I metodi di Monte Carlo possono aiutare a rispondere a una vasta gamma di domande in ambito aziendale, ingegneristico, scientifico, matematico e altri campi. Utilizzando il campionamento casuale ripetuto per creare una distribuzione di probabilità per una variabile, una simulazione Monte Carlo può fornire risposte a domande a cui altrimenti non sarebbe possibile rispondere. In ambito finanziario, ad esempio, la determinazione dei prezzi di un'opzione di capitale richiede l'analisi delle migliaia di modi in cui il prezzo del titolo può cambiare nel tempo. I metodi di Monte Carlo forniscono un modo per simulare le variazioni di prezzo delle azioni su una vasta gamma di possibili risultati, mantenendo il controllo sul dominio di eventuali input per il problema.

In passato, l'esecuzione di migliaia di simulazioni poteva richiedere molto tempo e accumulare costi elevati. Dataproc consente di eseguire il provisioning della capacità on demand e di pagarla al minuto. Apache Spark ti consente di utilizzare cluster di decine, centinaia o migliaia di server per eseguire simulazioni in modo intuitivo e scalabile per soddisfare le tue esigenze. Ciò significa che puoi eseguire più simulazioni più rapidamente, aiutando la tua azienda a innovare più rapidamente e gestire meglio i rischi.

La sicurezza è sempre importante quando si utilizzano i dati finanziari. Dataproc viene eseguito su Google Cloud, che contribuisce a mantenere i tuoi dati sicuri, protetti e privati in diversi modi. Ad esempio, tutti i dati vengono criptati durante la trasmissione e quando sono inattivi e Google Cloud è conforme a ISO 27001, SOC3 e PCI.

Obiettivi

  • Crea un cluster Dataproc gestito con Apache Spark preinstallato.
  • Esegui una simulazione Monte Carlo utilizzando Python che stima la crescita di un portafoglio di titoli nel tempo.
  • Esegui una simulazione di Monte Carlo utilizzando Scala per simulare le entrate di un casinò.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono essere idonei a una prova senza costi aggiuntivi.

Una volta completate le attività descritte in questo documento, puoi evitare la fatturazione continua eliminando le risorse che hai creato. Per ulteriori informazioni, consulta la pagina Pulizia.

Prima di iniziare

  • Configura un progetto Google Cloud
    1. Accedi al tuo account Google Cloud. Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti gratuiti per l'esecuzione, il test e il deployment dei carichi di lavoro.
    2. Nella console di Google Cloud Console, nella pagina del selettore dei progetti, seleziona o crea un progetto Google Cloud.

      Vai al selettore progetti

    3. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

    4. Abilita le API Dataproc and Compute Engine.

      Abilita le API

    5. Installa Google Cloud CLI.
    6. Per inizializzare l'interfaccia a riga di comando gcloud, esegui il comando seguente:

      gcloud init
    7. Nella console di Google Cloud Console, nella pagina del selettore dei progetti, seleziona o crea un progetto Google Cloud.

      Vai al selettore progetti

    8. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

    9. Abilita le API Dataproc and Compute Engine.

      Abilita le API

    10. Installa Google Cloud CLI.
    11. Per inizializzare l'interfaccia a riga di comando gcloud, esegui il comando seguente:

      gcloud init

Creazione di un cluster Dataproc

Segui i passaggi per creare un cluster Dataproc dalla console Google Cloud. Le impostazioni predefinite del cluster, che includono i nodi a due worker, sono sufficienti per questo tutorial.

Disattivazione del logging per gli avvisi in corso...

Per impostazione predefinita, Apache Spark stampa i log dettagliati nella finestra della console. Ai fini di questo tutorial, modifica il livello di logging in modo da registrare solo gli errori. Segui questi passaggi:

Usa ssh per connetterti al nodo primario del cluster Dataproc

Il nodo principale del cluster Dataproc ha il suffisso -m sul nome della VM.

  1. Nella console Google Cloud, vai alla pagina Istanze VM.

    Vai a Istanze VM

  2. Nell'elenco delle istanze di macchine virtuali, fai clic su SSH nella riga dell'istanza a cui vuoi connetterti.

    Pulsante SSH accanto al nome dell'istanza.

Si apre una finestra SSH connessa al nodo primario.

Connected, host fingerprint: ssh-rsa 2048 ...
...
user@clusterName-m:~$

Modifica l'impostazione di logging

  1. Dalla home directory del nodo principale, modifica /etc/spark/conf/log4j.properties.

    sudo nano /etc/spark/conf/log4j.properties
    
  2. Imposta log4j.rootCategory su ERROR.

    # Set only errors to be logged to the console
    log4j.rootCategory=ERROR, console
    log4j.appender.console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.target=System.err
    log4j.appender.console.layout=org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
    
  3. Salva le modifiche ed esci dall'editor. Se vuoi attivare nuovamente il logging dettagliato, annulla la modifica ripristinando il valore per .rootCategory al valore originale (INFO).

Linguaggi di programmazione Spark

Spark supporta Python, Scala e Java come linguaggi di programmazione per applicazioni autonome e fornisce interpreti interattivi per Python e Scala. La lingua scelta è una questione di preferenza personale. Questo tutorial utilizza gli interpreti interattivi perché puoi sperimentare modificando il codice, provando valori di input diversi e visualizzando i risultati.

Stima della crescita del portafoglio

In ambito finanziario, i metodi di Monte Carlo a volte vengono utilizzati per eseguire simulazioni che mirano a prevedere il rendimento di un investimento. Producendo campioni casuali di risultati in una serie di probabili condizioni di mercato, una simulazione di Monte Carlo può rispondere a domande relative alle prestazioni medie di un portafoglio o in scenari più peggiori.

Segui questi passaggi per creare una simulazione che utilizza i metodi Monte Carlo per provare a stimare la crescita di un investimento finanziario in base ad alcuni fattori di mercato comuni.

  1. Avvia l'interprete Python dal nodo primario Dataproc.

    pyspark
    

    Attendi la richiesta di Spark >>>.

  2. Inserisci il seguente codice. Assicurati di mantenere il rientro nella definizione della funzione.

    import random
    import time
    from operator import add
    
    def grow(seed):
        random.seed(seed)
        portfolio_value = INVESTMENT_INIT
        for i in range(TERM):
            growth = random.normalvariate(MKT_AVG_RETURN, MKT_STD_DEV)
            portfolio_value += portfolio_value * growth + INVESTMENT_ANN
        return portfolio_value
    
  3. Premi return finché non visualizzi di nuovo la richiesta di Spark.

    Il codice precedente definisce una funzione che modella cosa può succedere quando un investitore dispone di un conto di pensionamento già investito nel mercato azionario, a cui aggiunge ulteriore denaro ogni anno. La funzione genera un ritorno sull'investimento casuale, in percentuale, ogni anno per la durata di un periodo di tempo specificato. La funzione assume un valore seed come parametro. Questo valore viene utilizzato per recuperare il generatore di numeri casuali, che garantisce che la funzione non riceva lo stesso elenco di numeri casuali ogni volta che viene eseguito. La funzione random.normalvariate assicura che vengano distribuiti valori casuali in una distribuzione normale per la deviazione standard e la media specificata. La funzione aumenta il valore del portafoglio in base all'importo di crescita, che potrebbe essere positivo o negativo, e aggiunge una somma annuale che rappresenta un ulteriore investimento.

    Tu definisci le costanti richieste in un passaggio successivo.

  4. Crea molti semi per dare la funzione. Nella richiesta Spark, inserisci il codice seguente, che genera 10.000 semi:

    seeds = sc.parallelize([time.time() + i for i in range(10000)])
    

    Il risultato dell'operazione parallelize è un set di dati distribuito flessibile (RDD), ovvero una raccolta di elementi ottimizzati per l'elaborazione in parallelo. In questo caso, il RDD contiene seed che si basano sull'ora di sistema corrente.

    Durante la creazione di RDD, Spark taglia i dati in base al numero di worker e di core disponibili. In questo caso, Spark sceglie di utilizzare otto sezioni, una sezione per ciascun core. Questo va bene per questa simulazione, che ha 10.000 dati. Per le simulazioni più ampie, ogni sezione potrebbe superare il limite predefinito. In questo caso, se specifichi un secondo parametro a parallelize, puoi aumentare le sezioni di numero, il che può contribuire a mantenere la dimensione di ciascuna sezione da gestire, mentre Spark continua a sfruttare tutti e otto i core.

  5. Fornisci alla funzione di crescita il valore RDD che contiene i semi.

    results = seeds.map(grow)
    

    Il metodo map passa ogni seed nel RDD alla funzione grow e aggiunge ogni risultato a un nuovo RDD, che viene memorizzato in results. Tieni presente che questa operazione, che esegue una trasformazione, non produce immediatamente i risultati. Spark non funzionerà finché i risultati non saranno necessari. Questa valutazione lazy è il motivo per cui puoi inserire il codice senza definire le costanti.

  6. Specifica alcuni valori per la funzione.

    INVESTMENT_INIT = 100000  # starting amount
    INVESTMENT_ANN = 10000  # yearly new investment
    TERM = 30  # number of years
    MKT_AVG_RETURN = 0.11 # percentage
    MKT_STD_DEV = 0.18  # standard deviation
    
  7. Chiama reduce per aggregare i valori nel RDD. Inserisci il codice seguente per sommare i risultati dell'RDD:

    sum = results.reduce(add)
    
  8. Stima e visualizza il ritorno medio:

    print (sum / 10000.)
    

    Assicurati di includere il punto (.) alla fine. Indica l'aritmetica in virgola mobile.

  9. Ora cambia un'ipotesi e scopri come cambiano i risultati. Ad esempio, puoi inserire un nuovo valore per il ritorno medio sul mercato:

    MKT_AVG_RETURN = 0.07
    
  10. Esegui di nuovo la simulazione.

    print (sc.parallelize([time.time() + i for i in range(10000)]) \
            .map(grow).reduce(add)/10000.)
    
  11. Al termine dell'esperimento, premi CTRL+D per uscire dall'interprete Python.

Programmazione di una simulazione Monte Carlo a Scala

Monte Carlo, ovviamente, è famosa come meta di giochi e scommesse. In questa sezione, utilizzerai Scala per creare una simulazione che modella il vantaggio matematico di cui un casinò usufruisce in un gioco d'azzardo. Il "bordo della casa" in un casinò reale varia molto da un gioco all'altro; ad esempio, può superare il 20% in keno. Questo tutorial crea un gioco semplice in cui la casa ha un solo vantaggio percentuale. Ecco come funziona il gioco:

  • Il giocatore effettua una scommessa, composta da una serie di fiches da un fondo di rollback.
  • Il giocatore tira un dado a 100 facce (quanto sarebbe bello?).
  • Se il risultato del tiro è un numero compreso tra 1 e 49, il giocatore vince.
  • Per i risultati da 50 a 100, il giocatore perde la scommessa.

Come vedi, questo gioco crea uno svantaggio di una percentuale per il giocatore: in 51 dei 100 possibili risultati per ogni tiro, il giocatore perde.

Segui questi passaggi per creare ed eseguire il gioco:

  1. Avvia l'interprete di Scala dal nodo primario Dataproc.

    spark-shell
    
  2. Copia e incolla il codice seguente per creare il gioco. Scala non ha gli stessi requisiti di Python per il rientro, quindi puoi semplicemente copiare e incollare questo codice al prompt di scala>.

    val STARTING_FUND = 10
    val STAKE = 1   // the amount of the bet
    val NUMBER_OF_GAMES = 25
    
    def rollDie: Int = {
        val r = scala.util.Random
        r.nextInt(99) + 1
    }
    
    def playGame(stake: Int): (Int) = {
        val faceValue = rollDie
        if (faceValue < 50)
            (2*stake)
        else
            (0)
    }
    
    // Function to play the game multiple times
    // Returns the final fund amount
    def playSession(
       startingFund: Int = STARTING_FUND,
       stake: Int = STAKE,
       numberOfGames: Int = NUMBER_OF_GAMES):
       (Int) = {
    
        // Initialize values
        var (currentFund, currentStake, currentGame) = (startingFund, 0, 1)
    
        // Keep playing until number of games is reached or funds run out
        while (currentGame <= numberOfGames && currentFund > 0) {
    
            // Set the current bet and deduct it from the fund
            currentStake = math.min(stake, currentFund)
            currentFund -= currentStake
    
            // Play the game
            val (winnings) = playGame(currentStake)
    
            // Add any winnings
            currentFund += winnings
    
            // Increment the loop counter
            currentGame += 1
        }
        (currentFund)
    }
    
  3. Premi return finché non viene visualizzato il messaggio scala>.

  4. Inserisci il seguente codice per giocare 25 volte, che è il valore predefinito per NUMBER_OF_GAMES.

    playSession()
    

    Il tuo rollroll ha iniziato con un valore di 10 unità. È più alto o più basso ora?

  5. Ora simula 10.000 giocatori che scommettono 100 chips a gioco. Prova 10.000 giochi in una sessione. Questa simulazione Monte Carlo calcola la probabilità di perdere tutto il denaro prima della fine della sessione. Inserisci il seguente codice:

    (sc.parallelize(1 to 10000, 500)
      .map(i => playSession(100000, 100, 250000))
      .map(i => if (i == 0) 1 else 0)
      .reduce(_+_)/10000.0)
    

    Nota che la sintassi .reduce(_+_) è un'abbreviazione di Scala per l'aggregazione mediante l'uso di una funzione di somma. È funzionalmente equivalente alla sintassi .reduce(add) che hai visto nell'esempio Python.

    Il codice precedente esegue questi passaggi:

    • Crea un RDD con i risultati della riproduzione della sessione.
    • Sostituisce i risultati falliti dei giocatori con il numero 1 e i risultati diversi da zero con il numero 0.
    • Somma il numero di giocatori falliti.
    • Divide il conteggio in base al numero di giocatori.

    Un risultato tipico potrebbe essere:

    0.998
    

    Ciò garantisce quasi la perdita di tutti i soldi, nonostante il casinò abbia un solo vantaggio dell'1%.

Esegui la pulizia

Elimina il progetto

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Passaggi successivi

  • Per saperne di più sull'invio di job Spark a Dataproc senza dover utilizzare ssh per connetterti al cluster, leggi Dataproc—Invia un job