lapply vs boucle for - Performance de la R

Il est souvent dit que l'on doit préférer lapply sur for boucles.
Il y a quelques exception comme par exemple Hadley Wickham souligne dans son avant-R livre.

(http://adv-r.had.co.nz/Functionals.html) (Modification en place, la Récursivité, etc).
Le suivant est un de ces cas.

Juste pour l'amour de l'apprentissage, j'ai essayé de réécrire un perceptron de l'algorithme dans une forme fonctionnelle pour indice de référence
la performance relative.
source (https://rpubs.com/FaiHas/197581).

Voici le code.

# prepare input
data(iris)
irissubdf <- iris[1:100, c(1, 3, 5)]
names(irissubdf) <- c("sepal", "petal", "species")
head(irissubdf)
irissubdf$y <- 1
irissubdf[irissubdf[, 3] == "setosa", 4] <- -1
x <- irissubdf[, c(1, 2)]
y <- irissubdf[, 4]
# perceptron function with for
perceptron <- function(x, y, eta, niter) {
# initialize weight vector
weight <- rep(0, dim(x)[2] + 1)
errors <- rep(0, niter)
# loop over number of epochs niter
for (jj in 1:niter) {
# loop through training data set
for (ii in 1:length(y)) {
# Predict binary label using Heaviside activation
# function
z <- sum(weight[2:length(weight)] * as.numeric(x[ii, 
])) + weight[1]
if (z < 0) {
ypred <- -1
} else {
ypred <- 1
}
# Change weight - the formula doesn't do anything
# if the predicted value is correct
weightdiff <- eta * (y[ii] - ypred) * c(1, 
as.numeric(x[ii, ]))
weight <- weight + weightdiff
# Update error function
if ((y[ii] - ypred) != 0) {
errors[jj] <- errors[jj] + 1
}
}
}
# weight to decide between the two species
return(errors)
}
err <- perceptron(x, y, 1, 10)
### my rewriting in functional form auxiliary
### function
faux <- function(x, weight, y, eta) {
err <- 0
z <- sum(weight[2:length(weight)] * as.numeric(x)) + 
weight[1]
if (z < 0) {
ypred <- -1
} else {
ypred <- 1
}
# Change weight - the formula doesn't do anything
# if the predicted value is correct
weightdiff <- eta * (y - ypred) * c(1, as.numeric(x))
weight <<- weight + weightdiff
# Update error function
if ((y - ypred) != 0) {
err <- 1
}
err
}
weight <- rep(0, 3)
weightdiff <- rep(0, 3)
f <- function() {
t <- replicate(10, sum(unlist(lapply(seq_along(irissubdf$y), 
function(i) {
faux(irissubdf[i, 1:2], weight, irissubdf$y[i], 
1)
}))))
weight <<- rep(0, 3)
t
}

Je n'ai pas attendu une amélioration constante en raison de ladite
les questions de. Mais néanmoins, j'ai été vraiment surpris quand j'ai vu la forte détérioration
à l'aide de lapply et replicate.

J'ai obtenu ces résultats en utilisant microbenchmark fonction de microbenchmark bibliothèque

Ce que pourrait être les raisons?
Pourrait-il être certains de fuite de mémoire?

                                                      expr       min         lq       mean     median         uq
f() 48670.878 50600.7200 52767.6871 51746.2530 53541.2440
perceptron(as.matrix(irissubdf[1:2]), irissubdf$y, 1, 10)  4184.131  4437.2990  4686.7506  4532.6655  4751.4795
perceptronC(as.matrix(irissubdf[1:2]), irissubdf$y, 1, 10)    95.793   104.2045   123.7735   116.6065   140.5545
max neval
109715.673   100
6513.684   100
264.858   100

La première fonction est lapply/replicate fonction

La seconde est la fonction avec for boucles

La troisième est la même fonction, dans C++ à l'aide de Rcpp

Ici Selon Roland, le profil de la fonction.
Je ne suis pas sûr que je puisse l'interpréter dans le droit chemin.
Il ressemble pour moi la plupart du temps est passé dans subsetting
La fonction de profilage

  • Veuillez être précis. Je ne vois pas du tout appel à apply dans votre fonction f.
  • Je suggère que vous apprenez comment les fonctions de profil: adv-r.had.co.nz/Profiling.html
  • Il y a quelques erreurs dans votre code; tout d'abord, irissubdf[, 4] <- 1 devrait être irissubdf$y <- 1, de sorte que vous pouvez utiliser ce nom plus tard, et la deuxième, weight n'est pas défini avant de l'utiliser dans f. Il n'est également pas clair pour moi que le <<- est en train de faire la bonne chose dans votre lapply et replicate de commande, mais il n'est pas clair pour moi ce que c'est censé faire. Cela peut également être une différence majeure entre les deux; la <<- doit composer avec les environnements, tandis que l'autre n'a pas, et si je ne sais pas exactement quel effet cela pourrait avoir, c'est pas tout à fait une des pommes avec des pommes comparaison plus.
  • Grâce à ce point, j'ai juste oublié la copie le code d'initialisation de poids( et weightdiff). J'ai utilisé <<- parce que la modification de l'algorithme du vecteur de poids à chaque itération, la seule solution que j'ai trouvé est de mettre à jour les données dans un vecteur de l'appelant de l'environnement
  • Salut, j'ai essayé par curiosité pour supprimer <<-. bien sûr, le code est maintenant le mal, mais il n'y a pas d'amélioration de la performance. Ainsi, le champ d'application le détachement n'est pas la cause
  • 1. Votre code ne s'exécute pas comme l'écrit; vous voulez probablement y et pas irissubdf$y. 2. Votre code ne fonctionne pas comme l'écrit; f() ne renvoie pas le même résultat que perceptron(*).
  • vous avez raison, c'est maintenant correct
  • Votre code est toujours mauvais.