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 :
mlrJusqu’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.
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))
| 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 |
| 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 |
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:
Loan_Status est la variable dependante et le reste constituent les variables independantes.Loan_Status il faut soumettre sa prédiction sur le site où les données ont été téléchargées.na ).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))
| 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 |
| 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))
| 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 |
| 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))
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.
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 |
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.
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)
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.
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.
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.
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 :
Tandis qu’un random search évoluerait comme suit :
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
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.
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.
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)