【问题标题】:Jensen-Shannon DivergenceJensen-Shannon 分歧
【发布时间】:2013-03-30 14:18:06
【问题描述】:

我还有一个问题希望有人能帮助我。

我正在使用 Jensen-Shannon-Divergence 来衡量两个概率分布之间的相似性。考虑到使用以 2 为底的对数,相似度得分在 1 和 0 之间似乎是正确的,0 表示分布相等。

但是,我不确定某处是否确实存在错误,并且想知道是否有人能够说“是的,这是正确的”或“不,你做错了什么”。

代码如下:

from numpy import zeros, array
from math import sqrt, log


class JSD(object):
    def __init__(self):
        self.log2 = log(2)


    def KL_divergence(self, p, q):
        """ Compute KL divergence of two vectors, K(p || q)."""
        return sum(p[x] * log((p[x]) / (q[x])) for x in range(len(p)) if p[x] != 0.0 or p[x] != 0)

    def Jensen_Shannon_divergence(self, p, q):
        """ Returns the Jensen-Shannon divergence. """
        self.JSD = 0.0
        weight = 0.5
        average = zeros(len(p)) #Average
        for x in range(len(p)):
            average[x] = weight * p[x] + (1 - weight) * q[x]
            self.JSD = (weight * self.KL_divergence(array(p), average)) + ((1 - weight) * self.KL_divergence(array(q), average))
        return 1-(self.JSD/sqrt(2 * self.log2))

if __name__ == '__main__':
    J = JSD()
    p = [1.0/10, 9.0/10, 0]
    q = [0, 1.0/10, 9.0/10]
    print J.Jensen_Shannon_divergence(p, q)

问题是,例如比较两个文本文档时,我觉得分数不够高。不过,这纯属主观感受。

一如既往地感谢任何帮助。

【问题讨论】:

  • 也许可以尝试将输出与this Matlab script? 进行比较,或者在 Octave 中运行。
  • if p[x] != 0.0 or p[x] != 0 看起来很奇怪。
  • 如果 p[x] != 0.0 或 p[x] != 0 用于确保我们不考虑为零的条目,无论它们是浮点数还是整数,是什么你指的是什么?或者你的意思是这条线是奇怪的句号?非常感谢。
  • p[x] != 0 相同,因为0.0 == 0。这就是为什么我怀疑那里可能有错字。

标签: python distance metrics


【解决方案1】:

请注意,下面的 scipy 熵调用是 Kullback-Leibler 散度。

见:http://en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence

#!/usr/bin/env python
from scipy.stats import entropy
from numpy.linalg import norm
import numpy as np

def JSD(P, Q):
    _P = P / norm(P, ord=1)
    _Q = Q / norm(Q, ord=1)
    _M = 0.5 * (_P + _Q)
    return 0.5 * (entropy(_P, _M) + entropy(_Q, _M))

还要注意,问题中的测试用例看起来有问题?? p分布的总和不等于1.0。

见:http://www.itl.nist.gov/div898/handbook/eda/section3/eda361.htm

【讨论】:

  • 不需要导入和使用norm,因为entropy 将在它们加起来不等于1 时对分布进行归一化(参见docs.scipy.org/doc/scipy-dev/reference/generated/…)。但是,要像这样计算 _M_P_Q 需要是 numpy.ndarray 对象。
  • @Tur1ng 请注意,需要 norm 因为 _M 的计算要求 _P_Q 是概率分布(已经标准化)。另请注意,列表被强制为 numpy 数组,所以这很好:[2, 4] / np.array([1, 2])
  • @DougShore 实际上,由于scipy.stats.entropy 对分布进行了归一化,因此您不需要对_P_Q 进行归一化来计算_M,您只需要将它们求和即可值,并且您可能可以节省一些计算。但是,这样的可读性要高得多。另一方面,我更喜欢不进行不必要计算的函数,并假设输入是归一化的概率。
【解决方案2】:

自从 Jensen-Shannon 距离 (distance.jensenshannon) 已包含在Scipy 1.2 中,Jensen-Shannon 散度可以通过 Jensen-Shannon 距离的平方获得:

from scipy.spatial import distance

distance.jensenshannon([1.0/10, 9.0/10, 0], [0, 1.0/10, 9.0/10]) ** 2
# 0.5306056938642212

【讨论】:

    【解决方案3】:

    获取一些已知散度分布的数据,并将您的结果与这些已知值进行比较。

    顺便说一句:KL_divergence 中的总和可以像这样使用zip built-in function 重写:

    sum(_p * log(_p / _q) for _p, _q in zip(p, q) if _p != 0)
    

    这消除了很多“噪音”,也更加“pythonic”。不需要与0.00 进行双重比较。

    【讨论】:

      【解决方案4】:

      python 中用于 n 个概率分布的通用版本

      import numpy as np
      from scipy.stats import entropy as H
      
      
      def JSD(prob_distributions, weights, logbase=2):
          # left term: entropy of misture
          wprobs = weights * prob_distributions
          mixture = wprobs.sum(axis=0)
          entropy_of_mixture = H(mixture, base=logbase)
      
          # right term: sum of entropies
          entropies = np.array([H(P_i, base=logbase) for P_i in prob_distributions])
          wentropies = weights * entropies
          sum_of_entropies = wentropies.sum()
      
          divergence = entropy_of_mixture - sum_of_entropies
          return(divergence)
      
      # From the original example with three distributions:
      P_1 = np.array([1/2, 1/2, 0])
      P_2 = np.array([0, 1/10, 9/10])
      P_3 = np.array([1/3, 1/3, 1/3])
      
      prob_distributions = np.array([P_1, P_2, P_3])
      n = len(prob_distributions)
      weights = np.empty(n)
      weights.fill(1/n)
      
      print(JSD(prob_distributions, weights))
      #0.546621319446
      

      【讨论】:

        【解决方案5】:

        明确遵循Wikipedia article 中的数学:

        def jsdiv(P, Q):
            """Compute the Jensen-Shannon divergence between two probability distributions.
        
            Input
            -----
            P, Q : array-like
                Probability distributions of equal length that sum to 1
            """
        
            def _kldiv(A, B):
                return np.sum([v for v in A * np.log2(A/B) if not np.isnan(v)])
        
            P = np.array(P)
            Q = np.array(Q)
        
            M = 0.5 * (P + Q)
        
            return 0.5 * (_kldiv(P, M) +_kldiv(Q, M))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-05-23
          • 1970-01-01
          • 1970-01-01
          • 2013-03-27
          • 2015-11-12
          • 1970-01-01
          • 2021-10-10
          相关资源
          最近更新 更多