Introduction

Les utilsateurs de R ont très souvent l’habitude d’utiliser en même temps plusieurs packages lorsqu’ils font du machine learning. Par exemple, on peut utiliser un package pour un le traitement des valeurs manquantes, puis on construit le modèle avec un autre package et enfin on évalue les performances du modèle avec un autre.

Le problème avec cette approche c’est que chaque package a ses propres spécificités et ses propres paramètrages. Ainsi en travaillant sur plusieurs packages on perd du temps à chercher et à compendre les paramètres de chaque librairie afin de pouvoir identifier les plus importants. Pour palier à ce problème, un nouveau package a été mis en place en février 2016. Il s’appelle mlr. Ce package contient presque toutes les algorithmes de machine learing que l’on utilise très fréquemment.

Dans l’étude de cas de ce tutoriel, nous allons nous intéresser à un problème de classsification dans lequel nous chercherons à améliorer le taux de bon classement en utilisant le machine learning. Nous n’allons pas expliquer en profondeur les algorithmes qui sont utilisés mais nous nous focaliserons sur leur implémentation qui est d’une simplicité rare avec mlr. A la fin de ce tutoriel, vous serez en mesure de réalisser sur vos données au moins une des taches que nous présenterons dans la suite.

Ce tutoriel a pour but de guider les personnes souhaitant utiliser le package MLR pour pour faire du machine learning sous r.

Il comporte cinq etapes :

  1. Chargement des données
  2. L’exploration des données
  3. Le traitement des valeurs manquantes
  4. La création de nouvelles variables
  5. L’application des méthodes de machine learning

Machine learning avec mlr

Jusqu’ici, R ne disposait d’aucune librarie similaire à Scikit-Learn dans Python dans laquelle on retrouve toutes les fonctionnalités pour faire du machine learning. Depuis février 2016, cela a changé avec la sortie de mlr. Nous allons essayer de comprendre les concepts de base qui régissent le fonctionnement de ce package.

Le quasi totalité du fonctionnement de cette librairie se structure comme suit: Créer une tache, Choisir un apprenant(une méthode).Effectuer l’apprentissage sur la base de la tache et de la méthode

Créer une tache consiste à charger des données dans le package. Choisir un apprenant signifie choisir un algorithme qui apprendra sur les données.

Defintion de l’espace de travail et chargement des données et de MLR

Les donnéees de l’étude proviennent de Telécharger les données. C’est une base de donnée très célèbre dans les articles de machine learning. Les données font référence au crédit scoring. Vous devez vous inscrire au site et participer au challenge pour être en mesure de télécharger ces données.

path <- "C:/Users/Data 1/Desktop/soccer";

setwd(path)

Installation des packages utiles pour l’analyse

# Reliez à MLR
if (!require(ParamHelpers)){install.packages("ParamHelpers")} ; library(ParamHelpers)
if (!require(BBmisc)){install.packages("BBmisc")} ; library(BBmisc)
if (!require(rpart)){install.packages("rpart")} ; library(rpart)
if (!require(xgboost)){install.packages("xgboost")} ; library(xgboost)
if (!require(FSelector)){install.packages("FSelector")} ; library(FSelector)
if (!require(kernlab)){install.packages("kernlab")} ; library(kernlab)
if (!require(gbm)){install.packages("gbm")} ; library(gbm)
if (!require(mlr)){install.packages("mlr")} ; library(mlr)

#Pour la mise en forme du markdown
if (!require(plotly)){install.packages("plotly")} ; library(plotly) # Pour le heatmap de la matrice de correlation
if (!require(FFTrees)){install.packages("FFTrees")} ; library(FFTrees) #pour l'arbre la représentation de l'arbre de décision 
if (!require(shiny)){install.packages("shiny")} ; library(shiny)
if (!require(qtlcharts)){install.packages("qtlcharts")} ; library(qtlcharts) # Pour la correlation des variables
if (!require(pander)){install.packages("pander")} ; library(pander) # pour bien afficher les extract de table

## Importation des données
train <- read.csv("train_loan.csv", na.strings = c(""," ",NA))
test <- read.csv("test_loan.csv", na.strings = c(""," ",NA))

## Vextract de la table
pander(head(train))
Table continues below
Loan_ID Gender Married Dependents Education Self_Employed
LP001002 Male No 0 Graduate No
LP001003 Male Yes 1 Graduate No
LP001005 Male Yes 0 Graduate Yes
LP001006 Male Yes 0 Not Graduate No
LP001008 Male No 0 Graduate No
LP001011 Male Yes 2 Graduate Yes
Table continues below
ApplicantIncome CoapplicantIncome LoanAmount Loan_Amount_Term
5849 0 NA 360
4583 1508 128 360
3000 0 66 360
2583 2358 120 360
6000 0 141 360
5417 4196 267 360
Credit_History Property_Area Loan_Status
1 Urban Y
1 Rural N
1 Urban Y
1 Urban Y
1 Urban Y
1 Urban Y

Exploration des données

Une fois les données chargées, nous pouvons voir le contenu de ces dernières

pander(summarizeColumns(train))
name type na mean disp median mad min max nlevs
Loan_ID factor 0 NA 0.9984 NA NA 1 1 614
Gender factor 13 NA NA NA NA 112 489 2
Married factor 3 NA NA NA NA 213 398 2
Dependents factor 15 NA NA NA NA 51 345 4
Education factor 0 NA 0.2182 NA NA 134 480 2
Self_Employed factor 32 NA NA NA NA 82 500 2
ApplicantIncome integer 0 5403 6109 3812 1823 150 81000 0
CoapplicantIncome numeric 0 1621 2926 1188 1762 0 41667 0
LoanAmount integer 22 146.4 85.59 128 47.44 9 700 0
Loan_Amount_Term integer 14 342 65.12 360 0 12 480 0
Credit_History integer 50 0.8422 0.3649 1 0 0 1 0
Property_Area factor 0 NA 0.6205 NA NA 179 233 3
Loan_Status factor 0 NA 0.3127 NA NA 192 422 2

Des sorties ci-haut nous tirons les conclusions suivantes:

  1. Nous avons 12 variables, dans lesquelles Loan_Status est la variable dependante et le reste constituent les variables independantes.
  2. L’échantillon d’apprentissage a 614 observations. L’échantillon test en a 367. L’échantillon test ne contient pas la variable Loan_Status il faut soumettre sa prédiction sur le site où les données ont été téléchargées.
  3. Dans les 2 échantillons, 6 variables contiennent des valeurs manquantes (on peut les voir dans la colonne na ).
  4. ApplicantIncome et Coapplicant Income sont des variables avec une distribution très assymmétrique. Comement le sait-on ? En regardant sur leur min max et median. Nous devons normaliser ces variables.
  5. LoanAmount, ApplicantIncome et CoapplicantIncome comporte des outliers , qui doivent être traités.
  6. Credit_History est de type entier Mais binaire on doit le convertir en facteur factor poour qu’elle puisse être traité dans les algo de mlr.

On peut aussi détecter l’assymétrie de la distribution avec un histogramme

