Primeira vez aqui? Seja bem vindo e cheque o FAQ!
x

Um exercício de Machine Learning usando a base "Titanic: Machine Learning from Disaster" do Kaggle

+1 voto
20 visitas
perguntada Jun 10 em Aprendizagem de Máquinas por Monica Guo (16 pontos)  

Como prever se uma pessoa sobreviveria ou não ao Titanic de acordo com as informações disponíveis, como gênero, idade, quantidade de parentes no navio, preço pago na passagem, etc. Link do exercício no Kaggle

Compartilhe

1 Resposta

+1 voto
respondida Jun 10 por Monica Guo (16 pontos)  

As variáveis disponíveis para o problema são:

1) survival: variável binária a ser prevista (output), diz se a pessoa sobreviveu ou não;
2) pclass: classificação do ticket (primeira, segunda ou terceira classe)
3) sex: gênero (masculino ou feminino)
3) age: idade em anos
4) sibsp: número de irmãos/cônjuge a bordo no Titanic
5) parch: número de pais/crianças a bordo no Titanic
6) ticket: número do ticket
7) fare: preço pago pela passagem
8) cabin: número da cabine
9) embarked: portão de embarque (C = Cherbourg, Q = Queenstown e S = Southampton)

Em um primeiro momento, as variáveis escolhidas para prever survival foram: pclass, sex, age, sibsp, parch e fare. Com base nessas variáveis, foram construídos histogramas referentes a cada uma das variáveis e também um gráfico para ver a relação entre pclass e fare (uma vez que elas passam informações muito parecidas). Essa análise das variáveis permite identificar correlação entre variáveis, detectar possíveis outliers, o que influencia na qualidade de previsão do modelo. O código correspondente a essa análise é:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model, svm
from sklearn.neighbors import KNeighborsClassifier

if __name__ == '__main__':

    # Geração de número pseudo aleatório para que o resultado seja
    # replicável
    RANDOM_SEED = 31

    # Lendo os dados
    data = np.genfromtxt("train.csv", delimiter = ",", skip_header = 1)  
    y_data = data[:, 1]
    # Selecionando as colunas do arquivo de acordo com as variáveis escolhidas
    x_data = data[:,[2, 6, 7, 8, 10]]
    y_data = y_data.reshape(891, 1)

    # Concatenando x e y em uma matriz 
    xy_data = np.hstack((x_data, y_data))
    xy_data = xy_data[~np.isnan(xy_data).any(axis = 1)]

    # Separando novamente o x_data do y_data
    x_data = xy_data[:, : - 1]
    y_data = xy_data[:, -1]


    # Analisando os dados
    plt.hist(x_data[:, 0], bins = 3)
    plt.xlabel("Passenger Class")
    plt.title("Histograma de Passenger Class (pclass)")
    plt.show()

    plt.hist(x_data[:, 1])
    plt.xlabel("Age")
    plt.title("Histograma de Age (age)")
    plt.show()

    plt.hist(x_data[:, 2])
    plt.xlabel("Number of siblings/spouses")
    plt.title("Histograma de Number of siblings/spouses (sibsp)")
    plt.show()

    plt.hist(x_data[:, 3])
    plt.xlabel("Number of parents/children")
    plt.title("Histograma de Number of parents/children (parch)")
    plt.show()

    plt.hist(x_data[:, 4])
    plt.xlabel("Fare")
    plt.title("Histograma de Fare (fare)")
    plt.show()

    plt.hist(y_data, bins = 2)
    plt.xlabel("Survived")
    plt.title("Histograma de Survived (survived)")
    plt.show()

    plt.scatter(x_data[:, 0], x_data[:, 4])
    plt.xlabel("Pclass")
    plt.ylabel("Fare")
    plt.show()

A imagem será apresentada aqui.

A imagem será apresentada aqui.

A imagem será apresentada aqui.

A imagem será apresentada aqui.

A imagem será apresentada aqui.

A imagem será apresentada aqui.

A imagem será apresentada aqui.

Após essa análise das variáveis, foi decidido deixar de utilizar a variável pclass, uma vez que ela já estaria sendo representada pela variável fare. Além disso, há presença de alguns outliers, como pode ser visto pelos histogramas. Contudo, optou-se por utilizar todos os pontos, mas caso o modelo tivesse o desempenho prejudicado, os outliers seriam removidos.

    # Remoção da variável pclass
    x_data = x_data[:, 1:]

Agora, dividimos os dados em 3 partes: uma para treinamento, outra para validação e seleção do modelo e a terceira para o teste. Como os dados não aparentavam seguir nenhum padrão, ou seja, não estavam "arrumados", dividimos as 3 partes sequencialmente.

    # Dividindo os dados em 3 partes: training set, validation set e test set
    num_train = int(0.9 * x_data.shape[0]) # num de elementos no training set
    num_val = int(0.05 * x_data.shape[0]) # num de elementos no validations set

    # Training set
    x_train = x_data[:num_train + 1, :]
    y_train = y_data[:num_train + 1]

    # Validation set
    x_val = x_data[num_train + 1:num_train + 1 + num_val + 1, :]
    y_val = y_data[num_train + 1:num_train + 1 + num_val + 1]

    # Test set
    x_test = x_data[num_train + 1 + num_val + 1:, :]
    y_test = y_data[num_train + 1 + num_val + 1:]

