Métodos Monte Carlo com o Dataproc e o Apache Spark


O Dataproc e o Apache Spark oferecem a infraestrutura e a capacidade para executar simulações Monte Carlo desenvolvidas em Java, Python ou Scala.

Os métodos Monte Carlo ajudam a responder uma grande variedade de perguntas relacionadas a negócios, engenharia, ciências, matemática e outros campos. Em uma simulação Monte Carlo, com o uso de amostragem aleatória repetida para criar uma distribuição de probabilidade para uma variável, você consegue respostas para perguntas que não conseguiria com outros métodos. Em finanças, por exemplo, a determinação do preço de uma opção de equidade requer a análise de milhares de possibilidades de mudança do preço da ação ao longo do tempo. Com os métodos Monte Carlo, você simula essas oscilações em uma ampla variedade de resultados possíveis e, ao mesmo tempo, mantém o controle sobre o domínio das entradas para o problema.

No passado, a execução de milhares de simulações poderia levar um longo tempo e ter um alto custo. Com o Dataproc, a capacidade é provisionada sob demanda e paga por minuto. Com o Apache Spark, você usa os clusters de dezenas, centenas ou milhares de servidores para executar simulações de maneira intuitiva, e faz o escalonamento de acordo com as suas necessidades. Isso significa que você consegue executar mais simulações com mais rapidez, o que ajuda a inovar no seu negócio com mais agilidade e possibilita um melhor gerenciamento dos riscos.

A segurança é sempre importante ao lidar com dados financeiros. O Dataproc é executado no Google Cloud, o que ajuda a manter os dados seguros, protegidos e particulares de várias maneiras. Por exemplo, todos os dados são criptografados durante a transmissão e quando em repouso, e o Google Cloud está em conformidade com ISO 27001, SOC3 e PCI.

Objetivos

  • Criar um cluster gerenciado do Dataproc com o Apache Spark pré-instalado.
  • Executar uma simulação Monte Carlo com o Python que faça a estimativa do crescimento de um portfólio de ações ao longo do tempo.
  • Executar uma simulação de Monte Carlo com o Scala que simule os lucros de um cassino.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.

Antes de começar

  • Configurar um projeto do Google Cloud
    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.

      Go to project selector

    3. Make sure that billing is enabled for your Google Cloud project.

    4. Enable the Dataproc and Compute Engine APIs.

      Enable the APIs

    5. Install the Google Cloud CLI.
    6. To initialize the gcloud CLI, run the following command:

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

      Go to project selector

    8. Make sure that billing is enabled for your Google Cloud project.

    9. Enable the Dataproc and Compute Engine APIs.

      Enable the APIs

    10. Install the Google Cloud CLI.
    11. To initialize the gcloud CLI, run the following command:

      gcloud init

Como criar um cluster do Dataproc

Siga as etapas para criar um cluster do Dataproc usando o Console do Google Cloud. As configurações de cluster padrão, que incluem dois nós de trabalho, são suficientes para este tutorial.

Como desativar a geração de registros de avisos

Por padrão, um registro detalhado é impresso na janela de console pelo Apache Spark. Para os fins deste tutorial, altere o nível da geração de registro para registrar somente os erros. Siga estas etapas:

Use ssh para se conectar ao nó principal do cluster do Dataproc

O nó principal do cluster do Dataproc tem o sufixo -m no nome da 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.

Uma janela SSH é aberta conectada ao nó principal.

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

Alterar a configuração de geração de registros

  1. No diretório inicial do nó principal, edite /etc/spark/conf/log4j.properties.

    sudo nano /etc/spark/conf/log4j.properties
    
  2. Defina log4j.rootCategory igual a 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. Salve as alterações e saia do editor. Se você quiser reativar a geração de registros detalhados, reverta a alteração restaurando o valor de .rootCategory para o valor original (INFO).

Linguagens de programação do Spark

O Spark é compatível com Python, Scala e Java como linguagens de programação para aplicativos autônomos, e fornece interpretadores interativos para o Python e o Scala. A escolha da linguagem é uma questão de preferência pessoal. Neste tutorial, usamos os interpretadores interativos porque assim é possível fazer testes por meio da alteração do código, tentando diferentes valores de entrada e visualizando os resultados.

Como estimar o crescimento do portfólio

Em finanças, os métodos Monte Carlo são, às vezes, usados para executar simulações que tentam prever o desempenho de um investimento. Para responder a perguntas sobre o desempenho do portfólio na média ou nos cenários mais pessimistas, amostras aleatórias dos resultados em várias condições prováveis de mercado são produzidas por essas simulações.

