data.table의 R 속도

Aug 19 2020

가능한 한 더 일반적으로 확장하고 싶은 특정 성능 문제가 있습니다.

문맥:

나는 defaultdict를 사용하여 상태와 작업을 값에 연결하는 Q-Learning 에이전트를위한 파이썬 코드 샘플로 Google colab에서 놀았습니다.

self._qvalues = defaultdict(lambda: defaultdict(lambda: 0))
return self._qvalues[state][action]

전문가는 아니지만 내 이해는 값을 반환하거나 키를 찾지 못하면 0을 반환합니다.

나는 R
에서 이것의 일부를 적용 하고 있습니다. 문제는 내가 얼마나 많은 상태 / 값 조합을 가지고 있는지, 기술적으로 내가 얼마나 많은 상태를 추측하는지 알 수 없다는 것입니다.
처음에 나는 rbindof data.frames와 함께 잘못된 길을 갔고 그것은 매우 느 렸습니다.
그런 다음 내 R 개체를 data.frame(state, action, value = NA_real). 작동하지만 여전히 매우 느립니다. 또 다른 문제는 내 data.frame 객체의 최대 크기가 미래에 문제가 될 수 있다는 것입니다.
나는 내를 chanded data.frameA와 data.table나는 마침내 (상태, 액션)하여 인덱스 나에게 최악의 성능을 준.

qvalues <- data.table(qstate = rep(seq(nbstates), each = nbactions),
                        qaction = rep(seq(nbactions), times = nbstates),
                        qvalue = NA_real_,
                        stringsAsFactors =  FALSE)
setkey(qvalues, "qstate", "qaction")

문제:

googlecolab / python과 내 로컬 R 구현을 비교하면 Google은 15 초에 객체에 대한 1000x10e4 액세스를 수행하고 내 코드는 28 초에 100x100 액세스를 수행합니다. 바이트 컴파일로 2s 향상을 얻었지만 여전히 너무 나쁩니다.

를 사용하면 profvis대부분의 시간이 다음 두 호출에서 data.table에 액세스하는 데 소비됩니다.

qval <- self$qvalues[J(state, action), nomatch = NA_real_]$qvalue
self$qvalues[J(state, action)]$qvalue <- value

나는 구글이 무엇을 가지고 있는지 정말로 모른다. 그러나 나의 데스크탑은 짐승이다. 또한 data.table보다 빠르다 는 일부 벤치 마크를 보았 pandas으므로 문제는 내가 선택한 컨테이너에 있다고 생각합니다.

질문 :

  1. data.table 사용이 잘못되었으며 파이썬 구현을 개선하고 일치시키기 위해 수정할 수 있습니까?
  2. 차원이 너무 커지면 문제가 될 수있는 모든 상태 / 동작 조합을 선언하는 것을 방지 할 수있는 또 다른 디자인이 있습니까?
  3. 해시 패키지에 대해 본 적이 있습니다. 갈 길이입니까?

포인터 주셔서 감사합니다!

최신 정보:

모든 입력에 감사드립니다. 그래서 내가 한 일은 귀하의 제안을 사용하여 내 data.table에 대한 3 개의 액세스를 대체하는 것입니다.

#self$qvalues[J(state, action)]$qvalue <- value
self$qvalues[J(state, action), qvalue := value] #self$qvalues[J(state, action),]$qvalue <- 0 self$qvalues[J(state, action), qvalue := 0]
#qval <- self$qvalues[J(state, action), nomatch = NA_real_]$qvalue
qval <- self$qvalues[J(state, action), nomatch = NA_real_, qvalue]

이로 인해 런타임이 33 초에서 21 초로 크게 개선되었지만 Python defaultdict구현에 비해 여전히 매우 느립니다 .

나는 다음 사항에 주목했다.
일괄 작업 : 함수 호출이 이전 호출에 따라 달라 지므로 할 수 없다고 생각합니다.
peudospin> 시간이 많이 걸린다는 것에 놀랐습니다. 나도 그렇다.하지만 그것이 profvis가 말하는 것이다 :

