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)
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow