为什么在 proxy::dist 中使用自定义邻近函数会使它在 R 中变得如此缓慢?

Why using a custom proximity function in proxy::dist makes it so slow in R?

我正在尝试将 proxy::dist 函数与自定义距离矩阵一起使用,但我现在的速度很慢。

这是我如何调用自定义函数的可重现示例:

set.seed(1)
test <- matrix(runif(4200), 60, 70)
train <- matrix(runif(4200), 60, 70)
dMatrix <- proxy::dist(x = test, y = train, method = customDTW,
                     by_rows = T, 
                     auto_convert_data_frames = T)

它应该计算 test 矩阵中每个时间序列与 train 矩阵中所有时间序列(每一行都是一个时间序列)之间的距离。

我的自定义函数是:

customDTW <- function(ts1, ts2){

  d <- dtw(ts1, ts2,
      dist.method = "Euclidean",
      window.type = "sakoechiba",
      window.size = 20
  )
  return(d$distance)
}

问题是,比起我用method="DTW",甚至比起我自己计算距离矩阵的情况,这都非常慢,而且随着时间序列的长度或它们的数量在增长,它呈指数级变慢。当然这源于嵌套循环,但我对效果的规模感到惊讶。我看不到它一定还有其他原因。

我的问题是我还可以如何使用proxy::dist?

实现我的customDTW以使其更快

这是我对执行时间的小实验:

60X7 的执行时间(使用 proxy::dist + customDTW

user  system elapsed 
2.852   0.012   2.867

60X70 的执行时间(使用 proxy::dist + customDTW

user  system elapsed 
5.384   0.000   5.382 

60X700 的执行时间(使用 proxy::dist + customDTW

user  system elapsed 
509.088  18.652 529.115

60X700 的执行时间(没有 使用proxy::dist

user  system elapsed 
26.696   0.004  26.753

DTW 本质上很慢 您是否考虑过尝试使用 dtwclust(dtw 的并行实现)

https://github.com/asardaes/dtwclust

https://cran.r-project.org/web/packages/dtwclust/vignettes/dtwclust.pdf

这是我发现的,似乎提高了速度,但仍然没有我预期的那么快。 (任何其他想法仍然非常受欢迎。)

诀窍是使用 proxy(即 Registry of proximities here)注册自定义距离函数,这样您就可以像内置距离测量。所以,首先:

proxy::pr_DB$set_entry(FUN = customDTW, names=c("customDTW"),
                         loop = TRUE, type = "metric", distance = TRUE)

现在您可以使用它,就像它已经在 proxy 包中一样。

dMatrix <- proxy::dist(x = test, y = train, method = "customDTW",
                         by_rows = T,
                         auto_convert_data_frames = T)

注意:如果要使用这种方法,那么customDTW方法必须处理一对时间序列,而不是全部。所以 customDTW 看起来像这样:

customDTW2 <- function(ts1, ts2){

  d <- dtw(ts1, ts2,
      dist.method = "Euclidean",
      window.type = "sakoechiba",
      window.size = 20
  )
  return(d$distance)
}

有关更多信息,请参阅 ?pr_DB

R 是一种解释型语言,在底层是用 C 实现的。据我了解,代理包是在 C 中使用 R 的解释能力多次调用 R 代码,但仍然可以'避免解释的开销,所以几乎所有 "pure" R 实现都会变慢。

用proxy注册函数时指定loop=TRUE意味着会发生上述情况(proxy会多次解释R代码来填充距离矩阵)。如果你真的想加快速度,你需要在 C/C++ 中实现填充本身,并使用 loop=FALSE 向代理注册函数;这就是 dtwclust 所做的(除其他外)。

如果您想测试自己的自定义 C/C++ 函数,即使您不想使用并行化,也可能需要查看 parallelDist 包。