Métodos de Montecarlo con Dataproc y Apache Spark


Dataproc y Apache Spark proporcionan la infraestructura y la capacidad que puedes usar para ejecutar simulaciones de Montecarlo escritas en Java, Python o Scala.

Los métodos de Monte Carlo pueden ayudar a responder a una amplia gama de preguntas en los ámbitos de los negocios, la ingeniería, la ciencia, las matemáticas y otros. Al usar un muestreo aleatorio repetido para crear una distribución de probabilidad de una variable, una simulación de Monte Carlo puede proporcionar respuestas a preguntas que, de otro modo, serían imposibles de responder. En finanzas, por ejemplo, para fijar el precio de una opción sobre acciones, es necesario analizar las miles de formas en las que el precio de las acciones podría cambiar con el tiempo. Los métodos de Monte Carlo permiten simular esos cambios en el precio de las acciones en una amplia gama de resultados posibles, al tiempo que se mantiene el control sobre el dominio de las entradas posibles del problema.

Antes, ejecutar miles de simulaciones podía llevar mucho tiempo y generar costes elevados. Dataproc te permite aprovisionar capacidad bajo demanda y pagar por ella por minutos. Apache Spark te permite usar clústeres de decenas, cientos o miles de servidores para ejecutar simulaciones de forma intuitiva y escalable para satisfacer tus necesidades. Esto significa que puedes realizar más simulaciones más rápido, lo que puede ayudar a tu empresa a innovar más rápido y a gestionar mejor los riesgos.

La seguridad es siempre importante cuando se trabaja con datos financieros. Dataproc se ejecuta en Google Cloud, lo que ayuda a mantener tus datos seguros, protegidos y privados de varias formas. Por ejemplo, todos los datos se cifran durante la transmisión y cuando están en reposo, y Google Cloud cumple las normas ISO 27001, SOC3 y PCI.

Objetivos

  • Crea un clúster de Dataproc gestionado con Apache Spark preinstalado.
  • Ejecuta una simulación de Monte Carlo con Python que estime el crecimiento de una cartera de acciones a lo largo del tiempo.
  • Ejecuta una simulación de Montecarlo con Scala que simule cómo gana dinero un casino.

Costes

En este documento, se utilizan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Los usuarios nuevos Google Cloud pueden disfrutar de una prueba gratuita.

Cuando termines las tareas que se describen en este documento, puedes evitar que se te siga facturando eliminando los recursos que has creado. Para obtener más información, consulta la sección Limpiar.

