R Language
Przetwarzanie równoległe
Szukaj…
Uwagi
Równoległość na komputerach zdalnych wymaga pobrania bibliotek na każdą maszynę. Preferuj wywołania package::function()
. Kilka pakietów ma wbudowaną równoległość równoległą, w tym caret
, pls
i plyr
.
Microsoft R Open (Revolution R) wykorzystuje również wielowątkowe biblioteki BLAS / LAPACK, które z natury równolegle wiele popularnych funkcji.
Równoległe przetwarzanie z pakietem foreach
Pakiet foreach
przenosi moc przetwarzania równoległego do R. Jednak zanim chcesz używać procesorów wielordzeniowych, musisz przypisać klaster wielordzeniowy. Pakiet doSNOW
to jedna z możliwości.
Prostym zastosowaniem pętli foreach jest obliczenie sumy pierwiastka kwadratowego i kwadratu wszystkich liczb od 1 do 100000.
library(foreach)
library(doSNOW)
cl <- makeCluster(5, type = "SOCK")
registerDoSNOW(cl)
f <- foreach(i = 1:100000, .combine = c, .inorder = F) %dopar% {
k <- i ** 2 + sqrt(i)
k
}
Struktura wyniku foreach
jest kontrolowana przez argument .combine
. Domyślna struktura wyjściowa to lista. W powyższym kodzie c
służy do powrotu wektor zamiast. Zauważ, że funkcja obliczeniowa (lub operator), taka jak "+"
może być również używana do wykonywania obliczeń i zwracania dalej przetwarzanego obiektu.
Należy wspomnieć, że wynikiem każdej pętli foreach jest ostatnie wywołanie. Zatem w tym przykładzie do wyniku zostanie dodane k
.
Parametr | Detale |
---|---|
.połączyć | łączenie funkcji. Określa sposób łączenia wyników pętli. Możliwe wartości to c , cbind , rbind , "+" , "*" ... |
.w celu | jeśli TRUE wynik jest porządkowany zgodnie z kolejnością iteracji vairable (tutaj i ). Jeśli FALSE wynik nie jest porządkowany. Może to mieć pozytywny wpływ na czas obliczeń. |
.pakiety | w przypadku funkcji udostępnianych przez dowolny pakiet oprócz base , takich jak np. mass , randomForest lub jeszcze, musisz dostarczyć tym pakietom c("mass", "randomForest") |
Przetwarzanie równoległe z pakietem równoległym
parallel
pakiet podstawowy umożliwia obliczenia równoległe poprzez rozwidlanie, gniazda i generowanie liczb losowych.
Wykryj liczbę rdzeni obecnych na localhost:
parallel::detectCores(all.tests = FALSE, logical = TRUE)
Utwórz klaster rdzeni na hoście lokalnym:
parallelCluster <- parallel::makeCluster(parallel::detectCores())
Najpierw należy utworzyć funkcję odpowiednią do równoległości. Rozważ zestaw danych mtcars
. Regresję mpg
można poprawić, tworząc osobny model regresji dla każdego poziomu cyl
.
data <- mtcars
yfactor <- 'cyl'
zlevels <- sort(unique(data[[yfactor]]))
datay <- data[,1]
dataz <- data[,2]
datax <- data[,3:11]
fitmodel <- function(zlevel, datax, datay, dataz) {
glm.fit(x = datax[dataz == zlevel,], y = datay[dataz == zlevel])
}
Utwórz funkcję, która może przechodzić przez wszystkie możliwe iteracje zlevels
. Jest to wciąż szeregowe, ale jest ważnym krokiem, ponieważ określa dokładny proces, który będzie równoległy.
fitmodel <- function(zlevel, datax, datay, dataz) {
glm.fit(x = datax[dataz == zlevel,], y = datay[dataz == zlevel])
}
for (zlevel in zlevels) {
print("*****")
print(zlevel)
print(fitmodel(zlevel, datax, datay, dataz))
}
Curry tę funkcję:
worker <- function(zlevel) {
fitmodel(zlevel,datax, datay, dataz)
}
Komputery parallel
korzystające z technologii parallel
nie mogą uzyskać dostępu do środowiska globalnego. Na szczęście każda funkcja tworzy lokalne środowisko, do którego parallel
mogą uzyskać dostęp. Utworzenie funkcji otoki pozwala na równoległość. Funkcję, która ma być zastosowana, należy również umieścić w środowisku.
wrapper <- function(datax, datay, dataz) {
# force evaluation of all paramters not supplied by parallelization apply
force(datax)
force(datay)
force(dataz)
# these variables are now in an enviroment accessible by parallel function
# function to be applied also in the environment
fitmodel <- function(zlevel, datax, datay, dataz) {
glm.fit(x = datax[dataz == zlevel,], y = datay[dataz == zlevel])
}
# calling in this environment iterating over single parameter zlevel
worker <- function(zlevel) {
fitmodel(zlevel,datax, datay, dataz)
}
return(worker)
}
Teraz utwórz klaster i uruchom funkcję otoki.
parallelcluster <- parallel::makeCluster(parallel::detectCores())
models <- parallel::parLapply(parallelcluster,zlevels,
wrapper(datax, datay, dataz))
Zawsze zatrzymuj klaster po zakończeniu.
parallel::stopCluster(parallelcluster)
Pakiet parallel
obejmuje całą rodzinę apply()
z prefiksem par
.
Generowanie liczb losowych
Głównym problemem z równoległością jest stosowanie RNG jako nasion. Liczby losowe według liczby są iterowane przez liczbę operacji od początku sesji lub od ostatniego set.seed()
. Ponieważ równoległe procesy powstają z tej samej funkcji, może używać tego samego ziarna, co może powodować identyczne wyniki! Połączenia będą się odbywać szeregowo na różnych rdzeniach, nie zapewniają żadnych korzyści.
Zestaw nasion musi zostać wygenerowany i wysłany do każdego równoległego procesu. Odbywa się to automatycznie w niektórych pakietach ( parallel
, na snow
itp.), Ale w innych należy to wyraźnie rozwiązać.
s <- seed
for (i in 1:numofcores) {
s <- nextRNGStream(s)
# send s to worker i as .Random.seed
}
Nasiona można również ustawić dla powtarzalności.
clusterSetRNGStream(cl = parallelcluster, iseed)
mcparallelDo
Pakiet mcparallelDo
pozwala na asynchroniczną ocenę kodu R w systemach operacyjnych Unix (np. Linux i MacOSX). Podstawowa filozofia pakietu jest dostosowana do potrzeb eksploracyjnej analizy danych, a nie kodowania. Jeśli chodzi o asynchronię kodowania, rozważ future
pakiet.
Przykład
Utwórz dane
data(ToothGrowth)
Uruchom mcparallel Wykonaj analizę na rozwidleniu
mcparallelDo({glm(len ~ supp * dose, data=ToothGrowth)},"interactionPredictorModel")
Rób inne rzeczy, np
binaryPredictorModel <- glm(len ~ supp, data=ToothGrowth)
gaussianPredictorModel <- glm(len ~ dose, data=ToothGrowth)
Wynik działania mcparallelDo zwraca wartość w środowisku docelowym, np. Globobal, gdy jest kompletny z komunikatem (domyślnie)
summary(interactionPredictorModel)
Inne przykłady
# Example of not returning a value until we return to the top level
for (i in 1:10) {
if (i == 1) {
mcparallelDo({2+2}, targetValue = "output")
}
if (exists("output")) print(i)
}
# Example of getting a value without returning to the top level
for (i in 1:10) {
if (i == 1) {
mcparallelDo({2+2}, targetValue = "output")
}
mcparallelDoCheck()
if (exists("output")) print(i)
}