R data.table hızı

Aug 19 2020

Mümkünse daha genel bir şekilde genişletmek istediğim belirli bir performans sorunum var.

Bağlam:

Bir Q-Learning aracısı için, bir durumu ve bir eylemi bir varsayılan dikteyi kullanarak bir değerle ilişkilendiren bir python kodu örneğiyle google colab'da oynuyorum:

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

Uzman değil ama benim anladığım kadarıyla anahtar bulunmazsa değeri döndürür veya toplar ve 0 döndürür.

Bunun bir kısmını R.'de uyarlıyorum
, sorun şu ki, kaç tane durum / değer kombinasyonuna sahip olduğumu bilmiyorum ve teknik olarak tahmin ettiğim kaç durumu bilmemem gerekiyor.
İlk başta ben, yanlış yöne gitti rbindve data.frames ve çok yavaş olduğunu söyledi.
Daha sonra R nesnemi a ile değiştirdim data.frame(state, action, value = NA_real). işe yarıyor ama yine de çok yavaş. başka bir sorun da data.frame nesnem, gelecekte sorun yaratabilecek maksimum boyuta sahip.
Sonra anlatmaya diğer chanded data.framea data.tablesonra nihayet (devlet, eylem) tarafından dizine, bana en kötü performansı verdi.

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

Sorun:

Googlecolab / python ile yerel R uygulamam karşılaştırıldığında, google nesneye 1000x10e4 erişim gerçekleştirirken, diyelim ki 15 saniye içinde kodum 100x100 erişimini 28 saniyede gerçekleştiriyor. Bayt derleme ile 2s iyileştirmelerim var ama bu yine de çok kötü.

Kullanarak profvis, zamanın çoğunun bu iki çağrıda data.table'a erişmek için harcandığını görüyorum:

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

Google'ın neye sahip olduğunu gerçekten bilmiyorum ama masaüstüm bir canavar. Ayrıca data.table, daha hızlı olduğunu belirten bazı kriterler gördüm pandas, bu yüzden sorunun konteyner seçimimde yattığını düşünüyorum.

Sorular:

  1. data.table kullanımım yanlış mı ve python uygulamasını iyileştirmek ve eşleştirmek için düzeltilebilir mi?
  2. Boyutlar çok büyüdüğünde sorun olabilecek tüm durum / eylem kombinasyonlarını bildirmekten kaçınmak için başka bir tasarım mümkün müdür?
  3. hash paketini gördüm, gidilecek yol bu mu?

Herhangi bir işaretçi için çok teşekkürler!

GÜNCELLEME:

tüm girdiler için teşekkürler. Yani yaptığım şey, önerilerinizi kullanarak verilerime 3 erişimi değiştirmekti.

#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]

bu, çalışma zamanını 33'lerden 21'lere düşürdü, bu büyük bir gelişme, ancak python defaultdictuygulamasına kıyasla hala çok yavaş .

Şunu not ettim:
toplu çalışma: İşleve yapılan çağrı önceki çağrıya bağlı olduğundan yapabileceğimi sanmıyorum.
peudospin> Almanın zaman alıcı olmasına şaşırdığınızı görüyorum. Ben de öyleyim ama profvis'in belirttiği bu:

ve burada referans olarak işlevin kodu:

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

Bu noktada, eğer daha fazla öneri yoksa, data.table'ın bu tür bir görev için çok yavaş olduğu sonucuna varacağım ve enva veya a kullanmaya bakmalıyım collections. (orada önerildiği gibi: Listeden veri, tablo ve hash'den hızlı tek öğe araması )

SONUÇ:

data.tableFor collections::dicta'yı değiştirdim ve darboğaz tamamen ortadan kalktı.

Yanıtlar

2 pseudospin Aug 19 2020 at 02:45

data.tableçok büyük veri tablolarında arama ve manipülasyon yapmak için hızlıdır, ancak python sözlükleri gibi satırları tek tek eklemek hızlı olmayacaktır. Açıkça istediğiniz gibi olmayan bir satır eklediğiniz her seferde tüm tabloyu kopyalamasını beklerdim.

Ya ortamları kullanmayı deneyebilirsiniz (bir hashmap gibi bir şeydir) ya da bunu gerçekten R'de yapmak istiyorsanız, uzman bir pakete ihtiyacınız olabilir, işte birkaç seçenek içeren bir yanıtın bağlantısı .

AbdessabourMtk Aug 19 2020 at 04:53

Kıyaslama

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)

o kullanarak temizlemek olduğunu seqçağırmaktan daha tüketir fazla zaman 1:nb. artı kartezyen bir ürün kullanmak 1:nb, kullanıldığında bile kodu daha hızlı hale getirir