그리고 여기에 참조로서의 함수 코드 :

QAgent$set("public", "get_qvalue", function( state, action) {
  #qval <- self$qvalues[J(state, action), nomatch = NA_real_]$qvalue
  qval <- self$qvalues[J(state, action), nomatch = NA_real_, qvalue] if (is.na(qval)) { #self$qvalues[self$qvalues$qstate == state & self$qvalues$qaction == action,]$qvalue <- 0 #self$qvalues[J(state, action),]$qvalue <- 0 self$qvalues[J(state, action), qvalue := 0]
    return(0)
  }
  return(qval)
})

이 시점에서 더 이상 제안이 없으면 data.table이 이런 종류의 작업에 너무 느리다는 결론을 내릴 것이므로 env또는 collections. (제안 된대로 : R 목록에서 빠른 단일 항목 조회 대 data.table 대 해시 )

결론:

나는 대체 data.tableA의 collections::dict및 병목 현상이 완전히 사라졌다.

답변

2 pseudospin Aug 19 2020 at 02:45

data.table매우 큰 데이터 테이블에서 조회 및 조작을 수행하는 데는 빠르지 만 파이썬 사전과 같이 행을 하나씩 추가하는 데는 빠르지 않습니다. 나는 당신이 분명히 원하는 것이 아닌 행을 추가 할 때마다 전체 테이블을 복사 할 것이라고 기대합니다.

환경 (해시 맵과 같은 것)을 사용해 보거나 R에서이 작업을 정말로 수행하려면 전문가 패키지가 필요할 수 있습니다. 여기 에 몇 가지 옵션이있는 답변에 대한 링크 가 있습니다.

AbdessabourMtk Aug 19 2020 at 04:53

기준

library(data.table)
Sys.setenv('R_MAX_VSIZE'=32000000000) # add to the ram limit
setDTthreads(threads=0) # use maximum threads possible
nbstates <- 1e3
nbactions <- 1e5

cartesian <- function(nbstates,nbactions){
    x= data.table(qstate=1:nbactions)
    y= data.table(qaction=1:nbstates)
    k = NULL
    x = x[, c(k=1, .SD)]
    setkey(x, k)
    y = y[, c(k=1, .SD)]
    setkey(y, NULL)
    x[y, allow.cartesian=TRUE][, c("k", "qvalue") := list(NULL, NA_real_)][]
}

#comparing seq with `:`
(bench = microbenchmark::microbenchmark(
    1:1e9,
    seq(1e9),
    times=1000L
    ))
#> Unit: nanoseconds
#>        expr  min   lq     mean median   uq   max neval
#>     1:1e+09  120  143  176.264  156.0  201  5097  1000
#>  seq(1e+09) 3039 3165 3333.339 3242.5 3371 21648  1000
ggplot2::autoplot(bench)


(bench = microbenchmark::microbenchmark(
    "Cartesian product" = cartesian(nbstates,nbactions),
    "data.table assignement"=qvalues <- data.table(qstate = rep(seq(nbstates), each = nbactions),
                        qaction = rep(seq(nbactions), times = nbstates),
                        qvalue = NA_real_,
                        stringsAsFactors =  FALSE),
    times=100L))
#> Unit: seconds
#>                    expr      min       lq     mean   median       uq      max neval
#>       Cartesian product 3.181805 3.690535 4.093756 3.992223 4.306766 7.662306  100
#>  data.table assignement 5.207858 5.554164 5.965930 5.895183 6.279175 7.670521  100
#>      data.table  (1:nb) 5.006773 5.609738 5.828659  5.80034 5.979303 6.727074  100
#>  
#>  
ggplot2::autoplot(bench)

seq호출하는 것보다 사용 이 더 많은 시간 을 소비 한다는 것은 분명 합니다 1:nb. 또한 데카르트 곱을 사용하면 1:nb사용되는 경우에도 코드가 더 빨라집니다.