일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- 최저 시급
- 성악설
- 통계 오류
- 인터스텔라
- R4DS
- 산입 범위
- 조던피더슨
- 이기적 유전자
- 비행기 추락
- 산입범위
- t-test
- 통계오류
- 큰수의 법칙
- R 기초
- 최저시급 개정안
- 티모시페리스
- 찬물샤워
- 핵개발
- 동전 던지기
- 유닛테스트
- 비율
- t검정
- 선형성
- R 프로그래밍
- 멘탈관리
- 아인슈타인
- 비선형성
- 핵 개발
- 수학적 사고
- 자기관리
- Today
- Total
public bigdata
병렬 프로그래밍 본문
해당 글은 R-blogger 포스트를 보고 개인적으로 정리한 내용이기에 중간중간 내용이 빠져있을 수 있으며 이해하기 어려울 것이다. 궁금하다면 원글을 참고할 것
※ 병렬 처리는 컴퓨터 cpu의 여러 코어를 사용하여 여러 작업을 동시에 실행하는 것이다.
병렬 처리가 필요한 경우?
- 계산적으로 무거운 과정
- 여러 개의 기계 학습 모델을 장착하는 것
- 일반적으로 3분 이상 걸리는 프로세스가 있는 경우 병렬 프로세싱을 사용하면 좋다
- 하나의 과제가 있더라도 해당 과제를 작은 조각으로 나누면 병렬 처리의 이점을 얻을 수 있다
※ R 병렬 프로그래밍에 널리 사용되는 패키지는 parallel, foreach가 있다.
1. 데이터 가져오기
library(tidyverse) # ggplot2, dplyr, tidyr, readr,
# purrr, tibble, stringr, forcats
horror_movies <- readr::read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2019/2019-10-22/horror_movies.csv")
아래와 같은 데이터를 가져온다. 영화에 대한 데이터이다.
> glimpse(horror_movies)
Observations: 3,328
Variables: 12
$ title <chr> "Gut (2012)", "The Haunting of Mia Moss (2017)",...
$ genres <chr> "Drama| Horror| Thriller", "Horror", "Horror", "...
$ release_date <chr> "26-Oct-12", "13-Jan-17", "21-Oct-17", "23-Apr-1...
$ release_country <chr> "USA", "USA", "Canada", "USA", "USA", "UK", "USA...
$ movie_rating <chr> NA, NA, NA, "NOT RATED", NA, NA, "NOT RATED", NA...
$ review_rating <dbl> 3.9, NA, NA, 3.7, 5.8, NA, 5.1, 6.5, 4.6, 5.4, 5...
$ movie_run_time <chr> "91 min", NA, NA, "82 min", "80 min", "93 min", ...
$ plot <chr> "Directed by Elias. With Jason Vail, Nicholas Wi...
$ cast <chr> "Jason Vail|Nicholas Wilder|Sarah Schoofs|Kirsti...
$ language <chr> "English", "English", "English", "English", "Ita...
$ filming_locations <chr> "New York, USA", NA, "Sudbury, Ontario, Canada",...
$ budget <chr> NA, "$30,000", NA, NA, NA, "$3,400,000", NA, NA,...
>
아래 코드는 불러온 데이터의 release_date 변수를 "-"기준으로 day, month, year를 생성하고 결측치 제거 그리고 day1인 경우의 데이터를 삭제하는데 그 이유는 모르겠다.
horror_date <- horror_movies %>%
separate(
release_date,
c("day", "month", "year"),
sep = "-")
horror_date$day <- as.numeric(horror_date$day)
# Remove rows without Date of the month
horror_date <- horror_date %>% filter(day < 32)
# I am excluding Day 1 from the analysis (Most aggreements starts at 1st)
horror_date_table <- horror_date %>% filter(day > 1)
# let's check what is the most common day in the month for a horror movie release
data <- horror_date_table %>%
group_by(day) %>%
count() %>%
arrange(desc(n))
data %>% print(n=30)
## # A tibble: 30 x 2
## # Groups: day [30]
## day n
##
## 1 13 124
## 2 18 119
## 3 25 119
## 4 21 110
## 5 31 107
## 6 28 102
## 7 10 100
## 8 20 100
## 9 5 99
## 10 7 98
## 11 2 95
## 12 29 94
## 13 14 91
## 14 12 90
## 15 24 90
## 16 8 88
## 17 15 88
## 18 17 88
## 19 16 87
## 20 3 86
## 21 4 86
## 22 6 86
## 23 9 85
## 24 22 85
## 25 26 84
## 26 11 83
## 27 27 82
## 28 19 77
## 29 30 74
## 30 23 65
아래 코드가 무엇인지는 잘 모르겠다. 어쨋든 병렬 처리를 위해서 2782건의 공포영화가 한달(30일) 기준으로 30일 중 하나의 일에 개봉될 확률이 어떤지 구하는 다항분포 시뮬레이션이라고 한다.
multfun <- function(x){
a <- list()
avc <- rmultinom(1000000, size=2782, prob=rep(1/30, 30)) # Create probability matrix
for (i in 1:30){ a[[i]] <- mean(avc[i,1:1000000] >=x) } # Find probabilities for each day
ifelse(do.call(sum,a)/30 <= 0.5, p_val <- 2 * do.call(sum,a)/30,
p_val <- 2*(1 - do.call(sum,a)/30))
}
코드를 돌려보면 아래와 같다고 한다.
# Run an example
multfun(124)
## [1] 0.001844267
위 코드를 여러번 아래처럼 여러번 돌려야 하는 경우가 생길 수 있다.
system.time({
multfun(124)
multfun(119)
multfun(110)
multfun(107)
multfun(102)
})
## user system elapsed
## 15.72 0.21 15.94
이 때 더 쉬운 방법은 lapply함수를 사용하는 것이다.
system.time({
lapply(c(124,119,119,197,102), multfun)
})
## user system elapsed
## 15.64 0.29 15.92
lapply나, sapply를 사용해본 적이 있다면 절반의 병렬처리를 이미 할 수 있는 것이다.
2. parallel 패키지의 parLapply함수를 이용한 병렬 처리
system.time({
cl <- makeCluster(5)
avc <- parLapply(cl , c(200000, 200000, 200000, 200000, 200000), fastbinom)
avc <- do.call(cbind,avc)
clusterExport(cl, varlist="avc", envir=environment())
parLapply(cl, data$n, multfun_fin)
})
## user system elapsed
## 0.56 0.60 4.10
위 코드에서 clusterExport 함수는 parLapply 내부의 multfun_fin이라는 함수가 사용할 avc라는 변수를 전역환경에서 찾아서 사용할 수 있도록 미리 등록해두는 것이다. varlist에 직접 값을 생성해서 넣는 경우라면 상관 없지만 미리 생성한 변수를 parLapply 동작 시 사용하게 된다면 모든 cpu core가 변수를 인식할 수 있도록 미리 등록해줘야 한다.
그리고 do.call이라는 함수도 보았는데 lapply와 비교해서 설명한다.
> lapply(list(a = c(1, 2, 3), b = c(4, 5, 6)), sum)
$a
[1] 6
$b
[1] 15
> do.call(sum, list( a = c(1, 2, 3), b = c(4, 5, 6)), )
[1] 21
lapply의 경우 리스트 내의 a, b 에 대해서 각각 sum 함수를 실행한다. 그런데 do.call은 인자를 넘겨서 함수를 실행하는 것이고 lapply는 데이터를 넘겨서 실행하는 것이기 때문에(추측) lapply의 경우 데이터만 넘겨주고 필요한 인자는 함수의 뒤에 작성하여 알려주고, do.call의 경우 list에 인자로 사용할 것들을 작성해서 넘겨주어야 한다. 아래 코드를 보자
lapply(list(a = c(1, 2, 3), b = c(4, 5, NA)), sum, na.rm = T)
do.call(sum, list( a = c(1, 2, 3), b = c(4, 5, NA), na.rm = T))
위 코드를 보면 sum계산에 NA값으로 인해서 na.rm=T라는 인자가 필요하다. lapply는 list에서 a, b 각각 데이터를 넘겨주고 sum을 계산하고 인자는 실행할 함수 뒤에 추가로 넘겨준다. do.call은 list의 a, b 각각 넘겨주고 sum을 실행하는 것이 아니라 sum(a, b)이렇게 넘겨준다. 그리고 필요한 인자도 list 내의 요소로 추가해줘야 원하는대로 실행이 가능하다.
### 코드 및 결과 ###
> lapply(list(a = c(1, 2, 3), b = c(4, 5, NA)), sum, na.rm = T)
$a
[1] 6
$b
[1] 9
> do.call(sum, list( a = c(1, 2, 3), b = c(4, 5, NA), na.rm = T))
[1] 15
lapply는 list의 요소 갯수 만금 sum을 호출하고 do.call은 요소들을 sum함수에 한 번에 넘겨주어 sum 함수를 1번만 호출하게 된다.
참고 링크
링크1
링크2
R이라는 언어에 대해서 조금씩 깊이 공부해 가면서 내가 모르는 내용이 너무도 많은 것 같다. 배움에는 끝이 없는건가... 조금 지치기도 한다.
'R programming' 카테고리의 다른 글
R data import(DBI/http data download) - 데이터 캠프 정리 (0) | 2020.01.04 |
---|---|
R 데이터 저장 (fread, rds, feather 등) (0) | 2019.12.19 |
ggplot tip (0) | 2019.11.07 |
mutiple plot function 코드 뜯어 보기 (0) | 2019.09.28 |
advanced R 자료_비표준 평가 관련3 (0) | 2019.09.03 |