使用 numpy 或 cython 进行高效的成对 DTW 计算

Posted

技术标签:

【中文标题】使用 numpy 或 cython 进行高效的成对 DTW 计算【英文标题】:Efficient pairwise DTW calculation using numpy or cython 【发布时间】:2017-12-13 04:10:24 【问题描述】:

我正在尝试计算 numpy 数组中包含的多个时间序列之间的成对距离。请看下面的代码

print(type(sales))
print(sales.shape)

<class 'numpy.ndarray'>
(687, 157)

所以,sales 包含 687 个长度为 157 的时间序列。使用 pdist 计算时间序列之间的 DTW 距离。

import fastdtw
import scipy.spatial.distance as sd

def my_fastdtw(sales1, sales2):
    return fastdtw.fastdtw(sales1,sales2)[0]

distance_matrix = sd.pdist(sales, my_fastdtw)

---编辑:尝试不使用pdist()-----

distance_matrix = []
m = len(sales)    
for i in range(0, m - 1):
    for j in range(i + 1, m):
        distance_matrix.append(fastdtw.fastdtw(sales[i], sales[j]))

---编辑:并行化内部for循环-----

from joblib import Parallel, delayed
import multiprocessing
import fastdtw

num_cores = multiprocessing.cpu_count() - 1
N = 687

def my_fastdtw(sales1, sales2):
    return fastdtw.fastdtw(sales1,sales2)[0]

results = [[] for i in range(N)]
for i in range(0, N- 1):
    results[i] = Parallel(n_jobs=num_cores)(delayed(my_fastdtw) (sales[i],sales[j])  for j in range(i + 1, N) )

所有方法都很慢。并行方法大约需要 12 分钟。有人可以建议一种有效的方法吗?

---编辑:按照下面答案中提到的步骤---

lib 文件夹如下所示:

VirtualBox:~/anaconda3/lib/python3.6/site-packages/fastdtw-0.3.2-py3.6- linux-x86_64.egg/fastdtw$ ls
_fastdtw.cpython-36m-x86_64-linux-gnu.so  fastdtw.py   __pycache__
_fastdtw.py                               __init__.py

所以,那里有一个 cython 版本的 fastdtw。安装时,我没有收到任何错误。即使是现在,当我在程序执行过程中按下CTRL-C时,我可以看到正在使用纯python版本(fastdtw.py):

/home/vishal/anaconda3/lib/python3.6/site-packages/fastdtw/fastdtw.py in fastdtw(x, y, radius, dist)

/home/vishal/anaconda3/lib/python3.6/site-packages/fastdtw/fastdtw.py in __fastdtw(x, y, radius, dist)

代码仍然像以前一样缓慢。

【问题讨论】:

阅读pdist 所说的关于提供自己的函数的内容。注意它调用了多少次。 fastdtw 产生什么? dm 中的内容是什么?我认为pdist 期望距离函数有一个简单的数字。 @hpaulj,你是对的,每次调用fastdtw都会产生一个float,这是pdist需要的距离,它还返回一个路径。请参阅我更新的帖子。 看起来pdist 是在给定 Python 函数时进行相同类型的迭代。只有在使用它自己的编译指标之一时它才会更快。任何速度提升都必须来自fastdtw 端。 【参考方案1】:

说实话,fastdtw 一点也不快

from cdtw import pydtw
from dtaidistance import dtw
from fastdtw import fastdtw
from scipy.spatial.distance import euclidean
s1=np.array([1,2,3,4],dtype=np.double)
s2=np.array([4,3,2,1],dtype=np.double)

%timeit dtw.distance_fast(s1, s2)
4.1 µs ± 28.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit d2 = pydtw.dtw(s1,s2,pydtw.Settings(step = 'p0sym', window = 'palival', param = 2.0, norm = False, compute_path = True)).get_dist()
45.6 µs ± 3.39 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit d3,_=fastdtw(s1, s2, dist=euclidean)
901 µs ± 9.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

