【问题标题】:Pairwise weighted distance vectorization成对加权距离矢量化
【发布时间】:2018-06-13 10:59:56
【问题描述】:

以下高效且矢量化的 Matlab 代码使用权重向量 WTS(每个维度 1 个权重;所有点的权重相同)计算 2 组点 A 和 B 之间的加权欧几里得距离:

    WTS = sqrt(WTS); 

    % modify A and B against weight values
    A = WTS(ones(1,size(A,1)),:).*A;
    B = WTS(ones(1,size(B,1)),:).*B; 

    % calculate distance
    AA = sum(A.*A,2);  
    BB = sum(B.*B,2)'; 
    D = sqrt(AA(:,ones(1,size(B,1))) + BB(ones(1,size(A,1)),:) - 2*A*B'); 

(来源:https://github.com/nolanbconaway/pairdist/blob/master/pairdist.m

我的问题是: 是否存在用于类似计算的有效矢量化形式(Matlab、R 或 Julia 都可以),不同之处在于 WTS 是一组具有相同的权重向量大小为 A?换句话说,不是 1 个权重向量,我需要为 A 中的每个点 1 个权重向量

这个答案似乎可以满足我的需要,但它是用 Python 编写的,我不确定如何将其转换为 Matlab/R/Julia:https://stackoverflow.com/a/19285289/834518

另外,不是Efficiently calculating weighted distance in MATLAB 的重复,因为该问题涉及单个权重向量的情况,我明确要求 N 个权重向量的情况。

编辑:示例应用:RBF 网络和高斯混合模型,您(可以)为每个神经元/组件拥有 1 个权重向量。对于这类问题,有效的解决方案是必不可少的。

【问题讨论】:

  • 您是否自己尝试过任何可能使您更接近解决方案的更改?你发现了什么?
  • @rahnema1 不是重复的,只有 1 个权重向量就是这种情况。
  • @MattB。对于 1 个权重向量的情况确实很简单,但是当权重向量与 A 中的点一样多时,我根本看不到如何做同样的事情,至少效率一样高。我尝试了一些不那么科学的临时修改,例如仅将 A 乘以 WTS 或将其包含在 2AB 项中,获得了灾难性的结果。换句话说,我在问之前尝试了很多东西。
  • @MattB。可能需要计算一个具有所有成对差异(而不是距离)的 3D 矩阵,然后我可以将每个矩阵与它对应的权重向量相乘(我认为这就是他在 Python 答案中所做的),但同样,我'不确定如何有效地做到这一点。
  • @DanGetz 正如我在 Distance.jl 解决方案下评论的那样,它要求 AB 大小相同,W 是一个向量,但 OP 希望 A 和 @ 987654329@ 是不同大小的矩阵,W 是一个与A 大小相同的矩阵。你能出示你的代码吗?

标签: r matlab julia vectorization euclidean-distance


【解决方案1】:

这是MATLAB中的矢量化版本(R2016b及更高版本):

W2 = 1./W.^2;
D = sqrt(sum((A./W).^2 ,2) - 2 * (A .* W2) * B.' +W2 * (B.^2).');

在 R2016b 之前的版本中你可以使用这个:

W2 = 1./W.^2;
D = sqrt(bsxfun(@plus,sum((A./W).^2 ,2) , -2 * (A .* W2) * B.' +W2 * (B.^2).'));

MATLAB 到 julia 的翻译:

W2 = 1./W.^2;
z=sqrt.(broadcast(+,sum((A./W).^2 ,2) , -2 * (A .* W2) * B.' .+W2 * (B.^2).'));

在这里,我提出的方法Vectorization 与@DanGetz 提供的Loop 方法进行了比较。其他解决方案在此不适用。

我们可以看到,对于小于 128 的维度,循环版本比矢量化版本更快。随着维度数量的增加,循环版本的性能会变差。

以下代码用于生成图形:

function pdist_vectorized (A::Matrix, B::Matrix, W::Matrix)
    W2 = 1./W.^2;
    return sqrt.(broadcast(+,sum((A./W).^2 ,2) , -2 * (A .* W2) * B.' .+W2 * (B.^2).'));
end

result = zeros(10,2);
for i = 1:10
    A = rand( 3000, 2^i);
    B = rand( 2000, 2^i);
    W = ones(size(A));
    result[i,1]=(@timed pdist_1alloc(A,B,W))[2];
    result[i,2]=(@timed pdist_vectorized(A,B,W))[2];
end

using Plots
pyplot()
plot(2.^(1:10), result, title="Pairwise Weighted Distance",
    label=["Loop" "Vectorization"], lw=3,
    xlabel = "Dimension", ylabel = "Time Elapsed(seconds)")

【讨论】:

  • 谢谢,@rahnema1。请看一下我在问题中的新评论,也许你有什么要补充的。
【解决方案2】:

另一个优化分配结果矩阵的版本,仅此而已:

function pdist_1alloc(A::Matrix, B::Matrix, W::Matrix)
    LA, LD = size(A) ; LB = size(B,1)
    res = zeros(LB, LA)
    indA = 0 ; indB = 0 ; indres = 0
    @inbounds for i=1:LD
        for j=1:LA
            a = A[indA+j] ; w = W[indA+j] ; a2w = a^2*w ; awtmp = -2.0*a*w
            for k=1:LB
                indres += 1
                b = B[indB+k] ; b2w = b^2*w
                res[indres] += a2w+awtmp*b+b2w
            end
        end
        indA += LA ; indB += LB ; indres = 0
    end
    res .= sqrt.(res)
    return res
end

它比@rahnema1 的版本快大约 2 倍,并且使用相同的技巧,但不那么可读。此外,我很抱歉首先误解了问题的确切设置(并建议在这里不直接适用的 Distance.jl)。

【讨论】:

  • 谢谢,Dan,不要担心误会。无论如何,您是否将函数的结果与其他版本进行了比较?我认为它缺少一些东西,因为(1,1)和(3,3)之间的加权距离(2,2)给我4,而它应该是sqrt(32)。 ( sqrt((2*2)^2 + (2*2)^2) )
  • @rahnema1 也许我们对“加权距离”仍有不同的定义。 rahnema1's 给了我这个距离的 sqrt(2)。我的版本是 4.0。 (考虑 1 向量 A 和 1 向量 B - 给出单个值)。根据我的理解,每个维度的平方差乘以权重,即(3-1)^2*2+(3-1)^2*2 = 16,sqrt-ing得到4.0。正确答案应该是什么? pdist([1.0 1.0], [3.0 3.0], [2.0 2.0])
  • 定义来自python 帖子。我认为你应该转置结果。
  • @rahnema1 转置是正确的(并且无关紧要),但我们的版本给出的值是不同的。对于pdist([1.0 1.0], [3.0 3.0], [2.0 2.0]),一个是sqrt(2.0),另一个是4.0
  • 谢谢,丹,这是有道理的。这两个定义对我来说都很好,因为无论如何这些权重都会被学习。顺便说一句,我在 JuliaBox 上使用 A 2000x1000 和 B 3000x1000 进行了新测试,使用 Float32(左)和 Float64(右):imgur.com/a/S57YU(第 4 行是 rahnema1,第 5 行是你的)。
【解决方案3】:

作为为未来读者提供的附加信息,Distances.jl 包具有您能想到的大多数距离的有效实现。作为一般建议,如果一个操作在科学计算中非常常见,那么会有一个包很好地实现它。

using Distances

D = pairwise(WeightedEuclidean(weights), A, B)

【讨论】:

  • 您的解决方案有两个问题: 1. A 和 B 应该是相同大小的矩阵。 2. 权重应该是一个向量。
  • 不,A 和 B 的大小不必相同。关于权重,自己算出来不是很简单吗?我的回答的重点是应该使用一个包,而不是重新发明轮子。其余的只是细节。
  • 矩阵 A 和 B(或 X 和 Y)应包含点的坐标作为列。权重向量应该包含空间每个维度的权重。如果您在 R^2 中,则这些是 2 个权重。这是有效计算成对加权欧几里得的最小表示。任何偏离这种表示的东西都可以证明是次优的。
  • 不,次优,因为表示此任务的任何额外位数都是不必要的。此外,像您键入的解决方案很难阅读,并且可能会产生临时问题,以防您忘记键入单个点“。”在里面。
  • @juliohm “细节”正是我的问题。此外,使用更多的权重并不是次优的,它们完全是不同的问题。在 RBF 网络或高斯混合中只有 1 个权重向量是没有意义的。
【解决方案4】:

在 Julia 中,您不必对其进行矢量化以提高效率,只需编写循环,它无论如何都会比这些矢量化形式更快,因为它可以融合并摆脱临时性。 Here's a pretty efficient implementation of pairwise applies in Julia 您可以使用。它有所有的花里胡哨,但如果你愿意,你可以把它配对。

请注意,向量化不一定“快”,它只是比在 R/Python/MATLAB 中循环更快,因为它只对用较低级别语言 (C/C++) 编写的优化内核执行单个函数调用,即实际上循环。但是将向量化函数放在一起通常会有很多临时分配,因为每个向量化函数都返回数组。因此,如果你真的需要效率,你应该避免一般的向量化,并用一种​​允许低成本函数调用/循环的语言编写它。 This post explains more about issues with vectorization in high level languages.

这回答了您的三个问题之一。对于 MATLAB 或 R,我没有很好的答案。

【讨论】:

  • 我知道 Julia 的非矢量化性能很好,也认为这将是最好的解决方案,但我确实尝试了非加权版本的非矢量化解决方案,但速度较慢。但是,这是一个幼稚的 2-for 实施,也许您的链接建议可以给我更好的结果。我会努力解决它们,谢谢:)
  • 你有没有把它放在一个函数中,用@code_warntype检查它的类型稳定性,然后去掉所有的临时分配?
  • 我刚刚在问题的评论中添加了我的函数。如果你能看看他们,我会很高兴。我确实使用@code_warntype 运行了它们,但无法确定我能做的任何事情(自从我上次使用 Julia 以来已经很长时间了,我仍然不太记得如何正确使用这些日志)。
  • D[i,j] = sum(((X[i, :] - Y[j, :]).*W[i,:]).^2)。这有两个主要问题。首先,每个X[i,:] 切片,这样就创建了临时数组。其次,这是使用行编写的。始终以列顺序语言沿列计算(MATLAB 也是如此)。这两个真的都不好。您应该在其中有另一个循环来剪切临时并更改顺序。
猜你喜欢
  • 2022-06-16
  • 2020-09-20
  • 1970-01-01
  • 2018-01-30
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
  • 1970-01-01
  • 2019-09-14
相关资源
最近更新 更多