有几种方法可以优化R的性能,例如使用BrodieG显示矢量化。
或者,您可以利用现有计算模式的性能优势,例如矩阵乘法,排序。
[Michael McCool等,Structured Parallel Programming - Patterns for Efficient Computation]。
而且我们还可以从多核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.矢量
矢量是加速R代码里面尤其关于“环”,如图@BrodieG绝对有用的方法。顺便说一句,我已经修改了一些在他的解决方案,以获得正确的结果如下,它可以获得约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%*%吨的矩阵乘法(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集合的原始代码更快。
4. KNN
在这种情况下,计算可被视为找到在簇数据集第一最近邻所以这是一个种简单的KNN,其中k = 1。而且我们可以很容易地使用由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矩阵运算代码相似,但如果我们申请更多的大数据这两种算法,我们可以看到矩阵算法仍然赢了,矩阵算法是快于KNN(15.9 .vs。29.1)的2X。
终于,在这篇文章中,我只是展示了一些性能调优的想法,我们可以继续微调这些代码,包括架构指定的优化和使用c/C++来重写它。甚至可以在多核CPU和NVIDIA GPU上并行化矩阵运算和KNN,您可以参考ParallelR
如果您可以解释您未经检查的答案如何不符合您的要求,这将非常有用。 – BrodieG
你能提供一个可重复的例子吗?也许是你的数据的一个小样本? –