#hist(train$ApplicantIncome, breaks = 300, main = "Revenu du demandeur",xlab = "ApplicantIncome")
ggplot(train, aes(ApplicantIncome)) + geom_histogram(binwidth = 100)+ggtitle("Distribution du Revenu du demandeur \n en dollar") +
  xlab("Revenu du demandeur") + ylab("Nombre de demandeurs de crédit")

#hist(train$CoapplicantIncome, breaks = 100,main = " Revenu du codemandeur",xlab = "CoapplicantIncome")

ggplot(train, aes(CoapplicantIncome)) + geom_histogram(binwidth = 100)+ggtitle("Distribution du Revenu du codemandeur \n en dollar") +
  xlab("Revenu du codemandeur") + ylab("Nombre de codemandeurs de crédit")

Comme on peut le voir sur ces deux graphiques, la grande queue distribution n’est rien d’autre que la concentration de la majorité des observations dans un côté du graphe. Nous avons donc une assymétrie à gauche. Pour mieux visualiser cela, on peut tracer un boxplot:

#boxplot(train$ApplicantIncome)
ggplot(train, aes(x = Gender, y = ApplicantIncome, fill = Gender)) + geom_violin()

On peut faire la même chose pour CoapplicantIncome et LoanAmount.

Changeons le type de la variable Credit_History en factor. Rappelons que la classe factor est toujours utilisée pour les variables catégorielles.

train$Credit_History <- as.factor(train$Credit_History)
test$Credit_History <- as.factor(test$Credit_History)

#Vérif:

class(train$Credit_History)
## [1] "factor"
pander(summary(train))
Table continues below
Loan_ID Gender Married Dependents Education Self_Employed
LP001002: 1 Female:112 No :213 0 :345 Graduate :480 No :500
LP001003: 1 Male :489 Yes :398 1 :102 Not Graduate:134 Yes : 82
LP001005: 1 NA’s : 13 NA’s: 3 2 :101 NA NA’s: 32
LP001006: 1 NA NA 3+ : 51 NA NA
LP001008: 1 NA NA NA’s: 15 NA NA
LP001011: 1 NA NA NA NA NA
(Other) :608 NA NA NA NA NA
Table continues below
ApplicantIncome CoapplicantIncome LoanAmount Loan_Amount_Term
Min. : 150 Min. : 0 Min. : 9.0 Min. : 12
1st Qu.: 2878 1st Qu.: 0 1st Qu.:100.0 1st Qu.:360
Median : 3812 Median : 1188 Median :128.0 Median :360
Mean : 5403 Mean : 1621 Mean :146.4 Mean :342
3rd Qu.: 5795 3rd Qu.: 2297 3rd Qu.:168.0 3rd Qu.:360
Max. :81000 Max. :41667 Max. :700.0 Max. :480
NA NA NA’s :22 NA’s :14
Credit_History Property_Area Loan_Status
0 : 89 Rural :179 N:192
1 :475 Semiurban:233 Y:422
NA’s: 50 Urban :202 NA
NA NA NA
NA NA NA
NA NA NA
NA NA NA
pander(summary(test))
Table continues below
Loan_ID Gender Married Dependents Education Self_Employed
LP001015: 1 Female: 70 No :134 0 :200 Graduate :283 No :307
LP001022: 1 Male :286 Yes:233 1 : 58 Not Graduate: 84 Yes : 37
LP001031: 1 NA’s : 11 NA 2 : 59 NA NA’s: 23
LP001035: 1 NA NA 3+ : 40 NA NA
LP001051: 1 NA NA NA’s: 10 NA NA
LP001054: 1 NA NA NA NA NA
(Other) :361 NA NA NA NA NA
Table continues below
ApplicantIncome CoapplicantIncome LoanAmount Loan_Amount_Term
Min. : 0 Min. : 0 Min. : 28.0 Min. : 6.0
1st Qu.: 2864 1st Qu.: 0 1st Qu.:100.2 1st Qu.:360.0
Median : 3786 Median : 1025 Median :125.0 Median :360.0
Mean : 4806 Mean : 1570 Mean :136.1 Mean :342.5
3rd Qu.: 5060 3rd Qu.: 2430 3rd Qu.:158.0 3rd Qu.:360.0
Max. :72529 Max. :24000 Max. :550.0 Max. :480.0
NA NA NA’s :5 NA’s :6
Credit_History Property_Area
0 : 59 Rural :111
1 :279 Semiurban:116
NA’s: 29 Urban :140
NA NA
NA NA
NA NA
NA NA

On voit que la variable Dependents a un level 3+ qui doit être traité aussi. C’est très simple de le modifier le nom du level dans une variable de type factor. On le fait comme suit:

#Renommage de level
levels(train$Dependents)[4] <- "3"
levels(test$Dependents)[4] <- "3"

train$Dependents <- as.integer(as.character(train$Dependents))
test$Dependents <- as.integer(as.character(test$Dependents))

Traitement des valeurs manquantes

Ce ne sont pas uniquement les débutants sur R qui butent sur le traitement des valeurs manquantes, certains habitués de R ont également des difficultés sur cette partie. mlr offre une façon très conviviale d’imputer les valeurs manquantes avec de multiples méthodes.

Après qu’on ait réalisé les modifications les plus urgentes sur les données, imputons les valeurs manquantes.

Dans notre cas, nous allons utiliser les méthodes basiques que sont la moyenne et le mode. Vous pouvez utiliser d’autres algos de ML mais cela aura un coût sur le temps d’exécution de vos taches.

#Paramétrage des imputations des valeurs manquantes par la moyenne et le mode
imp <- impute(train, classes = list(factor = imputeMode(), integer = imputeMean()), dummy.classes = c("integer","factor"), dummy.type = "numeric")
imp1 <- impute(test, classes = list(factor = imputeMode(), integer = imputeMean()), dummy.classes = c("integer","factor"), dummy.type = "numeric")

Cette fonction est pratique par ce qu’on a pas besoin d’indiquer le nom de chaque variable à imputer. Elle selectionne les variables sur la base de leurs classes (numérique ou autre). Elle crée également un dummy pour les valeurs manquantes. Quelques fois ces variables (dummy) contiennent un trend qui peut être capturé. dummy.classes indique pour quelle classe on doit créer une variable dummy. dummy.type indique quelle doit être la classe de la nouvelle variable dummy.

$data est un argument de la fonction imp il indique la table sur laquelle appliquée l’imputation sachant que cete table a été indiquée dans le paramétrage des fonctions d’imputation.

imp_train <- imp$data
imp_test <- imp1$data

Maintenant qu’on a une table complète, on peut regarder les nouvelles variables qui sont sorties de la phase d’imputation:

