Previsão de Preço de Casas com Apache Spark

Regressão Linear

Neste artigo, usaremos o conjunto de dados da California Housing. Observe, é claro, que esses dados são realmente 'pequenos' e que o uso do Spark nesse contexto pode ser um exagero, porém este notebook é apenas para fins educacionais e deve nos dar uma idéia de como podemos usar o PySpark para construir um modelo de aprendizado de máquina.

Este modelo foi criado com base num estudo do Kaggle

Compreendendo o conjunto de dados

O conjunto de dados da California Housing apareceu em um artigo de 1997 intitulado Sparse Spatial Autoregressions, escrito por Pace, R. Kelley e Ronald Barry e publicado na revista Statistics and Probability Letters. Os pesquisadores construíram esse conjunto de dados usando os dados do censo da Califórnia em 1990.
Os dados contêm uma linha por grupo de blocos censitários. Um grupo de blocos é a menor unidade geográfica para a qual o US Census Bureau publica dados de amostra (um grupo de blocos normalmente tem uma população de 600 a 3.000 pessoas). Nesta amostra, um grupo de blocos inclui, em média, 1425,5 indivíduos que vivem em uma área geograficamente compacta.

Esses dados espaciais contêm 20.640 observações sobre os preços da habitação com 9 variáveis econômica

  • Longitude: refere-se à distância angular de um local geográfico ao norte ou ao sul do equador da Terra para cada grupo de blocos.
  • Latitude: refere-se à distância angular de um local geográfico a leste ou oeste do equador da Terra para cada grupo de blocos.
  • Idade média da moradia: é a idade média das pessoas que pertencem a um grupo de quarteirões. Observe que a mediana é o valor que fica no ponto médio de uma distribuição de frequência dos valores observados.
  • Total de quartos-de-dormir: é o número total de quartos-de-dormir nas casas por grupo de blocos.
  • Total de quartos: é o número total de quartos nas casas por grupo de blocos
  • População: é o número de habitantes de um grupo de blocos
  • Famílias: refere-se a unidades de casas e seus ocupantes por grupo de blocos
  • Renda mediana: é usada para registrar a renda mediana das pessoas que pertencem a um grupo de blocos
  • Valor médio da casa: é a variável dependente e refere-se ao valor médio da casa por grupo de blocos
Além disso, também aprendemos que todos os grupos de blocos têm zero entradas, pois as variáveis independentes e dependentes foram excluídas dos dados.

O valor médio da casa é a variável dependente e será atribuído o papel da variável de destino em nosso modelo de ML.

Instalando o pyspark no nosso Kernel, no meu caso, já tinha instalado, mas aparecerá uma barra de progresso da instalação e uma mensagem final de sucesso.



Importação das bibliotecas e sias funcionalidades para uso ao longo do algoritmo



Importação das libs de visualização e sua respetivas configuração


Definindo semente aleatória para a reprodutibilidade do notebook


Criando a sessão Spark


Carregar os dados de um arquivo em um Data Frame

No local do caminho do data set, devem informar o caminho relativo ao local onde armazenaram o arquivo de dados



A especificação do esquema ao carregar dados em um DataFrame fornecerá melhor desempenho do que a inferência do esquema.



Carregar os dados



Após a carga devemos de inspecionar pelo menos as 5 primeiras linhas



Visualizando todas as colunas do Data Frame e o respetivo esquema criado anteriormente


Exploração de Dados

Vamos iniciar agora a fase de exploração de dados, para podermos entender melhor nosso data set e verificar as melhorias necessárias no mesmo para futuramente podermos obter bins resultados no nosso modelo preditivo.


Distribuição da idade média das pessoas que vivem na área



Agora vamos plotar nossa analise de forma a obtermos uma visualização analítica de nossos dados.


A maioria dos residentes está na juventude ou decide morar aqui durante a terceira idade. Alguns dados mostram idade média <10, que parece estar fora do padrão.

Resumo das estatísticas

Os DataFrames do Spark incluem algumas funções internas para processamento estatístico. A função describe() executa cálculos estatísticos resumidos em todas as colunas numéricas e os retorna como um DataFrame.




Veja os valores mínimo e máximo de todos os atributos (numéricos). Vemos que vários atributos têm uma ampla gama de valores: precisamos normalizar este conjunto de dados.

Pré-processamento de Dados

Com todas essas informações que coletamos de nossa pequena análise exploratória de dados, sabemos o suficiente para pré-processar nossos dados para alimentá-los com o modelo.
  • Não devemos nos preocupar com valores ausentes; todos os valores zero foram excluídos do conjunto de dados
  • Provavelmente, devemos padronizar nossos dados, pois vimos que a faixa de valores mínimos e máximos é bastante grande.
  • Possivelmente, podemos adicionar alguns atributos adicionais, como um recurso que registra o número de quartos-de-dormir por quartos ou os quartos por família.
  • Nossa variável dependente também é bastante grande; Para facilitar nossa vida, teremos que ajustar um pouco os valores.

Pré-processamento dos valores alvo

Primeiro, vamos começar com o medianHouseValue, nossa variável dependente. Para facilitar o trabalho com os valores teóricos, expressaremos os valores da casa em unidades de 100.000. Isso significa que um destino como 452600.000000 deve se tornar 4.526:


Podemos ver claramente que os valores foram ajustados corretamente quando analisamos o resultado do método show().

Engenharia de recursos

