Conheça o Kaggle e participe da sua primeira competição de Machine Learning

Olá, bem vindo de volta!

Neste artigo vamos aprender o que exatamente é o Kaggle, por que todo aspirante a Cientista de Dados deve conhece-lo e também iremos participar da nossa primeira competição! Tudo será feito usando a linguagem Python, através da distribuição Anaconda e da IDE Spyder.

Observação: Neste tutorial, estou considerando que você já tenha uma pequena noção do que é Machine Learning, caso não tenha, é só dar uma lida nesse artigo. Lembrando que já fiz outro tutorial que usei o Kaggle e expliquei brevemente do que se trata, você pode conferir clicando aqui.

 

O que é o Kaggle?

 

O Kaggle é uma plataforma fundada em 2010. Seu objetivo era ser um espaço onde empresas, instituições etc publicassem desafios de análise de dados e profissionais do mundo todo competiam entre si para produzir os melhores resultados.

Conforme foi crescendo, o Kaggle se tornou não apenas um ambiente para competições mas sim o principal ponto de colaboração na comunidade de Data Science. A plataforma vem crescendo tanto que no início de 2017 foi adquirida pelo Google.

 

 

Competições

 

Sem dúvida, o destaque da plataforma são as competições, que variam bastante de finalidade. Cada “anunciante” posta um desafio com a base de dados a ser analisada, junto de uma rica documentação. Normalmente, todo o trabalho de pré-processamento já foi feito quando uma nova base é inserida.  O mais interessante é que, apesar de ser um ambiente de disputas, os participantes são muito colaborativos dentro dos fóruns de discussão. O desafio a ser resolvido pode ser feito com a ferramenta que o competidor quiser. As linguagens mais comuns são Python, R e Julia.

Como é de se esperar, as competições mais concorridas são as que oferecem recompensas em dinheiro. Na imagem abaixo você confere as principais competições e seus prêmios no momento em que escrevo este artigo.

 

Sim, existem empresas pagando 100 mil dólares para quem conseguir resolver o desafio!

 

Repositório de Dados

 

Não só de competições vive o Kaggle. Hoje em dia sua plataforma é uma das mais ricas fontes de dados. Bases das mais diversas áreas do conhecimento são postadas lá. Por exemplo:

Nem todos as bases de dados são oriundas de competições. Os usuários tem liberdade de postar data sets e criar tópicos para discussões e análises. Por exemplo, um usuário fez esta análise do Cartola Fc.

Além de tudo isso, o Kaggle também possui anúncios de vagas de empregos (no mundo todo) para profissionais da área de dados. Inclusive, ter uma boa posição e boas análises na plataforma pode ser um grande portfólio para Cientistas, Engenheiros Dados etc que pleitearem vagas.

 

Nossa primeira competição

 

No Kaggle existem diversas competições voltadas para iniciantes, que obviamente não valem prêmios mas são ótimas para se iniciar na área de Data Science. A mais conhecida delas é o desafio de prever quais passageiros iriam sobreviver ao naufrágio do Titanic. E essa competição iremos resolver agora.

A primeira coisa a se fazer é baixar os data sets de treino e de teste através deste link. Você deverá colocar esses dados no mesmo diretório do script .py onde será desenvolvida a solução do problema proposto.

Vale lembrar que os dados de treino contém todas as features (colunas, atributos) que serão utilizadas para a construção do modelo de predição, inclusive a chamada variável target, que se quer prever. Neste caso é a variável binária Survival, onde 0 indica que o passageiro não sobreviveu e 1 indica a sobrevivência. Já nos dados de teste, que são os que avaliarão o modelo, estão presentes as mesmas features do treino com exceção da variável target.  Normalmente todo o conjunto de dados é dividido entre 70% – 80% para dados de treino e os restante para teste.

 

 

Agora, a primeira coisa que faremos no código é importar os dados de treino e teste usando a biblioteca Pandas e logo em seguida vamos verificar a quantidade de registros em cada base. Diferentemente dos outros tutoriais, vou explicando as bibliotecas a medida que forem sendo usadas (embora não seja uma boa prática de programação, acredito ser mais didático para fins de aprendizado).

 

