Data.table की गति

Aug 19 2020

मेरे पास एक विशिष्ट प्रदर्शन समस्या है, यदि मैं संभव हो तो और अधिक विस्तार करना चाहता हूं।

संदर्भ:

मैं एक क्यू-लर्निंग एजेंट के लिए एक अजगर कोड नमूने के साथ Google कोलाब पर खेल रहा हूं, जो एक डिफ़ॉल्ट का उपयोग करके एक राज्य और एक मूल्य के लिए एक कार्रवाई को संबद्ध करता है:

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

एक विशेषज्ञ नहीं है, लेकिन मेरी समझ यह है कि यदि मूल्य नहीं मिला है या 0 रिटर्न देता है और कुंजी नहीं मिलती है।

मैं आर में इसका हिस्सा हूं।
समस्या यह है कि मेरे पास कितने राज्य / मूल्य संयोजन नहीं हैं, और तकनीकी रूप से मुझे पता नहीं होना चाहिए कि मुझे कितने राज्यों का अनुमान है।
पहले तो मुझे साथ गलत तरीके से चला गया, rbindके data.frameएस और है कि बहुत धीमी थी।
मैंने फिर अपनी आर ऑब्जेक्ट को ए के साथ बदल दिया data.frame(state, action, value = NA_real)। यह काम करता है लेकिन यह अभी भी बहुत धीमा है। एक अन्य समस्या मेरा डेटा है। बफ़ट ऑब्जेक्ट का अधिकतम आकार है जो भविष्य में समस्याग्रस्त हो सकता है।
तो मैं अपने chanded data.frameएक करने के लिए 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 बनाम मेरे स्थानीय आर कार्यान्वयन की तुलना में, Google 1000x10e4 एक्सेस को ऑब्जेक्ट में 15s कहता है, जबकि मेरा कोड 28 में 100x100 एक्सेस करता है। मुझे बाइट संकलित करके 2s सुधार मिले लेकिन यह अभी भी बहुत बुरा है।

उपयोग करते हुए profvis, मुझे लगता है कि इन दो कॉलों पर डेटा का उपयोग करने में अधिकांश समय व्यतीत होता है:

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

मैं वास्तव में नहीं जानता कि Google के पास क्या है, लेकिन मेरा डेस्कटॉप एक जानवर है। इसके अलावा, मैंने देखा कि कुछ बेंचमार्क की data.tableतुलना में तेज था pandas, इसलिए मुझे लगता है कि समस्या कंटेनर के मेरी पसंद में निहित है।

प्रशन:

  1. क्या डेटा का मेरा उपयोग गलत है। और अजगर के कार्यान्वयन में सुधार और मिलान करने के लिए इसे ठीक किया जा सकता है।
  2. क्या अन्य राज्य / क्रिया संयोजनों को घोषित करने से बचने के लिए एक और डिज़ाइन संभव है जो कि एक समस्या हो सकती है यदि आयाम बहुत बड़े हो जाते हैं?
  3. मैंने हैश पैकेज के बारे में देखा है, क्या यह जाने का रास्ता है?

किसी भी सूचक के लिए बहुत बहुत धन्यवाद!

अपडेट करें:

सभी इनपुट के लिए आपको धन्यवाद। इसलिए मैंने जो किया वह आपके सुझावों का उपयोग करते हुए मेरे डेटा तक पहुंचने के लिए 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 के दशक में गिरा दिया जो कि एक व्यापक सुधार है, लेकिन अजगर defaultdictकार्यान्वयन की तुलना में यह अभी भी बहुत धीमा है।

मैंने निम्नलिखित को नोट किया:
बैच में काम करना: मुझे नहीं लगता कि मैं ऐसा कर सकता हूं क्योंकि फ़ंक्शन पर कॉल पिछले कॉल पर निर्भर करता है।
peudospin> मुझे लगता है कि आप हैरान हैं कि समय लग रहा है। इसलिए मैं हूं लेकिन यही बात बताती है:

और यहां एक संदर्भ के रूप में फ़ंक्शन का कोड है:

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

इस बिंदु पर, यदि कोई और सुझाव नहीं है, तो मैं डेटा समाप्त करूंगा। इस प्रकार के कार्य के लिए डेटा बहुत धीमी है, और मुझे एक envया एक का उपयोग करना चाहिए collections। (: के रूप में वहाँ सुझाव बनाम data.table हैश बनाम सूची से आर तेजी से एक आइटम देखने )

निष्कर्ष:

मैं data.tableएक के लिए बदल दिया collections::dictऔर अड़चन पूरी तरह से गायब हो गया।

जवाब

2 pseudospin Aug 19 2020 at 02:45

data.tableडेटा की बहुत बड़ी तालिका में लुकअप और जोड़-तोड़ करने के लिए तेज़ है, लेकिन यह अजगर शब्दकोशों की तरह एक-एक करके पंक्तियों को जोड़ने में तेज़ नहीं होने वाला है। मुझे उम्मीद है कि हर बार जब आप एक पंक्ति जोड़ेंगे तो पूरी तालिका की नकल होगी जो स्पष्ट रूप से वह नहीं है जो आप चाहते हैं।

आप या तो वातावरण का उपयोग करने की कोशिश कर सकते हैं (जो कुछ हैशमैप की तरह हैं), या यदि आप वास्तव में आर में ऐसा करना चाहते हैं, तो आपको एक विशेषज्ञ पैकेज की आवश्यकता हो सकती है, यहां कुछ विकल्पों के साथ उत्तर का लिंक दिया गया है।

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का उपयोग करने पर भी कोड को तेज बनाता है