R data.table hızı
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 rbind
ve data.frame
s 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.frame
a data.table
sonra 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:
- data.table kullanımım yanlış mı ve python uygulamasını iyileştirmek ve eşleştirmek için düzeltilebilir mi?
- 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?
- 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 defaultdict
uygulaması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:

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 env
a veya a kullanmaya bakmalıyım collections
. (orada önerildiği gibi: Listeden veri, tablo ve hash'den hızlı tek öğe araması )
SONUÇ:
data.table
For collections::dict
a'yı değiştirdim ve darboğaz tamamen ortadan kalktı.
Yanıtlar
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ı .
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