如何减少用于 R 中大数据的 Prado k-means 框架内的内存使用?

How to reduce memory usage within Prado's k-means framework used on big data in R?

我正在尝试验证基于 returns 相关矩阵的 Prado 聚类交易策略的 k-means 框架,如 his paper 中所见,对大量策略使用 R,比如 1000。

他尝试在所有可能的 k 上使用两个 for 循环来寻找最佳 k 和 k-means 的最佳初始化和一些初始化,即 k 从 2 到 N-1,其中 N 是策略数量。

问题是 运行ning k-means 很多次,尤其是有那么多集群时,内存耗尽,而我的计算机 m3.medium 我使用的 AWS 实例都无法做到工作。 (均为 4 GB RAM,但在 AWS 上,后台 RAM 消耗进程较少。)

那么,请问有什么办法可以处理这个内存问题吗?或者至少如何根据使用的策略数量来估计所需的内存量?

我已经尝试了软件包 biganalytics 及其 bigkmeans 功能,但还不够。 我也知道有更高的 RAM AWS 实例,但我想在切换到这样的实例之前确保我的代码是最佳的。 我还尝试限制使用的集群数量,这证实这是主要的内存消耗问题,但我不想坚持这种解决方案(也不会与更好的 AWS 实例结合使用)。

在 AWS 上正确执行的策略的最高数量约为 500。

内存优化的主要部分代码如下:

D <- nrow(dist)
seq.inits <- rep(1:nr.inits,D-2)
seq.centers <- rep(2:(D-1),each = nr.inits)
KM <- mapply(function(x,y){
  set.seed(x+333)
  kmeans(dist, y)
},seq.inits,seq.centers)

dist 是策略的“returns”相关距离矩阵(即列数等于行数,以及其他属性),nr.inits 是初始化次数。两者都是输入变量。之后,使用剪影得分确定最佳聚类,并在需要时重新聚类。

我知道距离矩阵不适合 k-means 的输入,我也知道数据挖掘问题,所以请不要解决这些问题。

我的上述问题是:

  1. 是否可以减少内存使用量,以便我能够在 m3.medium AWS 实例上 运行 1000 个策略?

  2. 是否可以至少根据使用的数字策略估计内存使用情况? (假设我尝试 2:(N-1) 个集群。)

其实第二个问题,最好是优化之后,对我来说比较重要。因为我想尝试比 "just" 1000 多得多的策略。

预先感谢您的回答!

不同时存储所有结果适用于许多问题, 即使你不使用 R。 此外,我认为您没有正确使用 kmeans, 因为它需要你的输入数据, 不是交叉距离矩阵。 同样,您不需要分配所有 seq.centers。 你提到剪影指数, 可以用cluster::silhouette计算,所以:

library(cluster)
data(ruspini) # sample data included in the cluster package

由于您的数据没有改变, 你可以预先计算交叉距离矩阵:

dm <- dist(ruspini)

您想要的工作流程中的一个 "iteration" 是:

km <- kmeans(ruspini, 2) # try 2 clusters
score <- mean(cluster::silhouette(km$cluster, dist = dm)[,3L])

你想为同一个 k 集群随机启动几个:

num_starts <- 2L
scores <- sapply(seq_len(num_starts), function(ignored) {
  km <- kmeans(ruspini, 2)
  mean(cluster::silhouette(km$cluster, dist = dm)[,3L])
})

注意只保存分数, 没有聚类结果。 您还想要 k:

的不同值
max_k <- 3L
num_starts <- 2L
scores <- sapply(2L:max_k, function(k) {
  repetitions <- sapply(seq_len(num_starts), function(ignored) {
    km <- kmeans(ruspini, k)
    mean(cluster::silhouette(km$cluster, dist = dm)[,3L])
  })

  max(repetitions)
})

对于 k 的每个值,我们 return 只有所有重复的最大分数 (同样,通过不存储所有内容来节省 space)。

为了使所有内容都可重现,您在顶部使用 set.seed; 使用一次就足以进行顺序计算。 也许您想利用并行化, 但是你可能需要更多的内存 (很难说有多少, 因为有很多因素在起作用), 并且您需要注意可重复性。 如果您想尝试一下,最终脚本可能如下所示:

library(doParallel)
library(cluster)

data(ruspini)
dm <- dist(ruspini)

max_k <- 3L
num_starts <- 2L

# get random seeds for each execution
RNGkind("L'Ecuyer")
set.seed(333L)
current_seed <- .Random.seed # initialize
seeds <- lapply(2L:max_k, function(ignored) {
  lapply(seq_len(num_starts), function(also_ignored) {
    seed <- current_seed
    current_seed <<- parallel::nextRNGStream(current_seed)
    # return
    seed
  })
})

workers <- makeCluster(detectCores())
registerDoParallel(workers)

scores <- foreach(k = 2L:max_k, k_seeds = seeds, .combine = c, .packages = "cluster") %dopar% {
  repetitions <- sapply(seq_len(num_starts), function(i) {
    set.seed(k_seeds[[i]])
    km <- kmeans(ruspini, k)
    mean(cluster::silhouette(km$cluster, dist = dm)[,3L])
  })

  max(repetitions)
}

stopCluster(workers); registerDoSEQ(); rm(workers)

names(scores) <- paste0("k_", 2L:max_k)