terça-feira, 3 de março de 2015

Tutorial de Big Data: Análise de Sentimentos de Posts - Amazon Elastic MapReduce

Os dados estão lá! E estão livres! Como podemos extrair informação deles em nosso benefício? O Bom Programador, cansado de ver marqueteiros "caozeiros" venderem soluções de "Big Data", resolveu arregaçar as mangas e "mandar ver".

Fizemos uma análise de sentimento de "tweets˜, para responder a uma pergunta: O que as pessoas sentem com relação aos 450 anos do Rio de Janeiro?

Tivemos que dar uma grande volta para podermos responder a essa simples pergunta. Usamos:

  • Hadoop;
  • API do Twitter;
  • Google Translate API;
  • SentiWordNet;
  • Amazon Elastic MapReduce.
Todo o código-fonte está disponível AQUI!




Atenção

A execução deste tutorial implicará em despesas em Dólar. O uso da Google Translate API é pago e o uso do Amazon Elastic MapReduce também. 

Eu já gastei cerca de US$ 15,00 para testar e rodar tudo.

O Google Cloud Platform, onde a Google Translate API fica, é pago,  porém, a título de promoção, lhe dá um crédito de US$ 300,00.

A Amazon AWS também é gratuito por 1 ano. Porém, como você vai usar instâncias pagas, vai ter custos. O EMR só usa instâncias "large" e "medium", que não são cobertas pela promoção de 1 ano.

Fique ciente que os erros de configuração do Amazon EMR implicam em custos!

Se quiser, você pode baixar uma versão do Hadoop e rodar localmente em sua máquina. Também pode usar o arquivo de tweets traduzidos que eu coloquei no repositório. Assim, economizará uma grana e poderá rodar os exemplos. Eu não vou explicar como instalar o Hadoop localmente e rodar o exemplo nesse post. Mais tarde, eu postarei alguma coisa.

Como fazer


Para responder à pergunta: "O que as pessoas sentem com relação aos 450 anos do Rio de Janeiro?", temos alguns problemas. Vamos ver como fazer essa análise: 
  1. Temos que coletar os tweets. Existe uma API do Twitter, que faz exatamente isso. Basta nos cadastrarmos para obter uma chave de acesso;
  2. Precisamos de alguma maneira para analisar os sentimentos dos textos. Há um arquivo que já tem essa classificação: o SentiWordNet;
  3. Mas há um problema: O SentiWordNet só tem versão em inglês... Precisamos dar um jeito de traduzir nossos tweets para inglês, e podemos usar a Google Translate API;
  4. Temos que coletar os tweets, traduzi-los e rodar o MapReduce. Fazer isso com uma rede doméstica e usando um computador pessoal, pode ser muito demorado. Por isso, vamos usar os serviços da Amazon AWS: EC2, S3, CloudFormation e Elastic MapReduce;
Temos dois projetos Java Maven que executam essas tarefas. Acesse o código-fonte no GitHub e baixe o zip ou clone o repositório. Importe os projetos para o Eclipse.

O projeto "twitterparser" gera um Jar que coleta os "tweets", traduzindo-os para inglês. Nesse projeto, eu uso a biblioteca: "Hosebird Client", que já coleta os tweets selecionando palavras-chave.

A saída é um arquivo "tweets-translated.txt", que eu uso como "input" para o MapReduce. 

