大矩阵运行glmnet()

20

我在使用glmnet套索算法处理一组宽数据集时遇到了问题。我的数据有N=50,但p > 49000,全部是因子变量。所以我必须创建一个模型矩阵才能运行glmnet,但是当我调用model.matrix(formula,data),其中formula = Class ~ .时,我遇到了内存不足的问题。

以下是一个生成数据集的示例:

data <- matrix(rep(0,50*49000), nrow=50)
for(i in 1:50) {
x = rep(letters[2:8], 7000)
y = sample(x=1:49000, size=49000)
data[i,] <- x[y]
}

data <- as.data.frame(data)
x = c(rep('A', 20), rep('B', 15), rep('C', 15))
y = sample(x=1:50, size=50)
class = x[y]
data <- cbind(data, class)

之后,我尝试创建一个模型矩阵以输入到glmnet中。

  formula <- as.formula(class ~ .)
  X = model.matrix(formula, data)
  model <- cv.glmnet(X, class, standardize=FALSE, family='multinomial', alpha=1, nfolds=10)

在最后一步(X = model.matrix ...)时,我内存不足。我该怎么办?


1
是时候增加更多的内存了。(或者重新启动,只运行最少的应用程序和数据。)这只是一个24MB大小的对象。 - IRTFM
我只有50个样本。我无法相信没有解决办法! - Flavio Barros
我没有说没有解决方案。 - IRTFM
2个回答

26

我问了Trevor Hastie教授并得到了以下建议:

"你好Flavio

model.matrix会使你的程序崩溃。 你将拥有49K个因子,而model matrix试图将它们表示为对比度,这将是6列矩阵,所以49*6约等于300K列。 为什么不制作二进制虚拟变量(每个因子7个),并直接构造它,而不使用model.matrix?您可以通过sparseMatrix存储此数据,节省1/7的空间(glmnet接受稀疏矩阵格式)"

我正是这样做的,效果非常好。我认为这对其他人也有用。

一篇包含代码的文章,源自这个问题:http://www.rmining.net/2014/02/25/genetic-data-large-matrices-glmnet/

为了避免链接失效,我在这里发布部分内容:

公式方法的问题在于,通常来说,基因组数据的列数多于行数。我处理的数据在这种情况下有40,000列,只有73个观察值。为了创建一个小的测试数据集,请运行以下代码:

for(i in 1:50) {
    x = rep(letters[2:8], 7000)
    y = sample(x=1:49000, size=49000)
    data[i,] <- x[y]
}

data <- as.data.frame(data)
x <- c(rep('A', 20), rep('B', 15), rep('C', 15))
y <- sample(x=1:50, size=50)
class = x[y]
data <- cbind(data, class)

因此,我们将使用这个数据集尝试用glmnet()拟合模型:

formula <- as.formula(class ~ .)
X <- model.matrix(formula, data)
model <- cv.glmnet(X, class, standardize=FALSE, family='multinomial', alpha=1, nfolds=10)

如果你的电脑没有比我的RAM更大,你可能会出现内存泄漏并在R中崩溃。解决方案是什么?我的第一个想法是尝试使用sparse.model.matrix(),它使用相同的公式创建一个稀疏矩阵模型。不幸的是,这种方法行不通,因为即使使用稀疏矩阵,最终模型仍然太大了!有趣的是,这个数据集只占用了24MB的RAM,但当你使用model.matrix时,结果是一个超过1GB的数组。

我找到的解决方案是手动构建矩阵。为此,我们逐列对虚拟变量进行编码,并将结果存储在稀疏矩阵中。然后,我们将使用该矩阵作为输入来运行模型,看看是否会出现内存泄漏:

## Creates a matrix using the first column
X <- sparse.model.matrix(~data[,1]-1)

## Check if the column have more then one level
for (i in 2:ncol(data)) {

## In the case of more then one level apply dummy coding 
if (nlevels(data[,i])>1) {
    coluna <- sparse.model.matrix(~data[,i]-1)
    X <- cBind(X, coluna)
}
## Transform fator to numeric
else {
   coluna <- as.numeric(as.factor(data[,i]))
   X <- cBind(X, coluna)
}

注意:请留意我们是如何使用稀疏矩阵的,需要安装Matrix包。同时,请注意使用cBind()连接列,而不是cbind()。

生成的矩阵大小较小,在我的测试中不到70 Mb。幸运的是,glmnet()支持稀疏矩阵,你可以运行模型:

mod.lasso <- cv.glmnet(X, class, standardize=FALSE, family='multinomial', alpha=1, nfolds=10)

因此,您可以使用此类型的数据创建模型,而无需耗尽内存,并且无需使用如bigmemoryff这样的R包来处理大型数据集。


2
你也可以尝试使用 Matrix::sparse.model.matrixMatrixModels::modelMatrix(*,sparse=TRUE) - Ben Bolker
1
那不起作用!!!请使用示例自行尝试。使用sparse.model.matrix创建的对象要大得多。在发布这个问题之前,我已经尝试过了。 - Flavio Barros
@FlavioBarros - 也许可以添加一些你使用的代码示例?我认为这会很有帮助。 - R_User
这是最好的答案吗?看起来不太方便。为了避免链接失效,最好将代码添加到答案中。 - marbel
1
@MartínBel,问题已经解决。感谢您的建议。 - Flavio Barros

6
对于可能感兴趣的人,我开发了一个名为biglasso的R包,它可以在大数据情况下拟合Lasso类型模型。它使用基于bigmemory包的内存映射(大)设计矩阵,并且可以无缝地处理大于RAM的数据。此外,它通过使用新提出的特征筛选规则以及更好的实现方式,比glmnet更具计算和内存效率。请查看GitHub页面获取详细信息,并随时提供任何建议或评论。

当你在谷歌上搜索 r glmnet faster 时,这个线程就会出现。所以,即使对 OP 没有好处,这也是一个提供此信息的好地方。 - eric_kernfeld
谢谢你制作这个。它很棒。 - Espen Riskedal
但是biglasso不支持AUC评估指标。 - Heaven
@Heaven,biglasso现在支持AUC,自1.4-1版本发布以来。https://github.com/YaohuiZeng/biglasso/blob/master/NEWS.md#biglasso-14-1 - Espen Riskedal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接