R中proxy::dist调用自定义DTW函数速度过慢,求优化方案
我完全懂你遇到的这个痛点——当数据规模上去之后,proxy::dist搭配自定义customDTW的速度暴跌,这确实是因为proxy::dist处理自定义方法时,默认是逐对调用你的函数,每一次调用都带额外的函数开销,而且没用到dtw包本身的批量优化能力。下面咱们一步步解决这个问题:
为什么当前实现速度拉胯?
你的customDTW每次只处理一对时间序列,而proxy::dist会对test和train的每一组序列组合都调用一次这个函数。当规模从60×70涨到60×700时,调用次数从4200次暴增到42000次,额外的函数调用开销被放大10倍,再加上dtw本身的计算成本,最终导致耗时呈指数级上升。而你不用proxy::dist的实现应该是用了更高效的批量处理逻辑,所以速度差这么多。
具体优化方案
1. 绕开proxy的逐对调用,直接用dtw批量计算
与其让proxy拆分数据对,不如自己批量处理所有组合,用R的向量化操作减少函数调用次数:
library(dtw) fastDTWMatrix <- function(test, train, window.size = 20) { apply(test, 1, function(ts1) { sapply(seq_len(nrow(train)), function(i) { dtw(ts1, train[i, ], dist.method = "Euclidean", window.type = "sakoechiba", window.size = window.size)$distance }) }) %>% t() } # 调用示例 set.seed(1) test <- matrix(runif(4200), 60, 70) train <- matrix(runif(4200), 60, 70) dMatrix <- fastDTWMatrix(test, train)
这个方法减少了函数调用的层级,sapply的循环效率比proxy的内部循环更高,60×700规模的速度应该能追上你不用proxy的实现。
2. 开启proxy的并行计算
proxy::dist支持并行计算,只需要注册并行后端后开启parallel参数即可:
library(proxy) library(dtw) library(doParallel) # 注册并行集群(留一个核心给系统) cl <- makeCluster(detectCores() - 1) registerDoParallel(cl) # 自定义函数保持简洁 customDTW <- function(ts1, ts2){ dtw(ts1, ts2, dist.method = "Euclidean", window.type = "sakoechiba", window.size = 20)$distance } # 调用时开启并行 dMatrix <- proxy::dist(x = test, y = train, method = customDTW, by_rows = T, auto_convert_data_frames = T, parallel = TRUE) # 用完记得关闭集群 stopCluster(cl)
并行计算能把任务分摊到多个CPU核心,大规模数据下耗时会明显降低。
3. 优化自定义函数的额外开销
你的原函数每次都要重复设置dtw参数,还返回整个dtw对象再提取距离,可以简化成直接返回距离,减少不必要的内存操作:
customDTW <- function(ts1, ts2){ # 直接返回计算结果,避免存储整个dtw对象 dtw(ts1, ts2, dist.method = "Euclidean", window.type = "sakoechiba", window.size = 20)$distance }
虽然单次优化幅度不大,但几万次调用下来积少成多。
4. 用专门的时间序列距离包
如果你的核心需求是计算时间序列距离矩阵,推荐用dtwclust包——它专门优化了DTW的批量计算,底层用C++实现,速度比自定义函数快很多,还和proxy兼容:
library(dtwclust) # 直接用dtwclust的高效DTW实现 dMatrix <- proxy::dist(test, train, method = "dtw_basic", by_rows = TRUE, window.size = 20)
优化后的预期效果
用上述方案优化后,60×700规模的计算时间应该能大幅降低,接近甚至超过你不用proxy的实现速度,尤其是开启并行或用dtwclust的情况。
内容的提问来源于stack exchange,提问作者Azim