Explorando os dados

 

Vamos dar uma olhada no dicionário de dados que o Kaggle disponibiliza junto a base e entender o que significa cada coluna.

  • PassengerId: Número de identificação do passageiro;
  • Survived: Indica se o passageiro sobreviveu ao desastre. É atribuído o valor de 0 para aqueles que não sobreviveram, e 1 para quem sobreviveu;
  • Pclass: Classe na qual o passageiro viajou. É informado 1 para primeira classe; 2 para segunda; e 3 para terceira;
  • Name: Nome do passageiro;
  • Sex: Sexo do passageiro;
  • Age: Idade do passageiro em anos;
  • SibSp: Quantidade de irmãos e cônjuges a bordo ;
  • Parch: Quantidade de pais e filhos a bordo;
  • Ticket: Número da passagem;
  • Fare: Preço da passagem;
  • Cabin: Número da cabine do passageiro;
  • Embarked: Indica o porto no qual o passageiro embarcou. Há apenas três valores possíveis: Cherbourg, Queenstown e Southampton, indicados pelas letras “C”, “Q” e “S”, respectivamente.

Vamos dar uma olhada nos primeiros registros do data frame train com o seguinte comando.

Ou podemos usar o explorador de variáveis do Spyder e ver em um formato mais agradável aos olhos.

 

 

O tipo de Machine Learning que iremos realizar aqui é a classificação, pois estamos tentando prever se um passageiro sobreviveu ou não. Esse tipo de problema é chamado de classificação binária, ou seja, só existem dois estados possíveis.

Em todo problema de ML é muito importante separar um tempo para avaliar o problema de negócio que será resolvido. Esse processo é chamado de aquisição de conhecimento.

No caso do Titanic, avaliar quais colunas seriam importantes variáveis preditoras será fundamental para a construção de um bom modelo.

Todo mundo que viu o filme Titanic sabe que na hora de naufrágio, as mulheres e crianças tinham preferência nos botes salva vidas além da grande disparidade entre os passageiros das melhores classes e os de classe inferior. Sendo assim, vamos verificar como se comportam as colunas Age, Sex e Pclass na relação entre sobreviventes versus pessoas que morreram. Faremos essa observação usando a biblioteca Matplotlib, umas das principais ferramentas de visualização de dados no Python.

Por conta da presença da coluna Survived, podemos segmentar os dados pelo sexo dos passageiros através do método DataFrame.pivot_table():

Podemos ver claramente que a proporção de mulheres sobreviventes é significantemente maior que a de homens. Vamos fazer o mesmo processo para a coluna Pclass:

Agora vamos verificar como se deu a distribuição de idades no naufrágio:

Podemos ver que a variedade de idade é bem grande, desde um bebê com menos de 5 meses até idosos de 80 anos. Outro ponto importante é a contagem de registros indicado pelo count. O output nos diz que existem 714 idades, sendo que no início do código nós descobrimos que são 814 linhas nos dados de treino. Ou seja, temos ocorrência de missing data (dados faltantes) na coluna idade. Vamos precisar fazer um trabalhinho nela, pois os algoritmos de Machine Learning não conseguem trabalhar com esse tipo de problema.

Para começar a pensar em uma forma de resolver esse problema, vamos olhar a distribuição de idades entre sobreviventes e mortos. Usaremos o método da indexação booleana para separar os dados e depois criaremos um histograma com o matplotlib:

O relacionamento parece bem complicado mas analisando com atenção, podemos ver que existe um maior índice de sobrevivência em algumas idades. Para facilitar a vida do modelo de Machine Learning, vamos transformar a variável contínua Age para do tipo categórica. Faremos isso dividindo as idades em intervalos através da função pandas.cut(). Essa função possui dois parâmetros obrigatórios: a coluna que desejamos “cortar” e uma lista de números que definem os limites dos nossos cortes. Também iremos usar um parâmetro opcional, que leva uma lista de nomes para cada parte do corte. Isso facilitará a compreensão de nossos resultados.

