python实现:KL距离、jensen-shannon距离



Kullback–Leibler divergence:KL距离,是从信息熵的角度出发,也叫相对熵,衡量相同事件空间里的两个概率分布之间的差异情况。

计算公式:
         


=p、q的交叉熵 -  p的信息熵                          

性质:(1)kl(P||Q) >= 0,无最大值     (2)不对称 KL(P||Q) != KL(Q||P)      (3)不满足三角不等式


问题来了:概率分布中p、q为0的情况如何处理?“log of zero”报错在python中如何解决?
一个简单的办法,给概率分布都加一个极小的值,使概率值不为0,而对概率分布又没有什么影响。
matlab中有esp表示最小值,python中的numpy库有spacing函数表示最小值,
python实现:KL距离、jensen-shannon距离_第1张图片
import numpy as np
from math import log
def KLD(p,q):
    p,q=zip(*filter(lambda (x,y): x!=0 or y!=0, zip(p,q))) #去掉二者都是0的概率值
    p=p+np.spacing(1)
    q=q+np.spacing(1)
    print p,q
    return sum([_p * log(_p/_q,2) for (_p,_q) in zip(p,q)])
p=np.ones(5)/5.0
q=[0,0,0.5,0.2,0.3]
print KLD(p,q)
结果:19.489850642923379

改进:由于它的不对称性,不能完全表示两个分布之间的单向关系,因此有人提出了Jensen-Shannon距离,计算每个分布与平均分布的KL距离再去均值。
M=(P+Q)/2
JSD=1/2*KL(P||M) +1/2*KL(Q||M)
当log的底数为2时,JSD的取值范围【0,1】
当log的底数为e时,JSD的取值范围【0,log(e,2)】

应用举例:计算两个字符串的字母分布距离。
如: ‘absfjowswls’ 和 ‘ahoafbaqqq’ 中每个字母出现的概率的分布有何差异?
import string
from math import log
import numpy as np
KLD=lambda p,q:sum([_p * log(_p,2)-_p * log(_q,2) for (_p,_q) in zip(p,q)])


def JSD_core(p,q):
    p,q=zip(*filter(lambda (x,y): x!=0 or y!=0, zip(p,q))) #去掉二者都是0的概率值
    M = [0.5*(_p+_q) for _p,_q in zip(p,q)]
    p=p+np.spacing(1)
    q=q+np.spacing(1)
    M=M+np.spacing(1)
#     print p,q,M
    return 0.5*KLD(p,M)+0.5*KLD(q,M)


reg=lambda x:[x.count(i) for i in string.lowercase]  #频数分布
rate=lambda y:[round(i*1.0/sum(reg(y)),4) for i in reg(y)]  #概率分布
s1='ahaebssa'
s2='awohwsess'
print JSD_core(rate(s1),rate(s2))

你可能感兴趣的:(python实现:KL距离、jensen-shannon距离)