Siga estas etapas para criar uma simulação que usa os métodos Monte Carlo para estimar o crescimento de um investimento financeiro com base em alguns fatores de mercado comuns.

  1. Inicie o interpretador de Python a partir do nó principal do Dataproc.

    pyspark
    

    Aguarde o prompt do Spark >>>.

  2. Insira o código a seguir. Mantenha o recuo na definição da função.

    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. Pressione return até ver novamente o prompt do Spark.

    No código acima, foi definida uma função que modela o que pode acontecer quando um investidor tem uma conta de aposentadoria existente que é investida no mercado de ações, onde eles depositam dinheiro anualmente. A função gera um retorno de investimento aleatório, como uma porcentagem, para cada ano que um termo especificado durar. Essa função gera um valor de semente como parâmetro que é usado para realimentar o gerador de números aleatórios. Isso garante que a função não receba a mesma lista de números cada vez que ela é executada. A função random.normalvariate garante que esses valores aleatórios ocorram em uma distribuição normal para a média e o desvio padrão especificados. A função aumenta o valor do portfólio de acordo com o volume de crescimento, que pode ser positivo ou negativo, e adiciona uma soma anual que representa o investimento adicional.

    As constantes obrigatórias serão definidas em uma etapa posterior.

  4. Crie muitas sugestões para alimentar a função. No prompt do Spark, insira o código a seguir, que gera 10.000 sugestões:

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

    O resultado da operação parallelize é um conjunto de dados distribuídos resiliente (RDD, na sigla em inglês), que é uma coleção de elementos otimizados para processamento paralelo. Nesse caso, o RDD contém sementes que são baseadas no horário atual do sistema.

    Quando o RDD é criado, os dados são divididos no Spark com base no número de workers e núcleos disponíveis. Nesse caso, o Spark escolhe usar oito partes, uma parte para cada núcleo. Isso atende a essa simulação, que tem 10.000 itens de dados. Para simulações maiores, cada parte pode ser maior do que o limite padrão. Nesse caso, especificar um segundo parâmetro para parallelize pode aumentar o número de frações, o que pode ajudar a manter o tamanho de cada fração sob controle, enquanto o Spark ainda aproveita todos os oito núcleos.

  5. Alimente o RDD que contém as sementes para a função de crescimento.

    results = seeds.map(grow)
    

    O método map passa cada semente no RDD para a função grow e anexa cada resultado a um novo RDD, que é armazenado em results. Observe que essa operação de transformação não produz os resultados imediatamente. Isso não é feito no Spark antes dos resultados serem necessários. Devido a essa avaliação lenta, o código pode ser inserido sem ter as constantes definidas.

  6. Especifique alguns valores para a função.

    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. Chame reduce para agregar os valores no RDD. Insira o seguinte código para somar os resultados no RDD:

    sum = results.reduce(add)
    
  8. Estime e exiba o retorno médio:

    print (sum / 10000.)
    

    Verifique se o caractere ponto (.) foi incluído no final. Ele representa um valor aritmético de ponto flutuante.

  9. Agora altere uma pressuposição e veja como os resultados mudam. Por exemplo, insira um novo valor para o retorno médio do mercado:

    MKT_AVG_RETURN = 0.07
    
  10. Execute a simulação novamente.

    print (sc.parallelize([time.time() + i for i in range(10000)]) \
            .map(grow).reduce(add)/10000.)
    
  11. Quando terminar os testes, pressione CTRL+D para sair do interpretador de Python.

Como programar uma simulação Monte Carlo em Scala

A cidade de Monte Carlo é conhecida como um destino de apostas. Nesta seção, você usa o Scala para criar uma simulação que modela a vantagem matemática que um cassino tem em um jogo de azar. O "house edge" de um cassino real varia bastante entre os jogos. Ele pode ser maior do que 20% no keno, por exemplo. Neste tutorial, um jogo simples será criado, onde a casa tem somente uma vantagem de 1%. Veja como ele funciona:

  • O jogador faz uma aposta, que consiste de algumas fichas do fundo de apostas.
  • O jogador rola um dado de cem lados (que legal, não?).
  • Se o resultado do teste for um número de 1 a 49, o jogador ganha.
  • Caso o resultado seja entre 50 e 100, o jogador perde a aposta.

Veja que, nesse jogo, o jogador tem uma desvantagem de 1%: em 51 dos 100 resultados possíveis de cada rolamento, ele perde.

Siga estas etapas para criar e executar o jogo:

  1. Inicie o interpretador de Scala a partir do nó principal do Dataproc.

    spark-shell
    
  2. Copie e cole o código a seguir para criar o jogo Scala não tem os mesmos requisitos que Python quando se trata de recuo. Portanto, basta copiar e colar esse código no prompt 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. Pressione return até ver o prompt scala>.

  4. Insira o código a seguir para jogar 25 vezes, que é o valor padrão de NUMBER_OF_GAMES.

    playSession()
    

    Seu fundo de apostas começou com o valor de 10 unidades. E agora, está mais alto ou mais baixo?

  5. Agora simule 10.000 jogadores apostando 100 fichas por jogo. Jogue 10.000 vezes em uma sessão. Nesta simulação de Monte Carlo, calculamos a probabilidade de perder todo seu dinheiro antes do final da sessão. Insira o código a seguir:

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

    Observe que a sintaxe .reduce(_+_) é uma abreviação em Scala para agregação usando uma função de soma. A funcionalidade dela é equivalente à sintaxe .reduce(add) que você viu no exemplo do Python.

    No código anterior, as seguintes etapas foram executadas:

    • criação de RDD com os resultados da sessão jogada;
    • substituição dos resultados dos jogadores falidos pelo número 1 e dos resultados diferentes de zero pelo número 0;
    • soma da contagem de jogadores falidos;
    • divisão da contagem pelo número de jogadores.

    Um resultado típico seria:

    0.998
    

    Isso representa uma garantia quase total de que você perderia todo o dinheiro, mesmo se o cassino tivesse somente uma vantagem de 1%.

Limpar

Excluir o projeto

  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.

A seguir

  • Para saber mais sobre como enviar jobs do Spark para o Dataproc sem precisar usar o ssh para se conectar ao cluster, leia Dataproc: enviar um job.