Aliado a tudo isso, vamos usar o método pandas.fillna() para substituir todos os valores faltantes por -0.5 (você já vai entender).  Vamos então segmentar as idades entre os seguintes intervalos:

  • Missing, from -1 to 0
  • Infant, from 0 to 5
  • Child, from 5 to 12
  • Teenager, from 12 to 18
  • Young Adult, from 18 to 35
  • Adult, from 35 to 60
  • Senior, from 60 to 100

Lembrando que tudo que fazemos nos dados de treino, também precisará ser feito nos dados de teste.

 

Agora vamos ver de uma maneira mais agradável, como se deu o índice de sobrevivência entre as idades.

 

Preparando os dados para o modelo de Machine Learning

 

Até agora nós identificamos que 3 colunas podem ser boas preditoras: Age, Sex e Pclass. Antes de construir o modelo, precisamos fazer um trabalho de pré-processamento nessas colunas. A maioria dos algoritmos de Machine Learning não conseguem lidar com variáveis categóricas, então precisamos transformar tais variáveis em números. Fazendo esse tipo de ação, é preciso ter atenção para não criarmos nenhuma relação numérica que não existe, caso contrário, isso irá prejudicar muito o modelo.

Por exemplo, a variável Pclass é dividida em três valores: 1,2 e 3. Embora a classe de cada passageiro certamente tenha algum tipo de relacionamento ordenado, a relação entre cada classe não é a mesma que a relação entre os números 1, 2 e 3. A a classe 2 vale o dobro da classe 1 e a classe 3 vale o  triplo da classe 1.

Podemos resolver isso criando uma estrutura chamada Dummy Columns, para cada valor único na coluna Pclass. A figura abaixo demonstra o que isso significa:

Podemos fazer isso com a função pandas.get_dummies(), que irá criar colunas exatamente como a imagem acima indica. Além disso, iremos criar uma função para substituir essa coluna no data frame original, tanto no treino como no testo. E também faremos o mesmo processo para as colunas Sex e Age, que assim como a Pclass, são categóricas.

 

Criando o modelo

 

Agora que estamos com os dados preparados, é hora de criarmos o modelo de predição. Para isso, usaremos o LogisticRegression, que normalmente é o primeiro modelo que se cria para problemas de classificação. Lembrando que Machine Learning é um processo interativo, onde se avalia constantemente os resultados.

Para esse parte do código, usaremos a biblioteca scikit-learn, uma das ferramentas de machine learning mais famosas atualmente. De forma simplificada, o worksflow do scikit-learn consiste nas seguintes etapas:

  • Instanciar o modelo desejado (no caso, LogisticRegression )
  • Ajustar o modelo ao conjunto de treino
  • Usar o modelo para realizar predições
  • Avaliar a acurácia

Vamos começar importando a classe LogisticRegression da biblioteca:

Agora, criaremos o objeto LogisticRegression:

Por fim, usamos o método LogisticRegression.fit () para treinar nosso modelo. O método .fit () aceita dois argumentos: X e y. O primeiro deve ser uma matriz bidimensional (como um dataframe) dos recursos que desejamos treinar nosso modelo e y deve ser uma matriz unidimensional (como uma série) do nosso alvo ou a coluna que desejamos prever.

O código acima treina o modelo usando somente as 3 colunas indicadas. Agora usaremos todas as colunas que criamos com o create_dummies():

Agora que temos o modelo treinado, é hora de entrar na segunda parte do workflow. Vamos fazer predições e avalia-las.

Lembrando que temos os dados de teste para fazer previsões. Podemos fazer previsões nesse conjunto de dados, mas, como não tem a coluna Survived, teríamos que enviar o resultado para a Kaggle para descobrir nossa precisão. Isso  seria complicado, pois precisaríamos enviar sempre que otimizamos nosso modelo. Nós também poderíamos ajustar e prever  nossos dados de treino, no entanto, se fizermos isso, existe uma grande probabilidade do nosso modelo sobre overfitting (ao invés dele aprender, decora os dados), o que significa que ele irá funcionar bem nos dados de treinamento, mas terá um desempenho muito ruim em novos dados.