pander(summarizeColumns(imp_train))
name type na mean disp median mad min max nlevs
Loan_ID factor 0 NA 0.9984 NA NA 1 1 614
Gender factor 0 NA 0.1824 NA NA 112 502 2
Married factor 0 NA 0.3469 NA NA 213 401 2
Dependents numeric 0 0.7629 1.003 0 0 0 3 0
Education factor 0 NA 0.2182 NA NA 134 480 2
Self_Employed factor 0 NA 0.1336 NA NA 82 532 2
ApplicantIncome numeric 0 5403 6109 3812 1823 150 81000 0
CoapplicantIncome numeric 0 1621 2926 1188 1762 0 41667 0
LoanAmount numeric 0 146.4 84.04 129 45.22 9 700 0
Loan_Amount_Term numeric 0 342 64.37 360 0 12 480 0
Credit_History factor 0 NA 0.145 NA NA 89 525 2
Property_Area factor 0 NA 0.6205 NA NA 179 233 3
Loan_Status factor 0 NA 0.3127 NA NA 192 422 2
Gender.dummy numeric 0 0.02117 0.1441 0 0 0 1 0
Married.dummy numeric 0 0.004886 0.06979 0 0 0 1 0
Dependents.dummy numeric 0 0.02443 0.1545 0 0 0 1 0
Self_Employed.dummy numeric 0 0.05212 0.2224 0 0 0 1 0
LoanAmount.dummy numeric 0 0.03583 0.186 0 0 0 1 0
Loan_Amount_Term.dummy numeric 0 0.0228 0.1494 0 0 0 1 0
Credit_History.dummy numeric 0 0.08143 0.2737 0 0 0 1 0
pander(summarizeColumns(imp_test))
name type na mean disp median mad min max nlevs
Loan_ID factor 0 NA 0.9973 NA NA 1 1 367
Gender factor 0 NA 0.1907 NA NA 70 297 2
Married factor 0 NA 0.3651 NA NA 134 233 2
Dependents numeric 0 0.8291 1.057 0 0 0 3 0
Education factor 0 NA 0.2289 NA NA 84 283 2
Self_Employed factor 0 NA 0.1008 NA NA 37 330 2
ApplicantIncome numeric 0 4806 4911 3786 1598 0 72529 0
CoapplicantIncome numeric 0 1570 2334 1025 1520 0 24000 0
LoanAmount numeric 0 136.1 60.95 126 38.55 28 550 0
Loan_Amount_Term numeric 0 342.5 64.62 360 0 6 480 0
Credit_History factor 0 NA 0.1608 NA NA 59 308 2
Property_Area factor 0 NA 0.6185 NA NA 111 140 3
Gender.dummy numeric 0 0.02997 0.1707 0 0 0 1 0
Dependents.dummy numeric 0 0.02725 0.163 0 0 0 1 0
Self_Employed.dummy numeric 0 0.06267 0.2427 0 0 0 1 0
LoanAmount.dummy numeric 0 0.01362 0.1161 0 0 0 1 0
Loan_Amount_Term.dummy numeric 0 0.01635 0.127 0 0 0 1 0
Credit_History.dummy numeric 0 0.07902 0.2701 0 0 0 1 0

Avez vous noté une différence entre les deux tables? Non ? Regardez encore. La réponse est la variable Married.dummy qui n’apparait que dans imp_train et pas dans imp_test. Il sera supprimé dans la partie de modélisation.

Transformation des données

La transformation des données est la partie la plus interessante dans un model predictif. Cette partie se divise en 2 aspects : la transformation de certaines variables et la création de nouvelles variables. Nous allons travailler ces deux aspects dans la suite.

Tout d’abord, supprimons les valeurs aberrantes des variables ApplicantIncome, CoapplicantIncome et LoanAmount.

Il y a plusieurs techniques pour supprimer les valeurs aberrantes. Nous allons capter toutes les outliers de ces variables et nous les bornerons avec des valeurs de seuils:

#Pour l'échantillon d'apprentissage
cd <- capLargeValues(imp_train, target = "Loan_Status",cols = c("ApplicantIncome"),threshold = 40000)
cd <- capLargeValues(cd, target = "Loan_Status",cols = c("CoapplicantIncome"),threshold = 21000)
cd <- capLargeValues(cd, target = "Loan_Status",cols = c("LoanAmount"),threshold = 520)

#renommage de l'échantillon d'apprentissage en cd_train
cd_train <- cd

#ajout d'un dummy pour de la variable Loan_Status dans l'échantillon test
imp_test$Loan_Status <- sample(0:1,size = 367,replace = T)

cde <- capLargeValues(imp_test, target = "Loan_Status",cols = c("ApplicantIncome"),threshold = 33000)
cde <- capLargeValues(cde, target = "Loan_Status",cols = c("CoapplicantIncome"),threshold = 16000)
cde <- capLargeValues(cde, target = "Loan_Status",cols = c("LoanAmount"),threshold = 470)

#renommage de l'échantillon test
cd_test <- cde

Nous avons choisis une valeur de seuil en analysant la distribution de la variable. Pour vérifier que les seuils ont bien été appliqués, vous pouvez tapez summary(cd_train$ApplicantIncome) et regarder la valeur maximale qui sera de 33000 pour ApplicantIncome.

Dans les 2 datasets on voit que toutes les variables dummies sont numeriques. Elles sont binaires donc nous devons les convertir en factor. Cette fois, nous allons utiliser une simple boucle for et if pour le faire sur toutes les variables.

#conversion numerique à factor - de train
for (f in names(cd_train[, c(14:20)])) {
if( class(cd_train[, c(14:20)] [[f]]) == "numeric"){
levels <- unique(cd_train[, c(14:20)][[f]])
cd_train[, c(14:20)][[f]] <- as.factor(factor(cd_train[, c(14:20)][[f]], levels = levels))
}
}

#conversion numerique à factor - de test
for (f in names(cd_test[, c(13:18)])) {
if( class(cd_test[, c(13:18)] [[f]]) == "numeric"){
levels <- unique(cd_test[, c(13:18)][[f]])
cd_test[, c(13:18)][[f]] <- as.factor(factor(cd_test[, c(13:18)][[f]], levels = levels))
}
}

Cette boucle dit : Pour chaque nom de variable dont la position est entre 14 et 20 de la base cd_train/ cd_test, si la classe de ces variables est numeric, prend chaque valeur unique de ces variables comme des levels et convertis les en factor.

Créons maintenant de nouvelles variables.

#Total_Income
cd_train$Total_Income <- cd_train$ApplicantIncome + cd_train$CoapplicantIncome
cd_test$Total_Income <- cd_test$ApplicantIncome + cd_test$CoapplicantIncome

#Revenue par crédit
cd_train$Income_by_loan <- cd_train$Total_Income/cd_train$LoanAmount
cd_test$Income_by_loan <- cd_test$Total_Income/cd_test$LoanAmount

#changement de la classe
cd_train$Loan_Amount_Term <- as.numeric(cd_train$Loan_Amount_Term)
cd_test$Loan_Amount_Term <- as.numeric(cd_test$Loan_Amount_Term)

#Montant de crédit par terme
cd_train$Loan_amount_by_term <- cd_train$LoanAmount/cd_train$Loan_Amount_Term
cd_test$Loan_amount_by_term <- cd_test$LoanAmount/cd_test$Loan_Amount_Term

#
#cd_train$Gender<-as.character(cd_train$Gender)
#cd_test$Gender<-as.character(cd_test$Gender)

