Bigglm в R: ограничения и улучшения исходного кода (например, Call to Fortran) - PullRequest
4 голосов
/ 03 января 2012

Недавно я использовал R для запуска обобщенной линейной модели (GLM) в CSV-файле размером 100 МБ (9 миллионов строк на 5 столбцов). Содержимое этого файла включает в себя 5 столбцов, называемых depvar, var1, var2, var3, var4, и все они распределены случайным образом, так что столбцы содержат числа, равные либо 0,1, либо 2. В основном я использовал пакет biglm для запуска GLM на этот файл данных и R обработали это примерно за 2 минуты. Это было на машине с Linux, использующей R версии 2.10 (в настоящее время я обновляю до 2.14), 4 ядра и 8 ГБ оперативной памяти. По сути, я хочу выполнить код быстрее, примерно на 30-60 секунд. Одним из решений является добавление большего количества ядер и ОЗУ, но это будет только временное решение, так как я понимаю, что наборы данных будут только увеличиваться. В идеале я хочу найти способ сделать код быстрее для bigglm. Я запустил некоторый код R профилирования на наборе данных. Добавление следующего кода (перед кодом, который я хочу запустить, чтобы проверить его скорость):

Rprof('method1.out')

Затем после ввода этой команды я пишу свой код bigglm, который выглядит примерно так:

x<-read.csv('file location of 100mb file')
form<-depvar~var1+var2+var3+var4
a<-bigglm(form, data=x, chunksize=800000, sandwich=FALSE, family=binomial())
summary(a)
AIC(a)
deviance(a)

После запуска этих кодов, которые занимают от 2 до 3 минут, я набираю следующее, чтобы увидеть мой код профилирования:

Rprofsummary('method1.out')

То, что я тогда получаю, это нарушение процесса bigglm, и отдельные строки занимают очень много времени. После просмотра этого я был удивлен, увидев, что был вызов кода Fortran, который занимал очень много времени (около 20 секунд). Этот код можно найти в базовом файле Bigglm по адресу:

http://cran.r-project.org/web/packages/biglm/index.html

В файле bigglm 0.8.tar.gz

По сути, я спрашиваю сообщество: можно ли сделать этот код быстрее? Например, изменив код, чтобы он вызывал код Фортрана для выполнения QR-декомпозиции. Кроме того, были и другие функции, такие как as.character и model.matrix, которые также занимали много времени. Я не приложил сюда файл профилирования, так как считаю, что его легко воспроизвести, учитывая предоставленную мною информацию, но в основном я намекаю на большую проблему больших данных и обработки GLM на этих больших данных. Эта проблема является общей для сообщества R, и я думаю, что любые отзывы или помощь будут благодарны по этому вопросу. Вы, вероятно, можете легко воспроизвести этот пример, используя другой набор данных, и посмотреть, что занимает так много времени в коде bigglm, и посмотреть, являются ли они теми же вещами, которые я нашел. Если так, может кто-то, пожалуйста, помогите мне понять, как заставить bigglm работать быстрее. После того, как Бен запросил его, я загрузил фрагмент кода профилирования, который у меня был, а также первые 10 строк моего CSV-файла:

 CSV File:

 var1,var2,var3,var4,depvar
   1,0,2,2,1
   0,0,1,2,0
   1,1,0,0,1
   0,1,1,2,0
   1,0,0,3,0
   0,0,2,2,0
   1,1,0,0,1
   0,1,2,2,0
   0,0,2,2,1

Этот вывод CSV был скопирован из моего текстового редактора UltraEdit, и видно, что var1 принимает значения 0 или 1, var2 принимает значения 0 и 1, var3 принимает значения 0,1,2, var4 принимает значения 0 , 1,2,3 и depvar принимает значения 1 или 0. Этот csv может быть реплицирован в Excel с помощью функции RAND примерно до 1 миллиона строк, затем его можно скопировать и вставить несколько раз, чтобы получить большое количество строк в текстовый редактор, такой как ultraedit. В основном введите RAND () в один столбец для 1 миллиона столбцов, затем выполните округление (столбец) в столбце рядом со столбцом RAND (), чтобы получить 1 с и нули. То же самое относится и к 0,1,2,3.

Файл профилирования длинный, поэтому я добавил строки, которые занимали больше всего времени:

 summaryRprof('method1.out')



$by.self
                        self.time self.pct total.time total.pct