Para resolver esse problema, podemos dividir os dados de treino em duas partes. Uma parte para treinar nosso modelo (80% das observações) e a outra (20%) para fazer previsões e testar nosso modelo. A convenção em aprendizagem de máquinas é chamar essas duas partes de treino e teste. Isso pode ser bastante confuso, pois já temos os conjuntos originais teste (que usaremos para fazer previsões submetidas ao Kaggle). Para evitar essa confusão, vamos denominar os dados de teste do Kaggle de houldout, que é o nome técnico usado para dados realizarem as previsões finais.

A biblioteca scikit-learn possui a função model_selection.train_test_split (), que podemos usar para dividir nossos dados. Train_test_split () aceita dois parâmetros, X e y, que contêm todos os dados que queremos treinar e testar e retorna quatro objetos: train_X, train_y, test_X, test_y:

Você pode notar que usamos alguns parâmetros extras: test_size, que nos permite controlar as proporções em que nossos dados são divididos e random_state. A função train_test_split ()  torna as observações aleatórias antes de dividi-las, além de definir uma seed aleatória, que torna os resultados reprodutíveis, para que você possa acompanhar e obter o mesmo resultado daqui.

Agora que temos nossos dados divididos em conjuntos de treino e teste, podemos ajustar nosso modelo novamente e, em seguida, usar esse modelo para fazer previsões. Uma vez que ajustamos no nosso modelo, podemos usar o método LogisticRegression.predict () para fazer previsões. Esse método requer um único parâmetro X, uma matriz bidimensional de atributos para as observações que desejamos prever. Esse X deve ter exatamente os mesmos atributos que a matriz que usamos para ajustar o modelo. O método retorna uma matriz de previsões.

Existem muitas formas de avaliar a acurácia de um modelo mas quando se está competindo no Kaggle, precisamos usar o mesmo método que ele utiliza para avaliar o resultado final, que no caso é a quantidade de passageiros que foram corretamente previstos. Novamente vamos usar a biblioteca scikit-learn. Com a função metrics.accuracy_score(), passamos dois parâmteros: y_true e y_pred, o output é um número que nos diz a percentagem de acertos.

Nosso modelo conseguiu uma precisão de 81,0%. Dado que o conjunto de teste é bastante pequeno (20%), há uma boa chance do modelo ter sofrido de overfitting e com isso, não funcionará em dados totalmente novos.

Para nos dar uma melhor compreensão do desempenho real do modelo, podemos usar uma técnica chamada cross validation, para treinar e testar o modelo em diferentes divisões de nossos dados e, em seguida, calcular a média de precisão.

A forma mais comum de cross validation, e a que usaremos, é chamada K-Fold. ‘Fold’ refere-se a cada iteração diferente em que treinamos o modelo, e ‘k’ apenas se refere ao número de Fold. No diagrama acima, temos ilustrado a validação k-fold onde k é 5. Usaremos a função model_selection.cross_val_score () do scikit-learn para automatizar o processo. A sintaxe básica para cross_val_score () é:

Onde,

  • estimador é um objeto, como LogisticRegression () que criamos.
  • X são todos os atributos do nosso conjunto de dados.
  • y é a variável target.
  • cv especifica o número de Fold.

A função retorna um array numpy dos score de precisão de cada Fold. Vale notar que, a função cross_val_score () pode usar uma variedade de técnicas de cross validation, mas o default é o tipo k-fold. Usaremos model_selection.cross_val_score () para realizar validação cruzada em nossos dados, antes de calcular a média das pontuações produzidas:

Pelo output, você pode ver que o número de precisão varia com cada Fold – variando entre 76,4% e 87,6%. Isso demonstra por que a cross validation é importante. Nossa precisão média foi de 80,2%, que não é longe dos 81,0% que atingimos divisão simples treino/teste, porém, nem sempre isso acontecer e usamos a cross validation jutamente para validar as métricas de erros.