Agora que ajustamos os valores em medianHouseValue, adicionaremos as seguintes colunas ao conjunto de dados:
  • Quartos por domicílio, que se refere ao número de quartos em domicílios por grupo de blocos;
  • População por domicílio, o que basicamente nos dá uma indicação de quantas pessoas vivem em domicílios por grupo de quarteirões;
  • Quartos-de-dormir por quarto, o que nos dará uma idéia de quantos quartos são quartos por grupo de blocos;
Como estamos trabalhando com DataFrames, podemos usar melhor o método select() para selecionar as colunas com as quais trabalharemos, ou seja, totalRooms, households e population. Além disso, temos que indicar que estamos trabalhando com colunas adicionando a função col() ao nosso código. Caso contrário, não poderemos executar operações por elementos, como a divisão que temos em mente para essas três variáveis.



Realizando a inspeção do resultado



Podemos ver que, na primeira linha, existem cerca de 6,98 quartos por domicílio, os domicílios do grupo de blocos são compostos por cerca de 2,5 pessoas e a quantidade de quartos é bastante baixa com 0,14:

Como não queremos necessariamente padronizar nossos valores-alvo, queremos garantir o isolamento dos valores em nosso conjunto de dados. Observe também que este é o momento de deixar de fora as variáveis que talvez não desejemos considerar em nossa análise. Nesse caso, vamos deixar de fora variáveis como longitude, latitude, HousingMedianAge e totalRooms.

Nesse caso, usaremos o método select() e passaremos os nomes das colunas na ordem que for mais apropriada. Nesse caso, a variável de destino medianHouseValue é colocada em primeiro lugar, para que não seja afetada pela padronização.


Extração de recursos

Agora que reorganizamos os dados, estamos prontos para normalizar os dados. Vamos escolher os recursos a serem normalizados.


Use um VectorAssembler para colocar recursos em uma coluna de vetor de recursos:


Todos os recursos se transformaram em um vetor denso.

Padronização 

Em seguida, podemos finalmente escalar os dados usando o StandardScaler. As colunas de entrada são os recursos e a coluna de saída com o redimensionado que será incluído no será denominada "features_escaladas":


Construindo um modelo de aprendizado de máquina com Spark ML

Com todo o pré-processamento concluído, finalmente chegou a hora de começar a construir nosso modelo de regressão linear! Como sempre, primeiro precisamos dividir os dados em conjuntos de treinamento e teste. Felizmente, isso não é problema com o método randomSplit():


Passamos em uma lista com dois números que representam o tamanho que queremos que seus conjuntos de treinamento e teste tenham e uma semente, necessária para fins de reprodutibilidade.

Observe que o argumento elasticNetParam corresponde a α ou a interceptação vertical e que o regParam ou o parâmetro de regularização corresponde a λ.


Criação de um modelo ElasticNet

ElasticNet é um modelo de regressão linear treinado com L1 e L2 como regularizador. Essa combinação permite aprender um modelo esparso, em que poucos pesos são diferentes de zero como Lasso, mantendo as propriedades de regularização do Ridge. Controlamos a combinação convexa de L1 e L2 usando o parâmetro l1_ratio.

A rede elástica é útil quando existem vários recursos correlacionados entre si. É provável que o Lasso escolha um deles aleatoriamente, enquanto a rede elástica provavelmente escolha ambos.

Uma vantagem prática da troca entre Lasso e Ridge é que ela permite que a Elastic-Net herde parte da estabilidade de Ridge em rotação.


Avaliando o modelo

Com nosso modelo, podemos gerar previsões para nossos dados de teste: use o método transform() para prever os rótulos para nossos dados de teste. Em seguida, podemos usar operações RDD para extrair as previsões e os rótulos verdadeiros do DataFrame.

Inspecionar os coeficientes do modelo


Gerando previsões


Inspecionar as métrica

Observar os valores previstos é uma coisa, mas outra coisa melhor é observar algumas métricas para ter uma idéia melhor de quão bom é o seu modelo.

Usando o atributo LinearRegressionModel.summary: Em seguida, também podemos usar o atributo summary para acessar o rootMeanSquaredError e o r2.


  • O RMSE mede quanto erro há entre dois conjuntos de dados comparando um valor previsto e um valor observado ou conhecido. Quanto menor um valor RMSE, mais próximos são os valores previstos e observados.
  • O R2 ("R ao quadrado") ou o coeficiente de determinação é uma medida que mostra quão próximos os dados estão da linha de regressão ajustada. Essa pontuação sempre estará entre 0 e 100% (neste caso, de 0 a 1), onde 0% indica que o modelo não explica a variabilidade dos dados de resposta em torno de sua média e 100% indica o contrário: explica toda a variabilidade. Isso significa que, em geral, quanto maior o quadrado R, melhor o modelo se ajusta aos nossos dados.

Usando o pacote RegressionEvaluator do pyspark.m


Usando o pacote RegressionMetrics do pyspark.mlli



Definitivamente, existem algumas melhorias necessárias para o nosso modelo! Se quisermos continuar com esse modelo, podemos brincar com os parâmetros que passamos para o seu modelo, as variáveis que incluímos no seu DataFrame original.



Dê sua opinião e possíveis melhorias no nosso modelo deixando seu comentário no artigo do blog


Referências:



Comentários

Postagens mais visitadas deste blog

Tutorial de Machine Learning com Python - Iniciantes - Parte 1

Regressão Múltipla com R

Tutorial de Machine Learning com Python - Iniciantes - Parte 2