Статьи

Вычислительное время прогнозирующих моделей

Во вторник, в конце моего 5-часового ускоренного курса по машинному обучению для актуариев, Пьер задал мне интересный вопрос о времени вычислений различных методов. Я представлял философию различных алгоритмов, но я забыл упомянуть время вычислений. Я хотел попробовать несколько алгоритмов классификации на наборе данных, используемых для иллюстрации методов

> rm(list=ls())
> myocarde=read.table(
"http://freakonometrics.free.fr/myocarde.csv",
head=TRUE,sep=";")
> levels(myocarde$PRONO)=c("Death","Survival")

Но набор данных довольно мал, с 71 наблюдением и 7 объяснительными переменными. Поэтому я решил повторить наблюдения и добавить несколько ковариат,

> levels(myocarde$PRONO)=c("Death","Survival")
> idx=rep(1:nrow(myocarde),each=100)
> TPS=matrix(NA,30,10)
> myocarde_large=myocarde[idx,]
> k=23
> M=data.frame(matrix(rnorm(k*
+ nrow(myocarde_large)),nrow(myocarde_large),k))
> names(M)=paste("X",1:k,sep="")
> myocarde_large=cbind(myocarde_large,M)
> dim(myocarde_large)
[1] 7100   31
> object.size(myocarde_large)
2049.064 kbytes

Набор данных не большой … но, по крайней мере, он не занимает 0,0001 сек. запустить регрессию. На самом деле, чтобы запустить логистическую регрессию , требуется 0,1 секунды

> system.time(fit< glm(PRONO~.,
+ data=myocarde_large, family="binomial"))
       user      system     elapsed 
      0.114       0.016       0.134 
> object.size(fit)
9,313.600 kbytes

И я был удивлен, что объект регрессии был 9Mo, что более чем в четыре раза превышает размер набора данных. С большим набором данных, в 100 раз больше,

> dim(myocarde_large_2)
[1] 710000     31

это занимает 20 сек.

> system.time(fit<-glm(PRONO~.,
+ data=myocarde_large_2, family="binomial"))
utilisateur     système      écoulé 
     16.394       2.576      19.819 
> object.size(fit)
90,9025.600 kbytes

и объект «только» в десять раз больше.

Обратите внимание, что со сплайном время вычислений довольно схоже

> library(splines)
> system.time(fit<-glm(PRONO~bs(INSYS)+.,
+ data=myocarde_large, family="binomial"))
       user      system     elapsed 
      0.142       0.000       0.143 
> object.size(fit)
9663.856 kbytes

Если мы используем другую функцию, точнее ту, которую я использую для  полиномиальных регрессий, она будет в два раза длиннее

> library(VGAM)
> system.time(fit1<-vglm(PRONO~.,
+ data=myocarde_large, family="multinomial"))
       user      system     elapsed 
      0.200       0.020       0.226 
> object.size(fit1)
6569.464 kbytes

в то время как объект меньше. Теперь, если мы используем пошаговую процедуру в обратном направлении, она будет немного длиннее: почти на одну минуту, в 500 раз дольше, чем одна логистическая регрессия.

> system.time(fit<-step(glm(PRONO~.,data=myocarde_large,
family="binomial")))

             ...

Step:  AIC=4118.15
PRONO ~ FRCAR + INCAR + INSYS + PRDIA + PVENT + REPUL + X16

        Df Deviance    AIC
<none>       4102.2 4118.2
- X16    1   4104.6 4118.6
- PRDIA  1   4113.4 4127.4
- INCAR  1   4188.4 4202.4
- REPUL  1   4203.9 4217.9
- PVENT  1   4215.5 4229.5
- FRCAR  1   4254.1 4268.1
- INSYS  1   4286.8 4300.8
       user      system     elapsed 
     50.327       0.050      50.368 
> object.size(fit)
6,652.160 kbytes

Я тоже хотел попробовать карет. В этом пакете приятно сравнивать модели. В обзоре книги « Вычислительная актуарная наука с R» в JRSS-A  Андрей Костеко  заметил, что этот пакет даже не упоминался и его не было. Поэтому я попробовал логистическую регрессию