Por fim, a implementação dos modelos. Todos os modelos foram implementados utilizando o pacote scikit-learn. Os parâmetros de cada modelo foram escolhidos com base no desempenho no validation set. Começamos com o modelo de classificação linear, que funciona como um benchmark para os outros modelos por ser um modelo mais simples. Em seguida, utilizamos o SVM linear com parâmetro de regularização (L2) igual a \(10^{-1}\). Depois implementamos o primeiro modelo não linear, o SVM com polinômio de grau 2. Por fim, fazemos o K Nearest Neighbors com o número de neighbors igual a 1. O código desses modelos e a performance no validation set é:

    # Classificação linear (benchmark)

    lin_model = linear_model.SGDClassifier(loss = "perceptron", 
                                           penalty = "none", max_iter = 10^8, 
                                           tol = 1e-5, 
                                           random_state = RANDOM_SEED)
    lin_model.fit(x_train, y_train)
    accuracy = lin_model.score(x_val, y_val)
    print("A acurácia do modelo linear no validation set é: " + str(accuracy))

    # SVM linear

    svm_lin_model = linear_model.SGDClassifier(loss = "hinge", 
                                           penalty = "l2", alpha = 1e-1,
                                           max_iter = 10^8, 
                                           tol = 1e-5, 
                                           random_state = RANDOM_SEED)
    svm_lin_model.fit(x_train, y_train)
    accuracy = svm_lin_model.score(x_val, y_val)
    print("A acurácia do SVM linear no validation set é: " + 
          str(accuracy))

    # SVM não linear

    svm_nonlinear_model = svm.NuSVC(kernel = "poly", degree = 2, 
                                    max_iter = 10^8, 
                                    tol = 1e-5, random_state = RANDOM_SEED)
    svm_nonlinear_model.fit(x_train, y_train)
    accuracy = svm_nonlinear_model.score(x_val, y_val)
    print("A acurácia do SVM não linear no validation set é: " 
          +str(accuracy))

    # K Nearest Neighbors

    knn_model = KNeighborsClassifier(n_neighbors = 1)
    knn_model.fit(x_train, y_train)
    accuracy = knn_model.score(x_train, y_train)
    print("A acurácia do modelo K Nearest Neighbors no validation set é: "
          + str(accuracy))

Os resultados obtidos foram:

A acurácia do modelo linear no validation set é: 0.7777777777777778
A acurácia do SVM linear no validation set é: 0.75
A acurácia do SVM não linear no validation set é: 0.8333333333333334
A acurácia do modelo K Nearest Neighbors no validation set é: 0.9720062208398134

Por fim, com os modelos totalmente definidos, tentamos estimar a capacidade de generalização avaliando os modelos no test set.

    # Testando os modelos para o test set

    accuracy_lin_test = lin_model.score(x_test, y_test)
    accuracy_svm_lin_test = svm_lin_model.score(x_test, y_test)
    accuracy_svm_nonlinear_test = svm_nonlinear_model.score(x_test, y_test)
    accuracy_knn_model_test = knn_model.score(x_train, y_train)
    print(accuracy_lin_test)
    print(accuracy_svm_lin_test)
    print(accuracy_svm_nonlinear_test)
    print(accuracy_knn_model_test)

Os resultados obtidos foram:

Modelo linear: 0.6571428571428571
SVM linear: 0.7142857142857143
SVM não linear: 0.6
K Nearest Neighbors: 0.9720062208398134

Pode-se notar que todos os modelos são melhores que simplesmente chutar se a pessoa sobrevive ou não (o que resulta em uma acurácia de 0.5). Além disso, percebe-se que a performance de todos os modelos é melhor no validation set se comparado com o test set. Isso acontece pois escolhemos os parâmetros que produziam o melhor desempenho no validation set. Enquanto isso, o test set possuem dados nunca vistos que são utilizados para estimar a capacidade de generalização dos modelos.

O modelo que apresentou o melhor resultado foi o K Nearest Neighbors com k igual a 1. Nesse modelo, dado um novo ponto para teste, ele escolhe o ponto visto no treinamento mais próximo e atribui a mesma classe para o novo ponto.

comentou Jun 18 por Stuart Mill (1,034 pontos)  
O código está bastante claro e conciso. Foi bom ter incluído o histograma para se ter uma ideia geral das variáveis também. Interessante notar a acurácia muito alta do modelo dos K-nearest neighbours... Você acha que daria para aumentar a acurácia sem tirar nenhuma variável a priori? Como é feita essa decisão? (Também estou querendo fazer uma questão como essa.)

Outra coisa, quando fui rodar o código, recebi avisos de não convergência dentro do número máximo de iterações especificado. Você acha que poderia ter algum viés na avaliação da performance dos modelos ou, dado o parâmetro de número de iterações, a convergência já deve estar próxima o suficiente?
...