【问题标题】:Python Earth Mover Distance of 2D arrays二维数组的Python Earth Mover距离
【发布时间】:2019-12-25 00:28:54
【问题描述】:

我想计算两个 2D 阵列(这些不是图像)之间的 Earth Mover 距离

现在我浏览了两个库:scipy (https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.wasserstein_distance.html) 和 pyemd (https://pypi.org/project/pyemd/)。

#define a sampeling method
def sampeling2D(n, mu1, std1, mu2, std2):
   #sample from N(0, 1) in the 2D hyperspace
   x = np.random.randn(n, 2)

   #scale N(0, 1) -> N(mu, std)
   x[:,0] = (x[:,0]*std1) + mu1
   x[:,1] = (x[:,1]*std2) + mu2

   return x

#generate two sets
Y1 = sampeling2D(1000, 0, 1, 0, 1)
Y2 = sampeling2D(1000, -1, 1, -1, 1)

#compute the distance
distance = pyemd.emd_samples(Y1, Y2)

虽然 scipy 版本不接受 2D 数组并返回错误,但 pyemd 方法返回一个值。如果您从文档中看到,它说它只接受一维数组,所以我认为输出是错误的。在这种情况下如何计算这个距离?

【问题讨论】:

    标签: python scipy statistics distribution earth-movers-distance


    【解决方案1】:

    因此,如果我理解正确,您正在尝试传输采样分布,即计算所有集群的权重为 1 的设置的距离。通常,您可以将 EMD 的计算视为最小值的实例成本流,在您的情况下,这归结为linear assignment problem:您的两个数组是二分图中的分区,两个顶点之间的权重是您选择的距离。假设您想使用欧几里得范数作为度量,则可以使用scipy.spatial.distance.cdist 获得边缘的权重,即地面距离,实际上 SciPy 在@987654329 中也提供了线性求和分配问题的求解器@(其中recently 看到了 SciPy 1.4 中提供的巨大性能改进。如果您遇到性能问题,这可能会让您感兴趣;对于 1000x1000 输入,1.3 实现有点慢)。

    换句话说,你想做的事情归结为

    from scipy.spatial.distance import cdist
    from scipy.optimize import linear_sum_assignment
    
    d = cdist(Y1, Y2)
    assignment = linear_sum_assignment(d)
    print(d[assignment].sum() / n)
    

    也可以使用scipy.sparse.csgraph.min_weight_bipartite_full_matching 作为linear_sum_assignment 的替代品;虽然针对稀疏输入(您的输入肯定不是),但它可能会在某些情况下提供性能改进。

    验证此计算的结果是否与您从最小成本流求解器中获得的结果相匹配可能是有益的; NetworkX 中提供了一个这样的求解器,我们可以在其中手动构建图形:

    import networkx as nx
    
    G = nx.DiGraph()
    
    # Represent elements in Y1 by 0, ..., 999, and elements in
    # Y2 by 1000, ..., 1999.
    for i in range(n):
        G.add_node(i, demand=-1)
        G.add_node(n + i, demand=1)
    
    for i in range(n):
        for j in range(n):
            G.add_edge(i, n + j, capacity=1, weight=d[i, j])
    

    此时,我们可以验证上述方法是否符合最小成本流:

    In [16]: d[assignment].sum() == nx.algorithms.min_cost_flow_cost(G)
    Out[16]: True
    

    同样,对于一维输入,看到结果与scipy.stats.wasserstein_distance 一致也很有启发意义:

    from scipy.stats import wasserstein_distance
    
    np.random.seed(0)
    n = 100
    
    Y1 = np.random.randn(n)
    Y2 = np.random.randn(n) - 2
    d =  np.abs(Y1 - Y2.reshape((n, 1)))
    
    assignment = linear_sum_assignment(d)
    print(d[assignment].sum() / n)       # 1.9777950447866477
    print(wasserstein_distance(Y1, Y2))  # 1.977795044786648
    

    【讨论】:

    • 我真的很喜欢你的问题重新表述。这可以用于有限数量的样本,但它有效。谢谢!!
    • 太好了,不客气。如果答案有用,可以标记为accepted。并且作为记录,预计 1000x1000 的情况下,使用 1.4 实现将花费不到一秒的时间。
    • 对不起,我以为我接受了。请问您使用的是哪个版本的scipy?因为我在 Google Colaboratory 工作,并使用最新版本“版本:1.3.1”。但是,它仍然“慢”,所以我不能超过 1000 个样本。
    • 是的,1.3.1 是最新的官方版本;您可以从github.com/scipy/scipy 获取 1.4 的预发布版本(或者等到它正式发布)。或者,如果您不介意自己构建一个包,可以找到解决问题的几个有效实现。例如,gist.github.com/kylemcdonald/3dcce059060dbd50967970905cf54cd9 提供的解决方案在大约 50 毫秒内解决了 1000x1000 的情况,而在 SciPy 1.3.1 中需要 100 秒。有关使用说明,请参阅要点的 cmets。
    • @AlexEftimiades:您对最小成本流公式感到满意吗?如果是这样,最小成本流问题的完整性定理告诉我们,由于所有需求都是整数 (1),因此存在一个沿着每条边(因此为 0 或 1)具有整数流的解,而这又是一个分配。
    猜你喜欢
    • 1970-01-01
    • 2017-09-12
    • 1970-01-01
    • 2017-12-14
    • 2011-07-28
    • 1970-01-01
    • 2017-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多