#cd_train$Education<-as.character(cd_train$Education)
#cd_test$Education<-as.character(cd_test$Education)

#cd_train$Married<-as.character(cd_train$Married)
#cd_test$Married<-as.character(cd_test$Married)

#cd_train$Self_Employed<-as.character(cd_train$Self_Employed)
#cd_test$Self_Employed<-as.character(cd_test$Self_Employed)

#cd_train$Property_Area<-as.character(cd_train$Property_Area)
#cd_test$Property_Area<-as.character(cd_test$Property_Area)

#cd_train$Loan_Status<-as.character(cd_train$Loan_Status)
#cd_test$Loan_Status<-as.character(cd_test$Loan_Status)

En créant de nouvelles variables (s’ils sont numériques), nous devons nous assurer qu’elles ne sont pas correlées avec les variables qui existent déjà. Il y a de grandes chances que cela puissent être le cas. Vérifions cela :

#split des données par rapport à la classe des variables
az <- split(names(cd_train), sapply(cd_train, function(x){ class(x)}))

#creation d'une base de données avec des variables numeriques
xs <- cd_train[,c(7,8,9,10,21,22,23)]

#Vérification de la corellation
#pander(cor(xs))

On peut le visualiser graphiquement avec un heatmap. Posez la souris sur un carré pour voir la corélation

correlation<-cor(xs)
plot_ly(z = correlation,
    x = row.names(correlation), y = colnames(correlation),color = "red",
    type = "heatmap")
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

On peut faire mieux que la visualisation ci-haut en représentant les point avec les correlation Cliquer sur le cadre de gauche en choisissant le couple de variables que vous souhaitez voir Visualiser le nuage des points sur le cadre à gauche

iplotCorr(cd_train[,c(7,8,9,10,21,22,23)],cd_train$Loan_Status, reorder=TRUE)
## Set screen size to height=700 x width=1000

Comme on peut le voir, il existe une corellation entre Total_Income et ApplicantIncome. Cela signifie que que la nouvelle variable n’apporte pas d’information supplémentaire dans les données. Donc cette variable n’est pas utile dans notre modélisation. Elle peut être supprimée

cd_train$Total_Income <- NULL
cd_test$Total_Income <- NULL

Il y a encore du potentiel pour créer de nouvelles features. Toujours garder en tête que vous pouvez encore générer de l’information dans vos données. Après avoir fait toutes ces moddofications regardons à nouveau ce qu’il ya dans nos tables:

pander(summarizeColumns(cd_train))
name type na mean disp median mad min max nlevs
Loan_ID factor 0 NA 0.9984 NA NA 1 1 614
Gender factor 0 NA 0.1824 NA NA 112 502 2
Married factor 0 NA 0.3469 NA NA 213 401 2
Dependents numeric 0 0.7629 1.003 0 0 0 3 0
Education factor 0 NA 0.2182 NA NA 134 480 2
Self_Employed factor 0 NA 0.1336 NA NA 82 532 2
ApplicantIncome numeric 0 5280 4986 3812 1823 150 40000 0
CoapplicantIncome numeric 0 1567 2340 1188 1762 0 21000 0
LoanAmount numeric 0 145.6 79.51 129 45.22 9 520 0
Loan_Amount_Term numeric 0 342 64.37 360 0 12 480 0
Credit_History factor 0 NA 0.145 NA NA 89 525 2
Property_Area factor 0 NA 0.6205 NA NA 179 233 3
Loan_Status factor 0 NA 0.3127 NA NA 192 422 2
Gender.dummy numeric 0 0.02117 0.1441 0 0 0 1 0
Married.dummy numeric 0 0.004886 0.06979 0 0 0 1 0
Dependents.dummy numeric 0 0.02443 0.1545 0 0 0 1 0
Self_Employed.dummy numeric 0 0.05212 0.2224 0 0 0 1 0
LoanAmount.dummy numeric 0 0.03583 0.186 0 0 0 1 0
Loan_Amount_Term.dummy numeric 0 0.0228 0.1494 0 0 0 1 0
Credit_History.dummy numeric 0 0.08143 0.2737 0 0 0 1 0
Income_by_loan numeric 0 50.42 35.04 41.37 10.92 12.09 365.8 0
Loan_amount_by_term numeric 0 0.4734 0.5023 0.3694 0.1441 0.025 9.25 0
pander(summarizeColumns(cd_test))
name type na mean disp median mad min max nlevs
Loan_ID factor 0 NA 0.9973 NA NA 1 1 367
Gender factor 0 NA 0.1907 NA NA 70 297 2
Married factor 0 NA 0.3651 NA NA 134 233 2
Dependents numeric 0 0.8291 1.057 0 0 0 3 0
Education factor 0 NA 0.2289 NA NA 84 283 2
Self_Employed factor 0 NA 0.1008 NA NA 37 330 2
ApplicantIncome numeric 0 4698 3707 3786 1598 0 33000 0
CoapplicantIncome numeric 0 1548 2155 1025 1520 0 16000 0
LoanAmount numeric 0 135.9 59.59 126 38.55 28 470 0
Loan_Amount_Term numeric 0 342.5 64.62 360 0 6 480 0
Credit_History factor 0 NA 0.1608 NA NA 59 308 2
Property_Area factor 0 NA 0.6185 NA NA 111 140 3
Gender.dummy numeric 0 0.02997 0.1707 0 0 0 1 0
Dependents.dummy numeric 0 0.02725 0.163 0 0 0 1 0
Self_Employed.dummy numeric 0 0.06267 0.2427 0 0 0 1 0
LoanAmount.dummy numeric 0 0.01362 0.1161 0 0 0 1 0
Loan_Amount_Term.dummy numeric 0 0.01635 0.127 0 0 0 1 0
Credit_History.dummy numeric 0 0.07902 0.2701 0 0 0 1 0
Loan_Status integer 0 0.5123 0.5005 1 0 0 1 0
Income_by_loan numeric 0 49.14 35.29 41.22 10.69 9.778 525 0
Loan_amount_by_term numeric 0 0.5182 1.394 0.3611 0.1235 0.09722 21.67 0

Application des méthodes de machine learning

Jusqu’ici, nous avons effectué toutes les étapes de transformations importantes sauf la normalisation des variables ayant une grande queue de distribution. Cela sera fait lorsque nous créerons les taches pour la modélisation.

Pour mlr, une tache n’est rien d’autre que le chargement de la table sur laquelle un algo va apprendre. Comme nous faisons face à un problème de classification, nous allons créer une tache de classification. Chaque tache dépend directement du type de problème auquel on fait face.

#Creation d'une tache
trainTask <- makeClassifTask(data = cd_train,target = "Loan_Status")
testTask <- makeClassifTask(data = cd_test, target = "Loan_Status")

## Verification de la tache
trainTask
## Supervised task: cd_train
## Type: classif
## Target: Loan_Status
## Observations: 614
## Features:
## numerics  factors  ordered 
##       14        7        0 
## Missings: FALSE
## Has weights: FALSE
## Has blocking: FALSE
## Classes: 2
##   N   Y 
## 192 422 
## Positive class: N

