R에서 for 문의 느린 속도를 개선하기 위해 가장 많이 사용하는 방법 중 하나가 doParallel 패키지의 foreach()를 통해 병렬로 연산하는 것인데요. 하지만 seed를 설정하여 이후 같은 결과를 얻게 하는 재현성(reproducibility)을 위해서 foreach()를 사용할 때에는 주의할 필요가 있습니다. 보통 seed를 설정할 때 사용하는 set.seed()가 제대로 적용되지 않기 때문이죠.

다음의 예제를 확인해볼까요? (본문에서 사용한 예제 코드는 doRNG 패키지의 RDocument를 참고하였습니다.)

각 반복마다 \(Uniform(0,1)\)에서 sample 1개씩을 뽑는 과정을 foreach() 함수를 사용한 예제입니다. 그리고 재현성을 위해 set.seed(1234)를 설정해주었습니다.

library(doParallel)
cl <- makeCluster(2)
registerDoParallel(cl)

# foreach에서 %dopar% 구문으로 생성
set.seed(1234)
s1 <- foreach(i = 1:4) %dopar% { 
  runif(1) 
}

set.seed(1234)
s2 <- foreach(i = 1:4) %dopar% { 
  runif(1) 
}

# 두 벡터가 같은지 비교
identical(s1, s2)
## [1] FALSE

하지만 예상과 달리 seed를 지정해주었음에도 runif()에서 뽑힌 값이 다른 것을 확인할 수 있습니다. (정확한 이유는 잘 모르겠습니다. . .)

그렇다면 이 문제는 어떻게 해결할 수 있을까요?

바로 doRNG 패키지를 사용하면 됩니다!!!


doRNG 패키지

앞에서의 문제는 doRNG 패키지 내의 %dorng% 또는 registerDoRNG()를 활용하여 해결할 수 있으며, 이제 두 방법을 하나씩 살펴보겠습니다.

1. %dorng% + .options.RNG

첫 번째 방법은 %dopar%대신 %dorng%를 사용하는 것인데요. 이와 더불어 .options.RNG 옵션으로 seed를 설정해주어야 합니다.

다음에서 사용법과 간단한 예제를 확인해보겠습니다. 다음은 %dorng% + .options.RNG = 1234가 같은 값을 생성하는지 비교하는 예제입니다.

# 패키지 load
library(doRNG)

# %dorng% 구문으로 생성
r1 <- foreach(i = 1:4, .options.RNG = 1234) %dorng% { 
  runif(1) 
}

r2 <- foreach(i = 1:4, .options.RNG = 1234) %dorng% { 
  runif(1) 
}

# 두 벡터가 같은지 비교
identical(r1, r2)
## [1] TRUE

set.seed()를 사용했을 때와 달리 %dorng% + .options.RNG를 적용하여 생성된 두 벡터가 같은 것을 확인할 수 있습니다.

2. registerDoRNG

두 번째 방법은 registerDoRNG() 함수를 사용하여 seed를 설정하는 것입니다. set.seed()와 마찬가지로 함수 argument로 seed를 설정해주면 됩니다.

다음은 같은 seed로 %dorng% + .options.RNGregisterDoRNG()가 같은 값을 생성하는지 비교하는 예제입니다.

# registerDoRNG로 seed 설정
registerDoRNG(1234)
r1 <- foreach(i = 1:4) %dopar% { 
  runif(1) 
}

# %dorng% 구문으로 seed 설정
r2 <- foreach(i = 1:4, .options.RNG = 1234) %dorng% { 
  runif(1) 
}

# 두 벡터가 같은지 비교
identical(r1, r2)
## [1] TRUE

사용하는 방법이 약간 다르지만, 두 방법에서 생성된 벡터가 서로 같다는 것을 확인할 수 있습니다.