大家好我有以下问题我在一些dimmentions(4-6个群集)中有一个群集中心和一个非常大的数据集,我需要将每一行分配给最近的群集。所以这不是一个真正的距离问题,而是性能问题我的代码如下:
distances <- matrix(NA, nrow = nrow(ClusterCenters), ncol = nrow(data))
calcData <- data[, colnames(ClusterCenters), drop=FALSE]
for(i in 1:nrow(ClusterCenters)) {
distances[i,] <- (rowSums((matrix(unlist(apply(calcData, 1, function(x) {x - ClusterCenters[i,]})), ncol = ncol(calcData), byrow = TRUE))^2))^0.5
}
ClusterMemberships <- vector(mode="numeric", length=nrow(calcData))
for(i in 1: nrow(calcData)) {
ClusterMemberships[i] <- which.min(distances[,i])
}
return(ClusterMemberships)
有没有办法加快速度?我在Windows服务器上工作。
答案 0 :(得分:2)
对于50 x 100万数据行矩阵匹配6个簇,每个簇50个值,我得到的结果大约3秒:
vals <- 50
clusts <- 6
clusters <- matrix(runif(vals * clusts), nrow=clusts)
data.count <- 1e6 # large number
data <- matrix(runif(data.count * vals), nrow=data.count)
system.time({
dists <- apply(clusters, 1, function(x) rowSums((data - x) ^ 2) ^ .5)
min.dist <- max.col(-dists, ties.method="first")
})
# user system elapsed
# 2.96 0.47 3.49
关键是要确保我们限制R函数调用的数量,因为这些调用变得昂贵。注意我apply
如何通过群集(其中只有六个)而不是数据行,其中有一百万个。然后我使用循环来计算每个集群相对于整个集合的距离(与您的数据相比,注释data
被转置,集群中的项目数量与行数一样多;这对于回收工作是必要的)
感谢@ user20650提供max.col
篇。
答案 1 :(得分:2)
有几种方法可以优化R的性能,例如使用BrodieG进行矢量化。
或者,您可以利用现有计算模式的性能优势,例如矩阵乘法,排序。进一步阅读本书中的模式
[Michael McCool等,结构化并行编程 - 高效计算模式] 。
对于来自多核CPU或GPU的这些现有模式,我们还可以从并行库中获得进一步的性能优势。在这种情况下,我们可以通过矩阵运算或KNN来表示计算。
<强> 1。纹强>
通过system.time为大数据集输入(1e6)分析这段代码,我们可以看到第一个循环,它计算两个向量之间的距离,占总计算时间的95%。所以,我们的优化将从这里开始。
# Original code
vals <- 50
clusts <- 6
ClusterCenters <- matrix(runif(vals * clusts), nrow=clusts)
data.count <- 1e6 # large number
calcData <- matrix(runif(data.count * vals), nrow=data.count)
system.time({
for(i in 1:nrow(ClusterCenters)) {
dists[i,] <- (rowSums((matrix(unlist(apply(calcData, 1, function(x) {x ClusterCenters[i,]})), ncol = ncol(calcData), byrow = TRUE))^2))^0.5
}
})
user system elapsed
71.62 1.13 73.13
system.time({
for(i in 1: nrow(calcData)) {
ClusterMemberships[i] <- which.min(dists[,i])
}
})
user system elapsed
5.29 0.00 5.31
<强> 2。矢量化强>
如@BrodieG所示,矢量化是一种非常有用的加速R代码的方法,尤其适用于'循环'。顺便说一下,我已经在他的解决方案中修改了一点,以获得如下所示的正确结果,并且它可以比原始代码获得 3-5X 加速。
#Vectorization: From BrodieG
dists1 <-matrix(NA, nrow = nrow(ClusterCenters), ncol = nrow(calcData)) system.time({
dists1 <- apply(ClusterCenters, 1, function(x) rowSums(sweep(calcData, 2,x, '-') ^ 2) ^ .5)
min.dist.vec <- max.col(-dists1, ties.method="first")
})
user system elapsed
16.13 1.42 17.61
all.equal(ClusterMemberships, min.dist.vec)
[1] TRUE
第3。矩阵模式
然后,让我们回顾第一个循环,它通过汇总(calcData [i,] - ClusterCenters [j,])^ 2的列来计算距离。
因此,我们可以通过扩展以下公式将此操作转移到矩阵:
calcData [i,] ^ 2 - 2 * calcData [i,] * ClusterCenters [j,] + ClusterCenters [J,] ^ 2
因此,对于第一和第三部分,我们可以进行简单的矩阵扫描乘法,例如
calcData * calcData
对于第二项,我们需要矩阵传递的技巧技巧,然后它变为矩阵乘法
ClusterCenters%*%t(calcData)
最后,整个代码的矩阵运算如下:
# Pattern Representation 1: Matrix
dists2 <-matrix(NA, nrow = nrow(ClusterCenters), ncol = nrow(calcData))
system.time({
data2 <- rowSums(calcData*calcData)
clusters2 <- rowSums(ClusterCenters*ClusterCenters)
# NVIDIA GPU: nvBLAS can speedup this step
# Futher Info on ParallelR.com
ClustersXdata <- calcData %*% t(ClusterCenters)
# compute distance
dists2 <- sweep(data2 - 2 * ClustersXdata, 2, clusters2, '+') ^0.5
min.dist.matrix <- max.col(-dists2, ties.method="first")
})
user system elapsed
1.17 0.09 1.28
all.equal(ClusterMemberships, min.dist.matrix)
[1] TRUE
现在,我们可以看到所有这三个部分都可以通过矩阵运算来完成。通过这种方法,计算时间几乎是从10 ^ 3到10 ^ 7的线性,并且它比1e6 calcData集的原始代码快 50X 。
<强> 4。 KNN 强>
在这种情况下,计算可以被视为在群集数据集中找到第一个最近邻居,因此它是一种k = 1的最简单的KNN。而且我们可以很容易地使用C语言编写的类包中的knn函数。同时,它也会非常高效。示例代码如下:
# Pattern Representation 2: KNN
library("class")
system.time(
min.dist.knn <- knn(ClusterCenters, calcData, cl = 1:nrow(ClusterCenters), k = 1)
)
user system elapsed
1.21 0.12 1.35
all.equal(ClusterMemberships, as.integer(min.dist.knn))
[1] TRUE
KNN的运行时与1e6的矩阵运算代码类似,但如果我们将更多的大数据应用于这两种算法,我们可以看到矩阵算法仍然胜利且矩阵算法 2X 强>快于KNN(15.9 .vs.29.1)。
最后,在这篇文章中我只展示了几个关于性能调优的想法,我们可以继续微调这些代码,包括体系结构指定的优化,并使用c / c ++来重写它。即使在多核CPU和NVIDIA GPU上并行化矩阵运算和KNN,您也可以参考ParallelR