"model.matrix.default"      25.40     20.5      26.76      21.6
".Call"                     20.24     16.3      20.24      16.3
"as.character"              17.22     13.9      17.22      13.9
"[.data.frame"              14.80     11.9      22.12      17.8
"update.bigqr"               5.72      4.6      14.38      11.6
"-"                          4.36      3.5       4.36       3.5
"anyDuplicated.default"      4.18      3.4       4.18       3.4
"|"                          3.98      3.2       3.98       3.2
"*"                          3.44      2.8       3.44       2.8
"/"                          3.18      2.6       3.18       2.6
"unclass"                    2.28      1.8       2.28       1.8
"sum"                        2.26      1.8       2.26       1.8
"attr"                       2.12      1.7       2.12       1.7
"na.omit"                    2.02      1.6      20.00      16.1
"%*%"                        1.74      1.4       1.74       1.4
"^"                          1.56      1.3       1.56       1.3
"bigglm.function"            1.42      1.1     122.52      98.8
"+"                          1.30      1.0       1.30       1.0
"is.na"                      1.28      1.0       1.28       1.0
"model.frame.default"        1.20      1.0      22.68      18.3
">"                          0.84      0.7       0.84       0.7
"strsplit"                   0.62      0.5       0.62       0.5

$by.total
                        total.time total.pct self.time self.pct
"standardGeneric"           122.54      98.8      0.00      0.0
"bigglm.function"           122.52      98.8      1.42      1.1
"bigglm"                    122.52      98.8      0.00      0.0
"bigglm.data.frame"         122.52      98.8      0.00      0.0
"model.matrix.default"       26.76      21.6     25.40     20.5
"model.matrix"               26.76      21.6      0.00      0.0
"model.frame.default"        22.68      18.3      1.20      1.0
"model.frame"                22.68      18.3      0.00      0.0
"["                          22.14      17.9      0.02      0.0
"[.data.frame"               22.12      17.8     14.80     11.9
".Call"                      20.24      16.3     20.24     16.3
"na.omit"                    20.00      16.1      2.02      1.6
"na.omit.data.frame"         17.98      14.5      0.02      0.0
"model.response"             17.44      14.1      0.10      0.1
"as.character"               17.22      13.9     17.22     13.9
"names<-"                    17.22      13.9      0.00      0.0
"<Anonymous>"                15.10      12.2      0.00      0.0
"update.bigqr"               14.38      11.6      5.72      4.6
"update"                     14.38      11.6      0.00      0.0
"data"                       10.26       8.3      0.00      0.0
"-"                           4.36       3.5      4.36      3.5
"anyDuplicated.default"       4.18       3.4      4.18      3.4
"anyDuplicated"               4.18       3.4      0.00      0.0
"|"                           3.98       3.2      3.98      3.2
"*"                           3.44       2.8      3.44      2.8
"/"                           3.18       2.6      3.18      2.6
"lapply"                      3.04       2.5      0.04      0.0
"sapply"                      2.44       2.0      0.00      0.0
"as.list.data.frame"          2.30       1.9      0.02      0.0
"as.list"                     2.30       1.9      0.00      0.0
"unclass"                     2.28       1.8      2.28      1.8
"sum"                         2.26       1.8      2.26      1.8
"attr"                        2.12       1.7      2.12      1.7
"etafun"                      1.88       1.5      0.14      0.1
"%*%"                         1.74       1.4      1.74      1.4
"^"                           1.56       1.3      1.56      1.3
"summaryRprof"                1.48       1.2      0.02      0.0
"+"                           1.30       1.0      1.30      1.0
"is.na"                       1.28       1.0      1.28      1.0
">"                           0.84       0.7      0.84      0.7
"FUN"                         0.70       0.6      0.36      0.3
"strsplit"                    0.62       0.5      0.62      0.5

Меня больше всего удивила функция .Call, которая вызывает Фортран. Может быть, я не понял этого. Кажется, что все вычисления сделаны, как только эта функция используется. Я думал, что это похоже на функцию связывания, которая пошла для извлечения кода на Фортране. Более того, если Fortran выполняет всю работу и все итеративно взвешенные наименьшие квадраты / QR, почему тогда весь оставшийся код занимает так много времени.

1 Ответ

4 голосов
/ 26 ноября 2012

Мое понимание заключается в том, что biglm разбивает данные на куски и запускает их последовательно.

  • Таким образом, вы можете ускорить процесс, оптимизировав размер фрагментов так, чтобы они были настолько большими, насколько позволяет ваша память.
  • Это также просто использование одного из ваших ядер.Это не многопоточный код.Вам нужно сделать немного магии, чтобы заставить это работать.
...