Comme on peut voir, il fournit une description de la table cd_train. Cependant il considère N comme la modalité de référence dans la variable d’octroie du crédit alors que celle-ci devrait être Y. Changeons cela:

trainTask <- makeClassifTask(data = cd_train,target = "Loan_Status", positive = "Y")

Pour une vision approfondie des données de la tache on peut appliquer le code suivant:

str(getTaskData(trainTask))
## 'data.frame':    614 obs. of  22 variables:
##  $ Loan_ID               : Factor w/ 614 levels "LP001002","LP001003",..: 1 2 3 4 5 6 7 8 9 10 ...
##  $ Gender                : Factor w/ 2 levels "Female","Male": 2 2 2 2 2 2 2 2 2 2 ...
##  $ Married               : Factor w/ 2 levels "No","Yes": 1 2 2 2 1 2 2 2 2 2 ...
##  $ Dependents            : num  0 1 0 0 0 2 0 3 2 1 ...
##  $ Education             : Factor w/ 2 levels "Graduate","Not Graduate": 1 1 1 2 1 1 2 1 1 1 ...
##  $ Self_Employed         : Factor w/ 2 levels "No","Yes": 1 1 2 1 1 2 1 1 1 1 ...
##  $ ApplicantIncome       : num  5849 4583 3000 2583 6000 ...
##  $ CoapplicantIncome     : num  0 1508 0 2358 0 ...
##  $ LoanAmount            : num  146 128 66 120 141 ...
##  $ Loan_Amount_Term      : num  360 360 360 360 360 360 360 360 360 360 ...
##  $ Credit_History        : Factor w/ 2 levels "0","1": 2 2 2 2 2 2 2 1 2 2 ...
##  $ Property_Area         : Factor w/ 3 levels "Rural","Semiurban",..: 3 1 3 3 3 3 3 2 3 2 ...
##  $ Loan_Status           : Factor w/ 2 levels "N","Y": 2 1 2 2 2 2 2 1 2 1 ...
##  $ Gender.dummy          : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Married.dummy         : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Dependents.dummy      : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Self_Employed.dummy   : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ LoanAmount.dummy      : num  1 0 0 0 0 0 0 0 0 0 ...
##  $ Loan_Amount_Term.dummy: num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Credit_History.dummy  : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Income_by_loan        : num  39.9 47.6 45.5 41.2 42.6 ...
##  $ Loan_amount_by_term   : num  0.407 0.356 0.183 0.333 0.392 ...

Maintenant, nous allons normaliser les données. Pour cette étape, nous utiliserons la fonction normalizeFeatures du package mlr. Par défaut, ce package normalise toutes les variables numériques dans la table. Heureusement les 3 variables à normaliser sont toutes numériques les autres variables sont catégorielles:

#normalize the variables
trainTask <- normalizeFeatures(trainTask,method = "standardize")
testTask <- normalizeFeatures(testTask,method = "standardize")

Avant d’appliquer les algo de ML, nous devons enlever les variables inutiles. Je parle de la variable Married.dummy qui n’est présente que dans la table d’apprentissage :

trainTask <- dropFeatures(task = trainTask,features = c("Loan_ID","Married.dummy"))

Le package MLR a des fonctions qui retournent l’importance des variables. Voyons quelles sont les variables importantes. Plus tard, nous pouvons utiliser cette information pour effectuer une selection de predicteurs pour l’amélioration du pouvoir préédictif du model. En lançant le programme, R vous demandera d’installer le package FSelector, installez le.

#Importance des variables
im_feat <- generateFilterValuesData(trainTask, method = c("information.gain","chi.squared"))
plotFilterValues(im_feat,n.show = 20)

#shiny visualisation
#plotFilterValuesGGVIS(im_feat)

Si vous vous posez des questions sur le gain d'information voici une explication simple. Le gain d’information est généralement utilisé dans un contexte avec des arbres de décision. Chaque noeud d’un arbre de décision est basé sur le gain d’information apparté par une variable. En général le noeud cherche les variables qui apportent le maximum d’information dans la discrimination de la variable d’intérêt.

Commençons la modélisation maintenant.Nous n’allons pas expliquer en détail le fonctionnement des algorithmes mais il y a des liens pour mieux comprendre ces derniers.

Avec MLR, on peut choisir et paramétrer les algos en utilisant makeLearner. Ce rebot va apprendre à partir de la tache que nous avons nommée trainTask et réaliser des prédictions avec testTask.

1. Analyse discriminante quadratique

QDA est un algorithm paramétrique c’est à dire qu’il se base sur un certains nombre d’hypothèses sur les données. Si les données respectent ces hypothèses, alors ces algorithmes surperforment voir surapprennent des fois.En savoir plus.

#Chargement de qda 
qda.learner <- makeLearner("classif.qda", predict.type = "response")

#apprentissage
qmodel <- train(qda.learner, trainTask)

#plotLearnerPrediction(learner = qda.learner, task = trainTask)

#prediction
qpredict <- predict(qmodel, testTask)

#création d'un fichier de soumission
submit <- data.frame(Loan_ID = test$Loan_ID, Loan_Status = qpredict$data$response)
write.csv(submit, "submit1.csv",row.names = F)

2. Régression logistique

Cette fois, vérifions la performance du modèle avec une validation croisée en calculant le taux de bon classement (AUC). Un taux de classsement fort sur la validation croisée nous indique que le model n’est pas atteint par la grande variance dans les données et peut être bien généralisé sur des données sur lesquelles il n’a pas appris.

# regression logistic
logistic.learner <- makeLearner("classif.logreg",predict.type = "response")

#cross validation (cv) accuracy
cv.logistic <- crossval(learner = logistic.learner,task = trainTask,iters = 3,stratify = TRUE,measures = acc,show.info = F)

On peut réaaliser une CV pour tout type d’algo et c’est incroyablement facile à faire. N’est ce pas?. Nous avons utiliser un échantillon stratifié avec 3 folds. Nous recommandons d’utiliser des folds stratifiés dans des problèmes de classification puis que cela garde les proportions de la variable cible dans les n folds. Nous pouvons vérifier l’acuuracy :

cv.logistic$aggr
## acc.test.mean 
##     0.7964531

Ceci est l’AUC moyen calculé sur 3 folds. Pour voir l’accuracy sur chaque fold on peut faire :

cv.logistic$measures.test
##   iter       acc
## 1    1 0.7951220
## 2    2 0.8186275
## 3    3 0.7756098

Maintenant nous allons apprendre l’algo et regarder les predictions sur l’échantillon test

#appentissage
 fmodel <- train(logistic.learner,trainTask)
 getLearnerModel(fmodel)
