R ความเร็วของข้อมูลตาราง

Aug 19 2020

ฉันมีปัญหาด้านประสิทธิภาพที่เฉพาะเจาะจงซึ่งฉันต้องการขยายให้มากขึ้นโดยทั่วไปหากเป็นไปได้

บริบท:

ฉันเล่นบน google colab ด้วยตัวอย่างโค้ด python สำหรับเอเจนต์ Q-Learning ซึ่งเชื่อมโยงสถานะและการกระทำกับค่าโดยใช้ defaultdict:

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

ไม่ใช่ผู้เชี่ยวชาญ แต่ความเข้าใจของฉันคือส่งคืนค่าหรือเพิ่มและส่งคืน 0 หากไม่พบคีย์

ฉันกำลังปรับส่วนนี้ใน R
ปัญหาคือฉันไม่มีชุดค่าผสมของรัฐ / ค่าเท่าไหร่และในทางเทคนิคฉันไม่ควรรู้ว่าฉันเดากี่รัฐ
ในตอนแรกที่ผมเดินไปทางที่ผิดกับrbindของdata.frames และที่ช้ามาก
จากนั้นฉันก็แทนที่วัตถุ R ของฉันด้วยไฟล์data.frame(state, action, value = NA_real). มันใช้งานได้ แต่ก็ยังช้ามาก ปัญหาอื่นคือวัตถุ data.frame ของฉันมีขนาดสูงสุดซึ่งอาจเป็นปัญหาในอนาคต
จากนั้นฉันก็เปลี่ยนdata.frameเป็น a 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 จะเข้าถึงวัตถุได้ 1,000x10e4 ในสมมติว่า 15 วินาทีในขณะที่รหัสของฉันทำการเข้าถึง 100x100 ใน 28 ฉันได้รับการปรับปรุง 2 วินาทีโดยการคอมไพล์ไบต์ แต่ก็ยังแย่เกินไป

การใช้profvisฉันเห็นว่าเวลาส่วนใหญ่ใช้ไปกับการเข้าถึง data.table ในการเรียกสองครั้งนี้:

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

ฉันไม่รู้จริงๆว่า Google มีอะไรบ้าง แต่เดสก์ท็อปของฉันเป็นสัตว์ร้าย นอกจากนี้ฉันเห็นว่าเกณฑ์มาตรฐานบางอย่างระบุว่าdata.tableเร็วกว่าpandasดังนั้นฉันคิดว่าปัญหาอยู่ที่ตัวเลือกคอนเทนเนอร์ของฉัน

คำถาม:

  1. การใช้ data.table ของฉันผิดหรือไม่และสามารถแก้ไขเพื่อปรับปรุงและจับคู่การใช้งาน python ได้หรือไม่
  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> ฉันเห็นว่าคุณประหลาดใจที่ได้รับใช้เวลานาน ฉันก็เช่นกัน แต่นั่นคือสิ่งที่ 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.tableสำหรับ a collections::dictและคอขวดก็หายไปอย่างสมบูรณ์

คำตอบ

2 pseudospin Aug 19 2020 at 02:45

data.tableรวดเร็วสำหรับการค้นหาและปรับแต่งในตารางข้อมูลขนาดใหญ่ แต่การเพิ่มแถวทีละแถวจะไม่รวดเร็วเหมือนพจนานุกรม python ฉันคาดหวังว่ามันจะเป็นการคัดลอกทั้งตารางทุกครั้งที่คุณเพิ่มแถวซึ่งไม่ใช่สิ่งที่คุณต้องการอย่างชัดเจน

คุณสามารถลองใช้สภาพแวดล้อม (ซึ่งคล้ายกับแฮชแมป) หรือถ้าคุณต้องการทำใน 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จะมีการใช้งานก็ตาม