> library(caret)
> system.time(fit<-train(PRONO~.,
+ data=myocarde_large,method="glm"))
       user      system     elapsed 
      5.908       0.032       5.954 
> object.size(fit)
12,676.944 kbytes

Это заняло 6 секунд (в 50 раз больше, чем стандартный вызов функции glm), и объект довольно большой. Это даже хуже, если мы попытаемся выполнить пошаговую процедуру

> system.time(fit<-train(PRONO~.,
+ data=myocarde_large,method="glmStepAIC"))

                ...

Step:  AIC=4118.15
.outcome ~ FRCAR + INCAR + INSYS + PRDIA + PVENT + REPUL + X16

        Df Deviance    AIC
<none>       4102.2 4118.2
- X16    1   4104.6 4118.6
- PRDIA  1   4113.4 4127.4
- INCAR  1   4188.4 4202.4
- REPUL  1   4203.9 4217.9
- PVENT  1   4215.5 4229.5
- FRCAR  1   4254.1 4268.1
- INSYS  1   4286.8 4300.8
       user      system     elapsed 
   1063.399       2.926    1068.060 
> object.size(fit)
9,978.808 kbytes

что заняло 15 минут, всего 30 ковариат … Вот график (я использовал микробенчмарк для его построения)

Давайте рассмотрим некоторые деревья .

> library(rpart)
> system.time(fit<-rpart(PRONO~.,
+ data=myocarde_large))
       user      system     elapsed 
      0.341       0.000       0.345 
> object.size(fit4)
544.664 kbytes

Здесь это быстро, и объект довольно маленький. И если мы изменим параметр сложности, чтобы получить более глубокое дерево, это почти то же самое

> system.time(fit<-rpart(PRONO~.,
+ data=myocarde_large,cp=.001))
       user      system     elapsed 
      0.346       0.000       0.346 
> object.size(fit)
544.824 kbytes

Но опять же, если мы запустим ту же функцию через каретку, она будет более чем в десять раз медленнее,

> system.time(fit<-train(PRONO~.,
+ data=myocarde_large,method="rpart"))
       user      system     elapsed 
      4.076       0.005       4.077 
> object.size(fit)
5,587.288 kbytes

и объект в десять раз больше. Теперь рассмотрим случайный лес .

> library(randomForest)
> system.time(fit<-randomForest(PRONO~.,
+ data=myocarde_large,ntree=50))
       user      system     elapsed 
      0.672       0.000       0.671 
> object.size(fit)
1,751.528 kbytes

При «только» 50 деревьях получить результат можно только в два раза дольше. Но с 500 деревьями (значение по умолчанию) требуется в двадцать раз больше (при разумном пропорциональном времени выращивание 500 деревьев вместо 50)

> system.time(fit<-randomForest(PRONO~.,
+ data=myocarde_large,ntree=500))
       user      system     elapsed 
      6.644       0.180       6.821 
> object.size(fit)
5,133.928 kbytes

Если мы изменим число используемых ковариат, на каждом узле мы увидим, что это почти не влияет. С 5 ковариатами (что является квадратным корнем от общего числа ковариат, т.е. это значение по умолчанию), это занимает 6 секунд,

> system.time(fit<-randomForest(PRONO~.,
+ data=myocarde_large,mtry=5))
       user      system     elapsed 
      6.266       0.076       6.338 
> object.size(fit)
5,161.928 kbytes

но если мы используем 10, это почти то же самое (даже меньше)

> system.time(fit<-randomForest(PRONO~.,
+ data=myocarde_large,mtry=10))
       user      system     elapsed 
      5.666       0.076       5.737 
> object.size(fit)
2,501.928 bytes

Если мы используем алгоритм случайного леса внутри каретки, это займет 10 минут,

> system.time(fit<-train(PRONO~.,
+ data=myocarde_large,method="rf"))
       user      system     elapsed 
    609.790       2.111     613.515

и визуализация