## 
## Call:  stats::glm(formula = f, family = "binomial", data = getTaskData(.task, 
##     .subset), weights = .weights, model = FALSE)
## 
## Coefficients:
##            (Intercept)              GenderMale              MarriedYes  
##               -3.30484                 0.03282                 0.57043  
##             Dependents   EducationNot Graduate        Self_EmployedYes  
##                0.03970                -0.44800                -0.03828  
##        ApplicantIncome       CoapplicantIncome              LoanAmount  
##                0.19729                -0.04405                -0.25441  
##       Loan_Amount_Term         Credit_History1  Property_AreaSemiurban  
##               -0.11972                 4.04878                 0.87167  
##     Property_AreaUrban            Gender.dummy        Dependents.dummy  
##                0.17868                -0.04055                 0.04687  
##    Self_Employed.dummy        LoanAmount.dummy  Loan_Amount_Term.dummy  
##               -0.03662                -0.18202                 0.13784  
##   Credit_History.dummy          Income_by_loan     Loan_amount_by_term  
##               -0.05018                -0.09829                -0.09079  
## 
## Degrees of Freedom: 613 Total (i.e. Null);  593 Residual
## Null Deviance:       762.9 
## Residual Deviance: 555.7     AIC: 597.7
#prediction
 fpmodel <- predict(fmodel, testTask)

#Soumission
 submit <- data.frame(Loan_ID = test$Loan_ID, Loan_Status = fpmodel$data$response)
 write.csv(submit, "submit2.csv",row.names = F)

Ce model performe plus que le précédent.0,79% ce qui n’est pas mal.

3. Arbre de décision

On dit souvent qu’un arbre de décision capture les relations non linéaires mieux que la régression logistique. Vérifions cette assertion. Cette fois nous allons tuner les hyperparametres de l’arbre pour avoir de meilleurs résultats. Pour avoir la liste des paramètres de n’importe quel algorithm, il suffit de faire (dans notre cas l’algo est rpart) :

getParamSet("classif.rpart")
##                    Type len  Def   Constr Req Tunable Trafo
## minsplit        integer   -   20 1 to Inf   -    TRUE     -
## minbucket       integer   -    - 1 to Inf   -    TRUE     -
## cp              numeric   - 0.01   0 to 1   -    TRUE     -
## maxcompete      integer   -    4 0 to Inf   -    TRUE     -
## maxsurrogate    integer   -    5 0 to Inf   -    TRUE     -
## usesurrogate   discrete   -    2    0,1,2   -    TRUE     -
## surrogatestyle discrete   -    0      0,1   -    TRUE     -
## maxdepth        integer   -   30  1 to 30   -    TRUE     -
## xval            integer   -   10 0 to Inf   -   FALSE     -
## parms           untyped   -    -        -   -    TRUE     -

Cette fonction renvoie la longue liste des paramètres tunables et non tunables. Créons l’arbre de décision. Assurez vous que le package rpart est bien actif avant de créer l’arbre.

#arbre
makeatree <- makeLearner("classif.rpart", predict.type = "response")

#3 fold cross validation
set_cv <- makeResampleDesc("CV",iters = 3L)

Nous utilisons une 3 folds CV car nous n’avons pas assés d’observations. Tunons maintenant les hyperparamètres:

#Recherchee des hyperparametres
 gs <- makeParamSet(
makeIntegerParam("minsplit",lower = 10, upper = 50),
makeIntegerParam("minbucket", lower = 5, upper = 50),
makeNumericParam("cp", lower = 0.001, upper = 0.2)
)

Comme on peut constater, nous n’avons tuné que 3 paramètres. minsplit représente le nombre minimum d’observations dans un noeud pour que le découpage se fasse. minbucket est le nombre minimum d’observations qui doivent être gardées dans un noeud final. cp est le paramètre de la compléxité. Plus il est petit, plus l’arbre apprendra une relation spécifique dans les données ce qui peut conduire à un surapprentissage.

# grid search
gscontrol <- makeTuneControlGrid()

#hypertune des parametres
invisible(stune <- tuneParams(learner = makeatree, resampling = set_cv, task = trainTask, par.set = gs, control = gscontrol, measures = acc))

Vous pouvez aller vous promener le temps que le tunning soit terminé. Peut être attraper des pokemons! Cela prend 15 minutes pour tourner sur une machine avec 8GB intel i5 processor sous windows.

pikachu
stune$x

Cette fonction renvoie la list des meilleurs parametre. Vous pouvez vérifier l’accuracy:

#cross validation accuracy
stune$y

En utilisant setHyperPars, on peut directement régler les paramètres optimaux en même temps qu’on réalise le gridsearch.

#utilisation des hyperparametres dans la modelisation
t.tree <- setHyperPars(makeatree, par.vals = stune$x)

#Apprentissage
t.rpart <- train(t.tree, trainTask)
getLearnerModel(t.rpart)

#predictions
tpmodel <- predict(t.rpart, testTask)

#Fichiers de soumission
submit <- data.frame(Loan_ID = test$Loan_ID, Loan_Status = tpmodel$data$response)
write.csv(submit, "submit3.csv",row.names = F)
cd_tree_train=cd_train
cd_tree_train$credit=ifelse(cd_tree_train$Loan_Status=='Y',1,0)
cd_train.fft <- fft(formula = credit ~.,
                        data = cd_tree_train[,c(-1,-13)])
## Warning: 'fft' is deprecated.
## Use 'FFTrees' instead.
## See help("Deprecated")
plot(cd_train.fft, 
     main = "Credit FFT", 
     decision.names = c("pas de credit", "credit"))

L’arbre de décision ne fait pas mieux que la régression logistique. Un arbre n’est pas suffisant, créons une forêt.

4. Random forest

Le random est un algorithme puissant, connu pour produire des résultats étonnants. Ses prédictions découlent d’un ensemble d’arbres. Il moyenne les predictions renvoyées par chaque arbre et produit un résulltat généralisé. Ici, les différentes étapes seront similaires à la méthode précédente mais à la différence d’un grid search, nous allos faire un random search par ce que c’est plus rapide.

Normalement vous vous posez la question quelle est la différence entre le grid search et le random search. Petite explication:

Lorsqu’on effectue une recherche opérationnelle dans un 2-dimension (optimisation de 2 paramètres) le processus du grid search fonctionne comme suit :

grid

Tandis qu’un random search évoluerait comme suit :

random

Ces deux illustrations donnent évidemment l’impression que le grid search est plus optimal que le random search. Cependant il faut garder à l’esprit que lorsque le nombre de dimensiosns est important alors le nombre de combinaisons augmente à un rythme exponentiel. Par exemple pour effectuer un grid search sur 10 paramètres booléens il faut tester \(2^{10}\) combinaisons. C’est pour cela que des fois un random search combiné une bonne heuristic est utilisé pour gagner du temps.

