2013-04-12 17 views
12

Vorrei accelerare la mia funzione di bootstrap, che funziona perfettamente bene. Ho letto che dal momento che R 2.14 c'è un pacchetto chiamato parallel, ma lo trovo molto difficile per sb. con scarsa conoscenza dell'informatica per implementarla davvero. Forse qualcuno può aiutare.Utilizzo di R parallelo per accelerare il bootstrap

Quindi qui abbiamo un bootstrap:

n<-1000 
boot<-1000 
x<-rnorm(n,0,1) 
y<-rnorm(n,1+2*x,2) 
data<-data.frame(x,y) 
boot_b<-numeric() 
for(i in 1:boot){ 
    bootstrap_data<-data[sample(nrow(data),nrow(data),replace=T),] 
    boot_b[i]<-lm(y~x,bootstrap_data)$coef[2] 
    print(paste('Run',i,sep=" ")) 
} 

L'obiettivo è quello di utilizzare l'elaborazione parallela/sfruttare i core multipli del mio PC. Sto usando R sotto Windows. Grazie!

EDIT (dopo la risposta da Noah)

la seguente sintassi può essere utilizzato per il test:

library(foreach) 
library(parallel) 
library(doParallel) 
registerDoParallel(cores=detectCores(all.tests=TRUE)) 
n<-1000 
boot<-1000 
x<-rnorm(n,0,1) 
y<-rnorm(n,1+2*x,2) 
data<-data.frame(x,y) 
start1<-Sys.time() 
boot_b <- foreach(i=1:boot, .combine=c) %dopar% { 
    bootstrap_data<-data[sample(nrow(data),nrow(data),replace=T),] 
    unname(lm(y~x,bootstrap_data)$coef[2]) 
} 
end1<-Sys.time() 
boot_b<-numeric() 
start2<-Sys.time() 
for(i in 1:boot){ 
    bootstrap_data<-data[sample(nrow(data),nrow(data),replace=T),] 
    boot_b[i]<-lm(y~x,bootstrap_data)$coef[2] 
} 
end2<-Sys.time() 
start1-end1 
start2-end2 
as.numeric(start1-end1)/as.numeric(start2-end2) 

Tuttavia, sulla mia macchina il codice R semplice è veloce. Si tratta di uno degli effetti collaterali noti dell'elaborazione parallela, vale a dire che i costi generali impongono il processo che si aggiunge al tempo in "compiti semplici" come questo?

Modifica: sulla mia macchina il codice parallel richiede circa 5 volte di più del codice 'semplice'. Apparentemente questo fattore non cambia in quanto aumento la complessità dell'attività (ad esempio, aumento boot o n). Quindi forse c'è un problema con il codice o la mia macchina (elaborazione basata su Windows?).

risposta

7

Non ho ancora testato foreach con il parallel backend su Windows, ma credo che questo sarà il lavoro per voi:

library(foreach) 
library(doSNOW) 

cl <- makeCluster(c("localhost","localhost"), type = "SOCK") 
registerDoSNOW(cl=cl) 

n<-1000 
boot<-1000 
x<-rnorm(n,0,1) 
y<-rnorm(n,1+2*x,2) 
data<-data.frame(x,y) 
boot_b <- foreach(i=1:boot, .combine=c) %dopar% { 
    bootstrap_data<-data[sample(nrow(data),nrow(data),replace=T),] 
    unname(lm(y~x,bootstrap_data)$coef[2]) 
} 
+0

Grazie, ho riassunto la sintassi suggerita per il test (codice modificato sopra). Ora usa il 100% della mia CPU (cioè tutti i processori). Tuttavia, questo è più lento rispetto a farlo senza elaborazione parallela, vedi sopra. – tomka

+0

Sarebbe bello se tu potessi dare qualche suggerimento addizionale sul problema del tempo, cioè perché il tuo suggerimento non lo sta accelerando? Grazie. – tomka

+0

Hmm. Interessante.Sulla mia macchina, (8 core HT, 8 GB ram, Ubuntu 12.04), ho ottenuto un aumento della velocità di circa 3.4X con poca RAM. Non ho familiarità con il multithreading in un ambiente Windows. Ecco alcune cose da provare: – Noah

8

provare il pacchetto di boot. È ben ottimizzato e contiene un argomento parallel. La cosa complicata di questo pacchetto è che devi scrivere nuove funzioni per calcolare la tua statistica, che accetta i dati su cui stai lavorando e un vettore di indici per ricampionare i dati. Così, a partire dal punto in cui si definisce data, si potrebbe fare qualcosa di simile:

# Define a function to resample the data set from a vector of indices 
# and return the slope 
slopeFun <- function(df, i) { 
    #df must be a data frame. 
    #i is the vector of row indices that boot will pass 
    xResamp <- df[i, ] 
    slope <- lm(y ~ x, data=xResamp)$coef[2] 
} 

# Then carry out the resampling 
b <- boot(data, slopeFun, R=1000, parallel="multicore") 

b$t è un vettore della statistica ricampionata, e boot ha un sacco di metodi bello fare facilmente roba con esso - per esempio plot(b)

Si noti che i metodi paralleli dipendono dalla piattaforma. Sul tuo computer Windows, dovrai utilizzare parallel="snow".

+0

La tua soluzione sta accelerando del 25% circa sul mio computer. Bello. Nella vera applicazione che sto guardando, il problema è molto più complesso, perché la mia funzione restituisce un elenco di parametri, che devono essere tutti riavviati. Pertanto, sto cercando un'implementazione diretta del parallelo. – tomka

+0

@tomka, 'boot' funziona ancora bene in questa situazione. La funzione che scrivi può restituire un vettore di parametri a cui sei interessato. In effetti, questo è uno dei punti di forza del boot: la capacità di scrivere autonomamente qualsiasi funzione arbitraria. –

3

Penso che il problema principale sia che si hanno molte piccole attività. In alcuni casi, è possibile migliorare le prestazioni utilizzando compito chunking, che si traduce in meno, ma i trasferimenti di dati più grandi tra il master e dei lavoratori, che è spesso più efficace:

boot_b <- foreach(b=idiv(boot, chunks=getDoParWorkers()), .combine='c') %dopar% { 
    sapply(1:b, function(i) { 
    bdata <- data[sample(nrow(data), nrow(data), replace=T),] 
    lm(y~x, bdata)$coef[[2]] 
    }) 
} 

Mi piace usare la funzione idiv per questo, ma potresti b=rep(boot/detectCores(),detectCores()) se ti va.