Если мы рассмотрим  метод k- ближайшего соседа, с осторожностью снова, это займет некоторое время, снова с 10 минутами

> system.time(fit<-train(PRONO~.,
+ data=myocarde_large,method="knn"))
       user      system     elapsed 
     66.994       0.088      67.327 
> object.size(fit)
5,660.696 kbytes

это почти то же самое время, что и алгоритм упаковки, на деревьях

> system.time(fit<-train(PRONO~.,
+ data=myocarde_large,method="treebag"))
Le chargement a nécessité le package : plyr
       user      system     elapsed 
     60.526       0.567      61.641 

> object.size(fit)
72,048.480 kbytes

но на этот раз объект довольно большой!

Мы также можем рассмотреть методы SVM со стандартным евклидовым расстоянием

> library(kernlab)
> system.time(fit<-ksvm(PRONO~.,
+ data=myocarde_large,
+ prob.model=TRUE, kernel="vanilladot"))
 Setting default kernel parameters  
       user      system     elapsed 
     14.471       0.076      14.698 
> object.size(fit)
801.120 kbytes

или используя какое-то ядро

> system.time(fit<-ksvm(PRONO~.,
+ data=myocarde_large,
+ prob.model=TRUE, kernel="rbfdot"))
       user      system     elapsed 
      9.469       0.052       9.701 
> object.size(fit)
846.824 kbytes

Оба метода занимают около 10 секунд, намного больше, чем наша основная логистическая регрессия (в сто раз больше). И снова, если мы попытаемся использовать каретку, чтобы сделать то же самое, это займет некоторое время …

> system.time(fit<-train(PRONO~.,
+ data=myocarde_large, method="svmRadial"))
       user      system     elapsed 
    360.421       2.007     364.669 
> object.size(fit)
4,027.880 kbytes

Вывод следующий

 

Я также хотел попробовать некоторые функции, такие как Ridge и LASSO.

> library(glmnet)
> idx=which(names(myocarde_large)=="PRONO")
> y=myocarde_large[,idx]
> x=as.matrix(myocarde_large[,-idx])
> system.time(fit<-glmnet(x,y,alpha=0,lambda=.05,
+ family="binomial"))
       user      system     elapsed 
      0.013       0.000       0.052 
> system.time(fit<-glmnet(x,y,alpha=1,lambda=.05,
+ family="binomial"))
       user      system     elapsed 
      0.014       0.000       0.013

Я был удивлен, увидев, как быстро это. И если мы используем перекрестную проверку для количественной оценки штрафа

> system.time(fit10<-cv.glmnet(x,y,alpha=1,
+ type="auc",nlambda=100,
+ family="binomial"))
       user      system     elapsed 
     11.831       0.000      11.831

Это занимает некоторое время … но это разумно, по сравнению с другими методами. И, наконец, рассмотрим несколько  улучшающих пакетов.

> system.time(fit<-gbm.step(data=myocarde_large,
+ gbm.x = (1:(ncol(myocarde_large)-1))[-idx], 
+ gbm.y = ncol(myocarde_large),
+ family = "bernoulli", tree.complexity = 5,
+ learning.rate = 0.01, bag.fraction = 0.5))
       user      system     elapsed 
    364.784       0.428     365.755 
> object.size(fit)
8,607.048 kbytes

Это было долго. Более 6 минут. Использование пакета glmboost через каретку было намного быстрее, на этот раз

> system.time(fit<-train(PRONO~.,
+ data=myocarde_large,method="glmboost"))
       user      system     elapsed 
     13.573       0.024      13.592 
> object.size(fit)
6,717.400 bytes

При использовании GBM через каретку было в десять раз дольше,

> system.time(fit<-train(PRONO~.,
+ data=myocarde_large,method="gbm"))
       user      system     elapsed  
    121.739       0.360     122.466 
> object.size(fit)
7,115.512 kbytes

 

Все это было сделано одним ноутбуком. Теперь мне нужно запустить те же коды на более быстрой машине, чтобы попробовать гораздо большие наборы данных….