getParamSet("classif.randomForest")
##                      Type  len   Def   Constr Req Tunable Trafo
## ntree             integer    -   500 1 to Inf   -    TRUE     -
## mtry              integer    -     - 1 to Inf   -    TRUE     -
## replace           logical    -  TRUE        -   -    TRUE     -
## classwt     numericvector <NA>     - 0 to Inf   -    TRUE     -
## cutoff      numericvector <NA>     -   0 to 1   -    TRUE     -
## strata            untyped    -     -        -   -    TRUE     -
## sampsize    integervector <NA>     - 1 to Inf   -    TRUE     -
## nodesize          integer    -     1 1 to Inf   -    TRUE     -
## maxnodes          integer    -     - 1 to Inf   -    TRUE     -
## importance        logical    - FALSE        -   -    TRUE     -
## localImp          logical    - FALSE        -   -    TRUE     -
## proximity         logical    - FALSE        -   -   FALSE     -
## oob.prox          logical    -     -        -   Y   FALSE     -
## norm.votes        logical    -  TRUE        -   -   FALSE     -
## do.trace          logical    - FALSE        -   -   FALSE     -
## keep.forest       logical    -  TRUE        -   -   FALSE     -
## keep.inbag        logical    - FALSE        -   -   FALSE     -
#creation du learner
rf <- makeLearner("classif.randomForest", predict.type = "response", par.vals = list(ntree = 200, mtry = 3))
rf$par.vals <- list(
importance = TRUE
)

#reglaage des paramètres tunables
#grid search pour trouver les hyperparameters
rf_param <- makeParamSet(
makeIntegerParam("ntree",lower = 50, upper = 500),
makeIntegerParam("mtry", lower = 3, upper = 10),
makeIntegerParam("nodesize", lower = 10, upper = 50)
)

#Faison un random search pour 50 iterations
rancontrol <- makeTuneControlRandom(maxit = 50L)

Le random search est plus rapide que le grid search, mais quelques fois il peut être moins efficace. Dans un grid search, l’algorithme se déploie sur toutes les combinaisons de paramètres possible. Dans un random search, on spécifie le nombre d’itérations et il passe aléatoirement sur une combinaison de parametre.Avec ce procéder, on peut rater des combinaisons qui auraient pu donner de meilleurs résultats tout comme on peut tomber sur la combinaison optimale.

#réglages des 3 fold cross validation
set_cv <- makeResampleDesc("CV",iters = 3L)

#hypertuning
rf_tune <- tuneParams(learner = rf, resampling = set_cv, task = trainTask, par.set = rf_param, control = rancontrol, measures = acc)

Maintenant que nous avons les paramètres finaux, voyons la list des paramètre et l’acurracy de la CV

#cv accuracy
rf_tune$y


# parameters
rf_tune$x

Créons maintenant le modèle

#utilisation des hyperparameters pour la  modelisation
rf.tree <- setHyperPars(rf, par.vals = rf_tune$x)

#Apprentissage
rforest <- train(rf.tree, trainTask)
getLearnerModel(t.rpart)

#predictions
rfmodel <- predict(rforest, testTask)

#soumission
submit <- data.frame(Loan_ID = test$Loan_ID, Loan_Status = rfmodel$data$response)
write.csv(submit, "submit4.csv",row.names = F)

Le modele fait pas mieux. Vous pouvez utiliser un grid search pour voir

5. SVM

Support Vector Machines (SVM) etst aussi un algo d’apprentissage supervisé pour les problèmes de régression et de classification. Il crée un hyperplan dans un n dimension pourr classifier les donnéees sur la base de la variable cible. Tournons la page ddes algorithme à arbres et voyons ce que celui-ci nous apporte.

Etant donnée que les étapes seront similaires que les méthodes précédentes, vous n’aurez pas de mal à comprendre le code.

#chargement du SVM
getParamSet("classif.ksvm") #installer le  package kernlab 
##                        Type  len    Def
## scaled              logical    -   TRUE
## type               discrete    -  C-svc
## kernel             discrete    - rbfdot
## C                   numeric    -      1
## nu                  numeric    -    0.2
## epsilon             numeric    -    0.1
## sigma               numeric    -      -
## degree              integer    -      3
## scale               numeric    -      1
## offset              numeric    -      1
## order               integer    -      1
## tol                 numeric    -  0.001
## shrinking           logical    -   TRUE
## class.weights numericvector <NA>      -
## fit                 logical    -   TRUE
## cache               integer    -     40
##                                                 Constr Req Tunable Trafo
## scaled                                               -   -    TRUE     -
## type              C-svc,nu-svc,C-bsvc,spoc-svc,kbb-svc   -    TRUE     -
## kernel        vanilladot,polydot,rbfdot,tanhdot,lap...   -    TRUE     -
## C                                             0 to Inf   Y    TRUE     -
## nu                                            0 to Inf   Y    TRUE     -
## epsilon                                    -Inf to Inf   Y    TRUE     -
## sigma                                         0 to Inf   Y    TRUE     -
## degree                                        1 to Inf   Y    TRUE     -
## scale                                         0 to Inf   Y    TRUE     -
## offset                                     -Inf to Inf   Y    TRUE     -
## order                                      -Inf to Inf   Y    TRUE     -
## tol                                           0 to Inf   -    TRUE     -
## shrinking                                            -   -    TRUE     -
## class.weights                                 0 to Inf   -    TRUE     -
## fit                                                  -   -    TRUE     -
## cache                                         1 to Inf   -   FALSE     -
ksvm <- makeLearner("classif.ksvm", predict.type = "response")

#Réglage des paramètres
pssvm <- makeParamSet(
makeDiscreteParam("C", values = 2^c(-8,-4,-2,0)), #cost parameters
makeDiscreteParam("sigma", values = 2^c(-8,-4,0,4)) #RBF Kernel Parameter
)

#Définition de la fonction de gridsearch
ctrl <- makeTuneControlGrid()

#tuning du modèle
res <- tuneParams(ksvm, task = trainTask, resampling = set_cv, par.set = pssvm, control = ctrl,measures = acc)
## [Tune] Started tuning learner classif.ksvm for parameter set:
##           Type len Def                   Constr Req Tunable Trafo
## C     discrete   -   - 0.00390625,0.0625,0.25,1   -    TRUE     -
## sigma discrete   -   -   0.00390625,0.0625,1,16   -    TRUE     -
## With control class: TuneControlGrid
## Imputation value: -0
## [Tune-x] 1: C=0.00390625; sigma=0.00390625
## [Tune-y] 1: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 2: C=0.0625; sigma=0.00390625
## [Tune-y] 2: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 3: C=0.25; sigma=0.00390625
## [Tune-y] 3: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 4: C=1; sigma=0.00390625
## [Tune-y] 4: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 5: C=0.00390625; sigma=0.0625
## [Tune-y] 5: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 6: C=0.0625; sigma=0.0625
## [Tune-y] 6: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 7: C=0.25; sigma=0.0625
## [Tune-y] 7: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 8: C=1; sigma=0.0625
## [Tune-y] 8: acc.test.mean=0.767; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 9: C=0.00390625; sigma=1
## [Tune-y] 9: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 10: C=0.0625; sigma=1
## [Tune-y] 10: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 11: C=0.25; sigma=1
## [Tune-y] 11: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 12: C=1; sigma=1
## [Tune-y] 12: acc.test.mean=0.721; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 13: C=0.00390625; sigma=16
## [Tune-y] 13: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 14: C=0.0625; sigma=16
## [Tune-y] 14: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 15: C=0.25; sigma=16
## [Tune-y] 15: acc.test.mean=0.687; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune-x] 16: C=1; sigma=16
## [Tune-y] 16: acc.test.mean=0.686; time: 0.0 min; memory: 123Mb use, 192Mb max
## [Tune] Result: C=1; sigma=0.0625 : acc.test.mean=0.767
#L'accuracy de la validation croisée
res$y
## acc.test.mean 
##     0.7670971
#Réglage du model avec les paramètres optimaux
t.svm <- setHyperPars(ksvm, par.vals = res$x)