Agora estamos prontos para usar o modelo que construímos para fazer previsões em nossos dados houldout, ou o que o Kaggle chama de “teste”.

O passo seguinte é colocar as previsões em um arquivo csv e submeter o resultado ao Kaggle. Quando eu fiz isso usando tudo o que criamos até aqui, a acurácia final foi de 75% aproximadamente, o que significa que nosso tratamento de overfitting não foi tão bom assim ou o nossa abordagem de solução de problema não foi muito eficaz. Sendo assim, vamos escolher outro algoritmo para resolver o desafio e usar outra estratégia de divisão de dados treino/teste. Como eu disse, o processo de Machine Learning é bastante interativa e o cientista de dados precisa sempre avaliar os resultados obtidos.

 

Construindo outro modelo

 

Para essa segunda tentativa, a primeira coisa que faremos é importar os dados de treino e teste mais uma vez, pois já havíamos os modificado bastante.

Agora vamos avaliar os dados que temos. Olhando para eles, podemos perceber que certas colunas não irão ser úteis para a construção. Esse é o caso da coluna Name, Ticket e Cabin. Você pode até dizer que o número da cabine pode ser importante, pois indica a classe onde o passageiro estava. Mas lembre-se que já existe uma coluna dedicada isso, a Pclass. Vamos  usar o método dataframe.drop(), do pandas, para excluir essas colunas indesejadas. Note que, como são várias colunas a serem retiradas, eu passei os nomes através de uma lista (caracterizada pelos colchetes []), em seguido passo o parâmetro axis = 1, indicando que toda coluna será removida, e não somente uma linha. E por último, o inplace = True salva as alterações direto no dataframe original, sem precisar armazenar as mudanças em outro objeto.

 

Preparando os dados para o modelo de Machine Learning

 

Da mesma forma como fizemos anteriormente, precisaremos tratar as variáveis categóricas do problema, pois o algoritmo de ML consegue trabalhar apenas com números. Usaremos de novo o método get_dummies() mas dessa vez, criaremos um novos data frames para armazenar as mudanças feitas.

Como passei como parâmetro um data frame inteiro (poderia ser colunas específicas) no get_dummies, todas as colunas não numéricas serão codificadas.

Agora vamos verificar a existência de valores nulos (do trabalho no último modelo já sabemos que existem missing data, mas vamos descobri-los de outra forma).

Na última linha de código, começamos indicando qual data frame seria analisado, no caso, new_data_train. Usamos o método isnull() para retornar todos os valores nulos encontrados e o .sum() para somar todas as ocorrência e agrupa-las. E o .sort_values(ascending = False) para ordenar os dados do maior para o menor. O output indicou que existem 177 valores faltantes na coluna Age. Como não é possível saber esses valores, vamos precisar imputar valores baseados em alguma estratégia. Substituir missing data é uma tarefa muito delicada e é um campo de estudo por si só. Mas para simplificar, irei inserir a idade média nesses casos. Esse tipo de abordagem varia bastante de problema para problema.

Para imputar valores nulos, utilizamos a função .fillna(). Como argumento, passamos o que deve ser inserido nos campos que não tiveram um valor definido, nesse caso, a média de idades da coluna Age.

Agora, vamos verificar a ocorrência de valores faltantes no conjunto de teste, utilizando código semelhante ao caso anterior.

Só temos um caso de missing data, na coluna Fare. Vamos imputar a média dos valores mais uma vez.

 

Criando o modelo

 

Com os dados ok, já podemos partir para a criação do modelo. Vamos fazer uso de outro algoritmo muito conhecido e bastante simples, o Decision Tree (Árvore de decisão).

A forma com que esse algoritmo funciona é similar a de um fluxograma: se determinada condição acontecer, um caminho é tomado, caso contrário, outro caminho é feito. A Árvore é composta pela estrutura completa desse fluxograma. Confira na ilustração abaixo um exemplo.

 

