我正在尝试实现此paper
中显示的KL分歧的非参数估计这是我的代码:
import numpy as np
import math
import itertools
import random
from scipy.interpolate import interp1d
def log(x):
if x > 0: return math.log(x)
else: return 0
g = lambda x, inp,N : sum(0.5 + 0.5 * np.sign(x-inp))/N
def ecdf(x,N):
out = [g(i,x,N) for i in x]
fun = interp1d(x, out, kind='linear', bounds_error = False, fill_value = (0,1))
return fun
def KL_est(x,y):
ex = min(np.diff(sorted(np.unique(x))))
ey = min(np.diff(sorted(np.unique(y))))
e = min(ex,ey) * 0.9
N = len(x)
x.sort()
y.sort()
P = ecdf(x,N)
Q = ecdf(y,N)
KL = sum(log(v) for v in ((P(x)-P(x-e))/(Q(x)-Q(x-e))) ) / N
return KL
我的麻烦在于scipy interp1d。我正在使用interp1d返回的函数来查找新输入的值。问题是,一些输入值非常接近(相隔10 ^ -5),并且函数返回两者的相同值。在上面的代码中,Q(x) - Q(x-e)导致除以零的错误。
以下是一些可以重现问题的测试代码:
x = np.random.normal(0, 1, 10)
y = np.random.normal(0, 1, 10)
ex = min(np.diff(sorted(np.unique(x))))
ey = min(np.diff(sorted(np.unique(y))))
e = min(ex,ey) * 0.9
N = len(x)
x.sort()
y.sort()
P = ecdf(x,N)
Q = ecdf(y,N)
KL = sum(log(v) for v in ((P(x)-P(x-e))/(Q(x)-Q(x-e))) ) / N
我如何获得更精确的插值?
答案 0 :(得分:2)
当e
变小时,您实际上是在尝试以数字方式计算P
和Q
的导数的比率。正如您所发现的那样,在浮点运行时,您会以这种方式快速耗尽精度。
另一种方法是使用可以直接返回导数的插值函数。例如,您可以尝试scipy.interpolate.InterpolatedUnivariateSpline
。您说的是kind='linear'
到interp1d
,因此等效于k=1
。构造它之后,样条曲线有方法derivatives()
,它为您提供不同点的所有导数。对于较小的e
值,您可以切换到使用派生词。