Antes de empezar

  • Configurar un Google Cloud proyecto
    1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
    2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

      Roles required to select or create a project

      • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
      • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

      Go to project selector

    3. Verify that billing is enabled for your Google Cloud project.

    4. Enable the Dataproc and Compute Engine APIs.

      Roles required to enable APIs

      To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

      Enable the APIs

    5. Install the Google Cloud CLI.

    6. Si utilizas un proveedor de identidades (IdP) externo, primero debes iniciar sesión en la CLI de gcloud con tu identidad federada.

    7. Para inicializar gcloud CLI, ejecuta el siguiente comando:

      gcloud init
    8. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

      Roles required to select or create a project

      • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
      • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

      Go to project selector

    9. Verify that billing is enabled for your Google Cloud project.

    10. Enable the Dataproc and Compute Engine APIs.

      Roles required to enable APIs

      To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

      Enable the APIs

    11. Install the Google Cloud CLI.

    12. Si utilizas un proveedor de identidades (IdP) externo, primero debes iniciar sesión en la CLI de gcloud con tu identidad federada.

    13. Para inicializar gcloud CLI, ejecuta el siguiente comando:

      gcloud init
    14. Crear una agrupación Dataproc

      Sigue los pasos para crear un clúster de Dataproc desde la consola de Google Cloud . Los ajustes predeterminados del clúster, que incluyen dos nodos de trabajo, son suficientes para este tutorial.

      Inhabilitar el registro de advertencias

      De forma predeterminada, Apache Spark imprime registros detallados en la ventana de la consola. En este tutorial, cambia el nivel de registro para que solo se registren los errores. Sigue estos pasos:

      Usa ssh para conectarte al nodo principal del clúster de Dataproc

      El nodo principal del clúster de Dataproc tiene el sufijo -m en el nombre de su VM.

      1. In the Google Cloud console, go to the VM instances page.

        Go to VM instances

      2. In the list of virtual machine instances, click SSH in the row of the instance that you want to connect to.

        SSH button next to instance name.

      Se abrirá una ventana de SSH conectada al nodo principal.

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

      Cambiar el ajuste de registro

      1. En el directorio principal del nodo principal, edita /etc/spark/conf/log4j.properties.

        sudo nano /etc/spark/conf/log4j.properties
        
      2. Asigna el valor ERROR a log4j.rootCategory.

        # 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. Guarda los cambios y cierra el editor. Si quieres volver a habilitar el registro detallado, revierte el cambio restaurando el valor de .rootCategory a su valor original (INFO).

      Lenguajes de programación de Spark

      Spark admite Python, Scala y Java como lenguajes de programación para aplicaciones independientes y proporciona intérpretes interactivos para Python y Scala. El idioma que elijas es una cuestión de preferencia personal. En este tutorial se usan intérpretes interactivos porque puedes experimentar cambiando el código, probando diferentes valores de entrada y viendo los resultados.

      Estimar el crecimiento de la cartera

      En el ámbito de las finanzas, los métodos de Monte Carlo se usan a veces para realizar simulaciones que intentan predecir el rendimiento de una inversión. Al generar muestras aleatorias de resultados en una serie de condiciones de mercado probables, una simulación de Montecarlo puede responder a preguntas sobre el rendimiento medio de una cartera o en el peor de los casos.

      Sigue estos pasos para crear una simulación que use métodos de Monte Carlo para intentar estimar el crecimiento de una inversión financiera en función de algunos factores comunes del mercado.

      1. Inicia el intérprete de Python desde el nodo principal de Dataproc.

        pyspark
        

        Espera a que aparezca la petición de Spark >>>.

      2. Introduce el siguiente código. Asegúrate de mantener la sangría en la definición de la función.

        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. Pulsa return hasta que vuelva a aparecer la petición de Spark.

        El código anterior define una función que modeliza lo que podría ocurrir cuando un inversor tiene una cuenta de jubilación invertida en el mercado de valores a la que añade dinero cada año. La función genera un rendimiento aleatorio de la inversión, expresado como porcentaje, cada año durante el plazo especificado. La función toma un valor de inicialización como parámetro. Este valor se usa para reinicializar el generador de números aleatorios, lo que asegura que la función no obtenga la misma lista de números aleatorios cada vez que se ejecute. La función random.normalvariate asegura que los valores aleatorios se produzcan en una distribución normal para la media y la desviación estándar especificadas. La función aumenta el valor de la cartera en la cantidad de crecimiento, que puede ser positiva o negativa, y añade una suma anual que representa una inversión adicional.

        Definirás las constantes necesarias en un paso posterior.

      4. Crea muchas semillas para introducir en la función. En la petición de Spark, introduce el siguiente código, que genera 10.000 semillas:

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

        El resultado de la operación parallelize es un conjunto de datos distribuidos resiliente (RDD), que es una colección de elementos optimizados para el procesamiento paralelo. En este caso, el RDD contiene semillas basadas en la hora del sistema actual.

        Al crear el RDD, Spark divide los datos en función del número de trabajadores y de los núcleos disponibles. En este caso, Spark elige usar ocho segmentos, uno por cada núcleo. Esto es suficiente para esta simulación, que tiene 10.000 elementos de datos. En simulaciones más grandes, cada segmento puede ser mayor que el límite predeterminado. En ese caso, especificar un segundo parámetro en parallelize puede aumentar el número de particiones, lo que puede ayudar a que el tamaño de cada partición sea manejable, mientras que Spark sigue aprovechando los ocho núcleos.

      5. Introduce el RDD que contiene las semillas en la función de crecimiento.

        results = seeds.map(grow)
        

        El método map transfiere cada valor inicial del RDD a la función grow y añade cada resultado a un nuevo RDD, que se almacena en results. Ten en cuenta que esta operación, que realiza una transformación, no produce resultados de inmediato. Spark no realizará este trabajo hasta que se necesiten los resultados. Esta evaluación diferida es el motivo por el que puedes introducir código sin que se definan las constantes.

      6. Especifica algunos valores para la función.

        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. Llama a reduce para agregar los valores del RDD. Introduce el siguiente código para sumar los resultados del RDD:

        sum = results.reduce(add)
        
      8. Estima y muestra la rentabilidad media:

        print (sum / 10000.)
        

        Asegúrate de incluir el carácter de punto (.) al final. Significa aritmética de punto flotante.

      9. Ahora, cambia una suposición y observa cómo cambian los resultados. Por ejemplo, puede introducir un nuevo valor para el retorno medio del mercado:

        MKT_AVG_RETURN = 0.07
        
      10. Vuelve a ejecutar la simulación.

        print (sc.parallelize([time.time() + i for i in range(10000)]) \
                .map(grow).reduce(add)/10000.)
        
      11. Cuando hayas terminado de experimentar, pulsa CTRL+D para salir del intérprete de Python.

      Programar una simulación de Monte Carlo en Scala

      Montecarlo, por supuesto, es famosa por sus casinos. En esta sección, usarás Scala para crear una simulación que modele la ventaja matemática que tiene un casino en un juego de azar. La "ventaja de la casa" en un casino real varía mucho de un juego a otro. Por ejemplo, puede superar el 20% en el keno. En este tutorial se crea un juego sencillo en el que la casa tiene solo un 1 % de ventaja. Así es como funciona el juego:

      • El jugador hace una apuesta, que consiste en un número de fichas de un fondo de banca.
      • El jugador tira un dado de 100 caras (¿no sería genial?).
      • Si el resultado de la tirada es un número del 1 al 49, el jugador gana.
      • Si el resultado es de 50 a 100, el jugador pierde la apuesta.

      Como puedes ver, este juego crea una desventaja del 1 % para el jugador: en 51 de los 100 resultados posibles de cada tirada, el jugador pierde.

      Sigue estos pasos para crear y ejecutar el juego:

      1. Inicia el intérprete de Scala desde el nodo principal de Dataproc.

        spark-shell
        
      2. Copia y pega el siguiente código para crear el juego. Scala no tiene los mismos requisitos que Python en lo que respecta a la sangría, por lo que puedes copiar y pegar este código en la petición de 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. Pulsa return hasta que veas la petición scala>.

      4. Introduce el siguiente código para jugar 25 veces, que es el valor predeterminado de NUMBER_OF_GAMES.

        playSession()
        

        Tu saldo inicial era de 10 unidades. ¿Es más alta o más baja ahora?

      5. Ahora,simula que 10.000 jugadores apuestan 100 fichas por partida. Juega 10.000 partidas en una sesión. Esta simulación de Monte Carlo calcula la probabilidad de perder todo tu dinero antes de que finalice la sesión. Introduce el siguiente código:

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

        Ten en cuenta que la sintaxis .reduce(_+_) es una abreviatura en Scala para agregar datos mediante una función de suma. Es funcionalmente equivalente a la sintaxis .reduce(add) que has visto en el ejemplo de Python.

        El código anterior realiza los siguientes pasos:

        • Crea un RDD con los resultados de la reproducción de la sesión.
        • Sustituye los resultados de los jugadores en bancarrota por el número 1 y los resultados distintos de cero por el número 0.
        • Suma el número de jugadores en bancarrota.
        • Divide el recuento entre el número de jugadores.

        Un resultado habitual podría ser el siguiente:

        0.998
        

        Lo que representa una garantía casi total de perder todo tu dinero, aunque el casino solo tuviera una ventaja del uno por ciento.

      Limpieza

      Eliminar el proyecto

      1. In the Google Cloud console, go to the Manage resources page.

        Go to Manage resources

      2. In the project list, select the project that you want to delete, and then click Delete.
      3. In the dialog, type the project ID, and then click Shut down to delete the project.

      Siguientes pasos

      • Para obtener más información sobre cómo enviar tareas de Spark a Dataproc sin tener que usar ssh para conectarte al clúster, consulta Dataproc: enviar una tarea.