O projeto "sentimentanalysis" contém as 3 classes necessárias para invocar o Hadoop: Mapper, Reducer e Instanciador. Nele, eu carrego o SentiWordNet (dentro do Mapper) e vou analisando os tweets nas seguintes categorias: 

  • VERY_POSITIVE;
  • VERY_NEGATIVE;
  • NEUTRAL;
  • POSITIVE;
  • NEGATIVE;

          Para classificar os tweets, eu usei a classe de exemplo do SentiWordNet, e a orientação deste POST do StackOverflow, (usuário: Maroun Maroun) que funcionou muito bem.

          Também aproveitamos o modelo criado por Frank Willian Cardoso de Oliveira, da universidade de Maringá - PR, que realizou trabalho usando técnica de tradução e SentiWordNet.

          Primeiro passo: Registrar-se nas APIs e no AWS

          Aprenda a usar o AWS EMR lendo esse livro da Amazon (gratuito na kindle store). Tente rodar o exemplo que ele dá, que usa programas prontos em Python.

          Cadastre-se para usar a API do Twitter, e salve os dados: 

          • consumerKey;
          • consumerSecret;
          • secret;
          • token;
          Cadastre-se para usar a Google Translate API e pegue sua API Key.

          Atenção: O uso da Google Translate API é pago! Você vai pagar pelo uso! Embora eles lhe dêem US$ 300,00 de promoção, fique ciente que o uso será cobrado.


          Se você optou por usar o arquivo de exemplo, e rodar em sua máquina, então não haverá custos e nem será necessário se inscrever no AWS e no Google Cloud Platform.

          Segundo Passo: Criar um Bucket S3

          Agora é o momento de criar um "Bucket S3", para armazenar os dados. Para isto, abra a S3 Console e crie um novo Bucket. 

          Eu dei o nome de "obomprogramadors3". O nome do Bucket tem que ser único. Guarde bem o nome que você deu e, onde estiver escrito "obomprogramadors3" (neste tutorial) substitua pelo nome do seu Bucket.

          Atenção: Não use espaços, caracteres especiais e underscore! Só letras e números!

          Crie três folders: "collector", "input" e "mapper". Selecione os três e torne-os públicos. Basta selecionar a "Action": "Make Public".

          No folder "collector", vamos colocar o jar e os arquivos necessários para rodar o nosso "twitterparser". No folder "input", vamos colocar o arquivo "tweets-translated.txt", e, no folder "mapper", vamos colocar o nosso Jar de MapReduce e o nosso arquivo SentiWordNet.

          Usar um Bucket S3 é a única maneira de mover dados de e para as instâncias EC2 e o cluster EMR.

          Terceiro passo: Criar uma instância EC2 para rodar o coletor de tweets

          Se você usar uma instância micro, e estiver dentro do prazo de 1 ano gratuito no AWS, não pagará nada por isso. Porém, caso não atenda a essas condições, você será cobrado pelo uso. Uma alternativa é usar o arquivo "tweets-translated.txt" que eu forneci. Outra, seria rodar o "twitterparser" em sua própria máquina. Basta ter uma boa internet para isso.

          Usando o Eclipse, gere um Jar executável do projeto "twitterparser", escolhendo: “Extract required libraries into generated JAR”.

          Suba esse Jar para o seu Bucket S3, jogando-o no folder "collector".

          Vá no serviço EC2, na console AWS, e crie uma nova instância. Selecione "Amazon Linux AMI", que já tem Java instalado.

          Na etapa 2: "Choose an Instance Type", selecione "t2-micro". Clique em "review and launch". Clique em "launch" e selecione seu par de chaves (arquivo Pem que você criou quando se cadastrou no AWS).


          Clique no link da frase: "The following instance launches have been initiated: i-507a02aa", para ver a sua instância na console de gerenciamento. Quando ela estiver rodando, você verá uma imagem assim:

          Anote as seguintes informações:

          • instance id;
          • public DNS.
          Agora, podemos iniciar uma sessão SSH com a instância e rodar o coletor. Se você usa MS Windows, tem que baixar o programa Putty. Se você usar Mac ou Linux, pode usar o comando SSH normal:


          ssh -i <path do arquivo pem> ec2-user@<public dns>

          Você deve informar o caminho para o seu arquivo Pem, que você criou quando se cadastrou no AWS. E deve informar o usuário "ec2-user" e o dns público da sua instância.

          Bem, agora, você precisa rodar o coletor. 

          Para começar, temos que instalar as ferramentas de gerenciamento do S3, para podermos ler e gravar arquivos no nosso Bucket: 
          1. cd /etc/yum.repos.d
          2. sudo wget http://s3tools.org/repo/RHEL_6/s3tools.repo
          3. sudo yum install s3cmd
          4. cd ~

            Agora, é só copiar o Jar para a pasta do usuário: 

            s3cmd —configure get s3://obomprogramadors3/collector/twitterparserexec.jar

            Lembre-se de mudar o nome do Bucket ( o meu é "obomprogramadors3"). A primeira vez, tem que rodar o “—configure”, que vai te pedir: Access key, Token e senha de encriptação, que você obteve quando criou sua conta no AWS.

            Crie um arquivo chamado "config.json", na pasta home, com o seguinte conteúdo: 

            {
            "consumerKey" : "<twitter consumer key>",
            "consumerSecret" : "<twitter consummer secret>",
            "limit" : 50,
            "secret" : "<twitter secret>",
            "token" : "<twitter token>",
            "googleApiKey": "<google translate API key>",
            "terms" : ["rio 450"],
            "output" : "<output folder>"
            }

            Preencha com os dados da API do Twitter e do Google Translate. Na propriedade "terms", indique um vetor JSON com os strings que você quer selecionar. Somente os tweets contendo esses termos serão selecionados.

            A propriedade "limit" é o número máximo de tweets a serem processados. Se você deixar um número muito grande, o processo vai demorar mais. Para essa demonstração, 50 tweets é o suficiente, embora seja estatisticamente insignificante. 

            A propriedade "output" é o folder de saída, por exemplo: "./", para o diretório corrente. 

            Agora, é só rodar o coletor: 

            java -jar twitterparserexec.jar ./config.json

            Após a execução, devemos copiar o resultado para a pasta "input", em nosso Bucket. Lembre-se que você está acessando sua instância EC2 via SSH. 

            s3cmd put s3://obomprogramadors3/input/ ./tweetes-translated.txt


            Finalmente, termine a instância EC2, caso contrário, ficará pagando por ela. Acesse a console, marque a instância e selecione a "Action" "Instance state" / "Terminate".

            Atenção: Certifique-se que sua instância esteja como "Terminated" para evitar cobranças desnecessárias!

            Quarto passo: Criar um Cluster EMR para realizar a operação de MapReduce

            Bom, antes de continuar, lembre-se que esta é a etapa mais custosa desse processo. Eu estimo que você vá gastar algo entre 5 e 15 dólares, dependendo da quantidade de tweets e dos erros que tenha cometido. 

            Para não perder tempo, crie um JAR do MapReduce, do tipo "runnable". Usando o eclipse, selecione o projeto "sentimentanalysis" e exporte como "runnable jar",  mas selecione: “Extract required libraries into generated JAR”.

            Suba o Jar para o s3, no folder mapper, e marque-o como público.


            Abra a console do EMR e crie um Cluster. 

            Ao criar o Cluster EMR: 

            - Marque "logging enabled" e selecione o log folder location para a raiz do seu bucket: 
            s3://obomprogramadors3.
            - Em “Software configuration”, desinstale as três aplicações: Hive, Pig e Hue, pois não vamos utilizá-las nesse tutorial.
            - Em “security and access”, selecione o seu arquivo pem no campo: “EC2 key pair”.

            - Crie dois “steps”: 
            1. Copiar o arquivo SentiWordNet para o HDFS. Antes de copiar, renomeie o arquivo e retire todos os caracteres Underscore “_”. Isso dá problema!
            2. Rodar o Jar do MapReduce que você criou

            STEP 1: 

            Crie um step “Custom Jar: 
            • Step Type: Custom JAR
            • Name: My Custom JAR
            • Jar Location: /home/hadoop/lib/emr-s3distcp-1.0.jar
            • Arguments: --src s3://obomprogramadors3/mapper/ --dest hdfs:///local/
            • Action on failure: Terminate cluster
            Atenção: Substitua "obomprogramadors3" pelo nome do seu Bucket.


            STEP 2: 

            Crie um step “Custom Jar”:
            • Name: “SentiExec: 
            • Jar: s3://obomprogramadors3/mapper/sentiexec.jar
            • Arguments: 
            • /local/SentiWordNet.txt s3n://obomprogramadors3/input/tweets-translated.txt s3n://obomprogramadors3/output/
            • Action on failure: Terminate cluster
            Atenção: Substitua "obomprogramadors3" pelo nome do seu Bucket.

            - No final, marque “Auto-terminate” para Yes.


            Ao criar o Cluster, ele será provisionado e vai executar os steps


            Selecione “Create cluster” e observe a página. Quando o estado aparecer “Terminated”, você pode pegar os arquivos dentro de /output, em seu bucket.



            Os arquivos estão dentro da pasta "output", em seu Bucket: 


            Ele gera vários arquivos, e você tem que consolidá-los. Se quiser, pode criar uma etapa "streaming program", para agregar os arquivos: 

                   --input hdfs:///temp/intermediate/ \
                   --output s3n://<path-to-bucket>/output-reports-dir/ \
                   --mapper /bin/cat \
                   --reducer org.apache.hadoop.mapred.lib.IdentityReducer \
                   --args -jobconf,mapred.reduce.tasks=1

            O resultado

            Após consolidar os resultados, obtive: 
            • VERY_POSITIVE 3
            • VERY_NEGATIVE 1
            • NEUTRAL 39
            • POSITIVE 7
            • NEGATIVE 8
            O sentimento geral é mais neutro para positivo do que negativo. 

            Limitações e conclusões

            Usar o SentiWordNet não é a melhor das alternativas, e, certamente, existem processos melhores, mas dá um resultado razoavelmente correto.

            A coleta de um número maior de tweets, juntamente com outras fontes, como feeds, pode dar maior valor estatístico à análise.