Nous reprenons le jeu de données portant sur les transactions immobilières dans le conté de King, et nous allons établir un modèle d’apprentissage supervisé avec le package Graphlab create.
Dans un premier temps, le modèle se contentera du plus proche voisin, puis nous augmenterons le nombre de voisins pour trouver un optimum. En n’oubliant pas qu’ici la variance va diminuer avec le nombre de voisins alors que le bias augmentera, nous chercherons le bon compromis.
L’exercice présenté dans ce post est issu du cours sur la régression dans le parcours “Machine Learning” de l’université de Washington sur Coursera. Nous attaquons la dernière semaine de cours portant sur la régression par la méthode des k plus proches voisins.
import graphlab import numpy as np sales = graphlab.SFrame('kc_house_data_small.gl/kc_house_data_small.gl/')
Le bloc de code ci-dessous nous permet de convertir un SFrame en Array numpy 2D, pour le calcul des distances.
def get_numpy_data(data_sframe, features, output): data_sframe['constant'] = 1 # Ajout d'une colonne constante à un SFrame # On ajoute la colonne "constant" devant la liste des variables pour pouvoir l'extraire avec les autres features = ['constant'] + features # Sélection des features de data_SFrame donnée par la liste des features dans le SFrame features_sframe (avec la constante): features_sframe = data_sframe[features] # On convertit features_SFrame en matrice numpy: feature_matrix = features_sframe.to_numpy() output_sarray = data_sframe[output] output_array = output_sarray.to_numpy() return(feature_matrix, output_array)
Pour le calcul de distances il faut impérativement normaliser les variables. Autrement, par exemple la surface totale (de l’ordre de plusieurs centaines de m²) aura une influence disproportionnée par rapport au nombre de chambres (limité à 11). La fonction ci-dessous permet de normaliser nos données et retourne également les coefficients de normalisation.
def normalize_features(feature_matrix): norms = np.linalg.norm(feature_matrix,axis=0) normalized_features = feature_matrix / norms return (normalized_features,norms)
Je sépare ensuite les variables en jeu de train/test/validation, puis je crée une liste des variables que je normalise.
(train_and_validation, test) = sales.random_split(.8, seed=1) # initial train/test split (train, validation) = train_and_validation.random_split(.8, seed=1) # Séparation du training set en training et validation feature_list = ['bedrooms', 'bathrooms', 'sqft_living', 'sqft_lot', 'floors', 'waterfront', 'view', 'condition', 'grade', 'sqft_above', 'sqft_basement', 'yr_built', 'yr_renovated', 'lat', 'long', 'sqft_living15', 'sqft_lot15'] features_train, output_train = get_numpy_data(train, feature_list, 'price') features_test, output_test = get_numpy_data(test, feature_list, 'price') features_valid, output_valid = get_numpy_data(validation, feature_list, 'price') features_train, norms = normalize_features(features_train) # Normalisation des variables du training set (colonnes) features_test = features_test / norms # Normalisation du test set par les normes du training test features_valid = features_valid / norms # Normalisation du validation set par les normes du training set
Le calcul de la distance entre une maison donnée (query) et chaque des maisons du training set se fait simplement via la fonction ci-dessous (axis=1 permet de sommer sur chaque ligne).
def compute_distance(train,query): distance = np.sqrt(np.sum((train - query)**2,axis=1)) return distance
La fonction ci-dessous permet de trouver le k plus proches voisins.
def get_k_nearest(k, feature_matrix, feature_vector): distances = compute_distance(feature_matrix, feature_vector) index_array = np.argsort(distances)[0:k] return index_array
Fonction pour faire une prédiction
#k est le nombre de voisins, feature_matrix celle sur laquelle on va comparer, feature_set est la query def predict(k, feature_matrix, output_values, feature_set): #On récupère les indices des voisins sur lesquels on veut faire la moyenne index = get_k_nearest(k,feature_matrix,feature_set) #Une fois qu'on a identifié les k voisins, on fait simplement la moyenne de leurs prix predicted_value = np.mean(output_values[index]) return predicted_value
Maintenant on peut écrire une fonction pour calculer le prix de chaque maison d’un set donné. En entrée on donne le nombre de voisins, la matrice à utiliser, les paramètres à considérer.
def predict_full_set(k,feature_matrix, output_values, feature_set): n_houses = feature_set.shape[0] predicted_values = [predict(k,feature_matrix,output_values,feature_set[i]) for i in range(0,n_houses)] return predicted_values
Le RSS est calculé comme suit :
def get_residual_sum_of_squares(predictions, output): # Then compute the residuals/errors residual = output - predictions # Then square and add them up residual_squared = residual * residual RSS = residual_squared.sum() return(RSS)
Pour trouver le nombre de plus proches voisins optimum, je réalise des prédictions pour des valeurs de k jusqu’à 16, et je calcule à chaque fois le RSS.
RSS_full = [] for i in range(1,16): prediction_i = predict_full_set(i,features_train,output_train,features_valid) RSS = get_residual_sum_of_squares(prediction_i,output_valid) RSS_full.append(RSS) import matplotlib.pyplot as plt %matplotlib inline kvals = range(1, 16) plt.plot(kvals, RSS_full,'bo-')
Le résultat est tracé ci-dessous :
Ainsi le RSS le plus faible est obtenu pour k = 8 voisins.