#Apprentissage
par.svm <- train(ksvm, trainTask)

#Prediction
predict.svm <- predict(par.svm, testTask)

#fichier de soumission
submit <- data.frame(Loan_ID = test$Loan_ID, Loan_Status = predict.svm$data$response)
write.csv(submit, "submit5.csv",row.names = F)

ce model renvoie un AUC de 77,08%. Ce n’est pas mal, mais c’est plus faible que le meilleure score. Ne soyez pas désespéré, le ML ne fonctionne que lorsqu’il engloutit de bonnes variables. C’est pour cela qu’il faut s’attarder sur la transformation et la création de nouvelles variables en créant des variables apportant davantage d’information. Faisons maintenant une méthode de boosting.

6. GBM

Entrons maintenant dans le cercle des algorithms de boosting. le GBM effectue une modélisation séquentielle c’est à dire qu’après une prédiction, l’algorithme récupère les prédictions incorrectes, leur assigne un poids plus important et réeffectue la prédiction jusqu’à ce que ces derniers soient bien prédites.

#lChargement de GBM
getParamSet("classif.gbm")
##                       Type len   Def
## distribution      discrete   -     -
## n.trees            integer   -   100
## cv.folds           integer   -     0
## interaction.depth  integer   -     1
## n.minobsinnode     integer   -    10
## shrinkage          numeric   - 0.001
## bag.fraction       numeric   -   0.5
## train.fraction     numeric   -     1
## keep.data          logical   -  TRUE
## verbose            logical   - FALSE
##                                                     Constr Req Tunable
## distribution      bernoulli,adaboost,huberized,multinomial   -    TRUE
## n.trees                                           1 to Inf   -    TRUE
## cv.folds                                       -Inf to Inf   -    TRUE
## interaction.depth                                 1 to Inf   -    TRUE
## n.minobsinnode                                    1 to Inf   -    TRUE
## shrinkage                                         0 to Inf   -    TRUE
## bag.fraction                                        0 to 1   -    TRUE
## train.fraction                                      0 to 1   -    TRUE
## keep.data                                                -   -   FALSE
## verbose                                                  -   -   FALSE
##                   Trafo
## distribution          -
## n.trees               -
## cv.folds              -
## interaction.depth     -
## n.minobsinnode        -
## shrinkage             -
## bag.fraction          -
## train.fraction        -
## keep.data             -
## verbose               -
g.gbm <- makeLearner("classif.gbm", predict.type = "response")

#Spécification de la méthode de tuning
rancontrol <- makeTuneControlRandom(maxit = 50L)

#3 fold cross validation
set_cv <- makeResampleDesc("CV",iters = 3L)

#parametres
gbm_par<- makeParamSet(
makeDiscreteParam("distribution", values = "bernoulli"),
makeIntegerParam("n.trees", lower = 100, upper = 1000), #number of trees
makeIntegerParam("interaction.depth", lower = 2, upper = 10), #depth of tree
makeIntegerParam("n.minobsinnode", lower = 10, upper = 80),
makeNumericParam("shrinkage",lower = 0.01, upper = 1)
)

n.minobsinnode fait référence au nombre minimal d’observations dans le noeud d’un arbre. shrinkage est le paramètre de régularisation qui indique la vitesse à laquelle doit tourner l’algo.

#tuning des parametres
tune_gbm <- tuneParams(learner = g.gbm, task = trainTask,resampling = set_cv,measures = acc,par.set = gbm_par,control = rancontrol)

#Vérification de l'accuracy de la cross validation
tune_gbm$y

#Réglage des paramètres
final_gbm <- setHyperPars(learner = g.gbm, par.vals = tune_gbm$x)

#Apprentisssage
to.gbm <- train(final_gbm, trainTask)

#prédiction 
pr.gbm <- predict(to.gbm, testTask)

#soumission
submit <- data.frame(Loan_ID = test$Loan_ID, Loan_Status = pr.gbm$data$response)
write.csv(submit, "submit6.csv",row.names = F)

L’AUC du model est de 78.47%. Le GBM performe mieux que le SVM, mais ne fait pas mieux que le RF. Finalement testont le XGboost aussi.

7. XGBOOST

Le Xgboost est considéré comme meilleur que le GBM ses propriètés natives. Il inclue un fradient du premier et du second ordre, il a la capacité de paralléliser ses taches ce qui lui permet de mieux pruner les arbres. En général le xgboost requiert que les données soient transformées au format de matrice mais avec mlr ce n’est pas nécessaire.

L’un des avantage de ce package est comme éévoqué au début, c’est que vous pouvez utiliser la même structure de code pour implémenter différents algo.

#chargement de Xgboost
set.seed(1001)
getParamSet("classif.xgboost")

#création du learner
xg_set <- makeLearner("classif.xgboost", predict.type = "response")
xg_set$par.vals <- list(
objective = "binary:logistic",
eval_metric = "error",
nrounds = 250
)

#Définition des paramètres à tuner
xg_ps <- makeParamSet(
makeIntegerParam("nrounds",lower=200,upper=600),
makeIntegerParam("max_depth",lower=3,upper=20),
makeNumericParam("lambda",lower=0.55,upper=0.60),
makeNumericParam("eta", lower = 0.001, upper = 0.5),
makeNumericParam("subsample", lower = 0.10, upper = 0.80),
makeNumericParam("min_child_weight",lower=1,upper=5),
makeNumericParam("colsample_bytree",lower = 0.2,upper = 0.8)
)

#définition de la fonction de grid-search
rancontrol <- makeTuneControlRandom(maxit = 100L) #fait 100 iterations

#3 fold cross validation
set_cv <- makeResampleDesc("CV",iters = 3L)

#tuning des paramètres
xg_tune <- tuneParams(learner = xg_set, task = trainTask, resampling = set_cv,measures = acc,par.set = xg_ps, control = rancontrol)

#Réglage des paramètres
xg_new <- setHyperPars(learner = xg_set, par.vals = xg_tune$x)

#Apprentissage
xgmodel <- train(xg_new, trainTask)

#prediction
predict.xg <- predict(xgmodel, testTask)

#soumission
submit <- data.frame(Loan_ID = test$Loan_ID, Loan_Status = predict.xg$data$response)
write.csv(submit, "submit7.csv",row.names = F)

Infos

  1. Données à télécharger https://datahack.analyticsvidhya.com/contest/practice-problem-loan-prediction-iii/
  2. Méthodes https://www.analyticsvidhya.com
  3. Package et méthodes https://mlr-org.github.io/mlr-tutorial/devel/html/index.html

A Faire

  1. Performance des models avec traçage des courbes de ROC et des courbes de gain
  2. Visualisation des prédictions
  3. Steaking : ensemble learning, combinaison de modèles