O algoritmo da termina quando todas as “folhas” forem respondidas. Existem casos onde nós podemos definir um grau de profundidade para a árvore, a fim de não deixá-la muito extensa e nosso modelo lento. O importante é você entender como a estrutura de uma Árvore de Decisão funciona, pois o Scikit-Learn fará todo esse trabalho por nós.

Vamos então fazer o split dos dados para o modelo. Criaremos duas variáveis: x, que irá armazenar os dados do passageiro e y, que terá somente a variável target, o que queremos prever.

Por fim, vamos criar o modelo. Precisamos instanciar uma variável do tipo DecisionTreeClassifier (da mesma forma como fizemos para o outro algoritmo). Perceba que foi passado o parâmetro max_depth, para limitar o tamanho da árvore, que no caso será 3 (Durante, essa configuração foi a que gerou uma precisão maior no modelo). Após criarmos a variável, usamos o método .fit(), para ajustar o modelo aos dados (x e y).

E criamos nosso modelo! Agora vamos avaliar seu nível de precisão usando o método score().

Nosso modelo atingiu um resultado interessante. Nesse código,verificamos a precisão utilizando o próprio conjunto de treino, mas, é possível realizá-lo no conjunto de teste.

 

Submetendo o resultado ao Kaggle

 

Segundo as regras da competição, precisamos enviar ao Kaggle um arquivo .csv contendo as previsões feitas. Esse arquivo deve conter somente duas colunas: PassangerId e Survived. Vamos então usar a biblioteca pandas para fazer isso.

Nesse código, nós declaramos um data frame vazio e após isso, criamos as duas colunas pedidas. Na coluna PassengerId, armazenamos o valor da coluna de mesmo nome do conjunto de teste (new_data_test). Em seguida, colocamos dentro da coluna Survived, os resultados da previsão de nosso modelo no conjunto de teste, utilizando a função predict().

Finalmente, vamos exportar o dataframe para um arquivo .csv, de novo usando o Pandas, com o método .to_csv(). Passamos como parâmetro o nome do arquivo que será criado e colocamos o index = False para não criar um index indesejado (que seja o PassengerId).

E a última etapa que precisamos fazer (Ufa!), é ir na página da competição no Kaggle e submeter o arquivo que acabamos de criar (clicar no botão Submit Predictions).

 

Feito isso, você verá a opção para fazer upload do seu arquivo. Clique sobre esse botão e escolha o arquivo gerado no seu diretório.

Após o upload ser concluído, é só clicar em Make Submission. E pronto, você participou da sua primeira competição no Kaggle!

 

Após o envio do arquivo, o Kaggle te informa a posição em que ficamos, baseado no score atingido pelo nosso modelo. Perceba que o score foi de 0.779, diferente do 0.82 que encontramos quando o modelo foi testado. Isso indicado, mais uma vez, a ocorrência de overfitting.

Conclusão

 

E chegamos ao fim de mais um tutorial. Você pode ter visto que não ficamos em uma posição muito boa com o nosso algoritmo mas como já disse, Machine Learning é um processo iterativo que sempre busca melhorar os resultados. Talvez, implementando outros algoritmos e usando outras técnicas de pré-processamento, conseguiríamos uma posição melhor no ranking. Isso serve de motivação para continuarmos avançando nos estudos!

Observação: não se deixe enganar com muitos score’s acima de 90%. Muito provavelmente essas pessoas estão usando métodos para “roubar”, pois como é um problema que dispõe de poucos dados de treino, dificilmente um modelo atingiria tal precisão de acerto.

 

Referências

 

Galera, para este tutorial, me baseei no excelente artigo do Paulo Vasconcellos, que é Cientista de Dados na MaxMilhas e no artigo do José Guilherme Lopes, outra grande referência em Data Science que possui um blog muito informativo. E também me baseei no artigo muito didático da Dataquest.

Para quem deseja se aprofundar mais no Kaggle vale dar uma olhada neste material da Dataquest e neste curso, que a Data Camp disponibiliza de graça.

 

 

É isso ai pessoal, espero que tenham gostado desse tutorial e nos vemos no próximo!

Abraço

 

 

 

Article By :