fastdtwdtaidistance 慢 219 倍,比 cdtw 慢 20 倍

考虑改变。这里是dtaidistancegit:

https://github.com/wannesm/dtaidistance

要安装,只需:

pip install dtaidistance

【讨论】:

对于较大的时间序列(在我的例子中包含 3000 个样本),情况看起来有点不同:* dtw.distance(x,y): 21.120484339067534 * dtw.distance_fast(x,y): 0.35772456875758607 * fastdtw(x,y): 1.0159217517881522 * pydtw.dtw(x,y): 0.06623724053156366 pydtw.dtw 的结果非常有趣...您是否使用相同的设置并多次运行它?但是,我想对于较大的数组,平均处理时间在它们之间的比例差距会更小。 @哈格巴德 在我的机器上,不同的运行会产生不同的结果。我的确切设置可以在in this question找到。【参考方案2】:

TL;DR

您的fastdtw 安装了快速的 cpp 版本,然后默默地退回到了一个很慢的纯 python 版本。

您需要修复fastdtw-package 的安装。


整个计算是在fastdtw 中完成的,所以你不能真正从外部加速它。而且并行化和 python 并不是一件容易的事(还没有?)。

fastdtw 文档说它需要大约 O(n) 操作来进行比较,因此对于您的整个测试集,它需要大约数量级的 10^9 操作,这应该在大约几秒钟内完成,如果例如,用 C 编程。你看到的性能远不及它。

如果我们查看code of fastdtw,我们会看到有两个版本:通过 cython 快速导入的 cython/cpp-version 和慢速回退的 pure-python-version。如果没有预设快速版本,则默认使用慢速 python 版本。

所以运行你的计算,用Ctr+C 打断它,你会看到,你在 python 代码的某个地方。你也可以去你的lib文件夹看看,里面只有纯python版本。

所以您安装快速fastdtw 版本失败。实际上,我认为wheel-package 是拙劣的,至少对于我的版本,只有纯python 代码存在。

怎么办?

    获取源代码,例如通过git clone https://github.com/slaypni/fastdtw 进入fstdtw文件夹并运行python setup.py build 注意错误。我的是

致命错误:numpy/npy_math.h:没有这样的文件或目录

    修复它。

对我来说,解决方法是更改​​setup.py 中的以下几行:

import numpy # THIS ADDED
extensions = [Extension(
        'fastdtw._fastdtw',
        [os.path.join('fastdtw', '_fastdtw' + ext)],
        language="c++",
        include_dirs=[numpy.get_include()], # AND ADDED numpy.get_include()
        libraries=["stdc++"]
    )]
    重复 3.+4。直到成功 运行python setup.py install

现在您的程序应该快 100 倍左右。 `

【讨论】:

@user1274878 这些家伙昨天解决了这个问题,并且使用当前版本(3.0.2)它对我来说开箱即用(我不使用 anaconda) @user1274878 很难说,您的问题可能是什么,我无法远程修复您的设置。转到.../fastdtw/__init__.py 并将其代码替换为print("Loading fastdtw") from ._fastdtw import fastdtw, dtw。您现在应该可以看到,为什么导入快速版本会失败。如果您没有看到“正在加载 fastdtw”,那么您正在使用另一个版本。解决您看到的问题。

以上是关于使用 numpy 或 cython 进行高效的成对 DTW 计算的主要内容,如果未能解决你的问题,请参考以下文章

NumPy之计算两个矩阵的成对平方欧氏距离

NumPy之计算两个矩阵的成对平方欧氏距离

评估TensorFlow中多维输入之间的成对欧氏距离

几种分布的成对图形比较

非常大的数据集中的成对距离

R语言Bonferroni校正的成对t检验进行事后检验(post hoc)实战:单因素方差分析告诉我们并不是所有的群体手段的效果是均等的,确切地找出哪些组彼此不同使用Bonferroni校正检验