我正在尝试使用sklearn的kde拟合一些正常数据的内核密度估计。这是一个示例:
import numpy as np
from sklearn.neighbors.kde import KernelDensity as kde
x = np.random.normal(scale = 2, size = [10000, 1])
np.var(x) # 4.0
test_kde = kde()
test_kde.fit(x)
np.var(test_kde.sample(10000)) # 5.0
方差增加一。我在这里做些不可思议的蠢事吗?
答案 0 :(得分:2)
问题是您没有指定正确的bandwidth
来缩放各个密度函数,这就是为什么您过于平滑估计的密度函数的原因。由于您的示例数据遵循正态分布,因此带宽为
>>> h = ((4 * np.std(x)**5) / (3 * len(x)))**(1/5)
>>> h
0.33549590926904804
将是最佳的。可以找到说明on Wikipedia。
>>> test_kde = kde(bandwidth=h)
>>> test_kde.fit(x)
>>> samples = test_kde.sample(10000)
>>> np.var(samples)
4.068727474888099 # close enough to 4
内核密度估计通过使用内核函数(通常是正态分布的密度函数)来估计数据分布的密度而工作。通常的想法是,通过对样本参数化的许多密度函数求和,最终将在给定足够样本的情况下近似原始密度函数:
我们可以将其可视化为您的数据:
from matplotlib.colors import TABLEAU_COLORS
def gauss_kernel(x, m=0, s=1):
return (1/np.sqrt(2 * np.pi * s**2) * np.e**(-((x - m)**2 / (2*s**2))))
from matplotlib.colors import TABLEAU_COLORS
x_plot = np.linspace(-2, 2, 10)
h = 1
for xi, color in zip(x_plot, TABLEAU_COLORS.values()):
plt.plot(xi, gauss_kernel(xi, m=0, s=2) * 0.001, 'x', color=color)
plt.plot(x, 1 / (len(x) * h) * gauss_kernel((xi - x) / h), 'o', color=color)
plt.plot(xi, (1 / (len(x) * h) * gauss_kernel((xi - x) / h)).sum() * 0.001, 'o', color=color)
该图显示了[-2; 2]
中某些点的估计密度和真实密度,以及每个点的核函数(相同颜色的曲线)。估计的密度仅仅是相应内核函数的总和。
可以看到,各个内核函数的右/左越远,它们的和(以及密度)就越低。为了解释这一点,您必须记住,由于我们的原始数据点是从0居中,因为它们是从均值0和方差为2的正态分布中采样的。因此,离中心越远,数据点> 0越少。因此,这意味着将这些点作为输入的高斯核函数最终会将所有数据点放在其平尾部分之一中,并将它们的权重非常接近于零,这就是为什么该核函数之和在此处将很小的原因。 。也可以说我们正在使用高斯密度函数对数据点进行加窗处理。
通过设置h=2
,您可以清楚地看到带宽参数的影响:
h = 2
for xi, color in zip(x_plot, TABLEAU_COLORS.values()):
plt.plot(xi, gauss_kernel(xi, m=0, s=2) * 0.001, 'x', color=color)
plt.plot(x, 1 / (len(x) * h) * gauss_kernel((xi - x) / h), 'o', color=color)
plt.plot(xi, (1 / (len(x) * h) * gauss_kernel((xi - x) / h)).sum() * 0.001, 'o', color=color)
单个内核函数更加平滑,因此,估计 密度也更平滑。原因在于 平滑运算符。内核称为
1/h K((x - xi)/h)
在高斯核的情况下,意味着计算平均值为xi
且方差为h
的正态分布的密度。因此:h
越高,每个密度估计值就越平滑!
在sklearn的情况下,可以通过使用网格搜索(例如通过测量密度估计的质量)来估计最佳带宽。 This example向您展示了如何。如果您选择了合适的带宽,则可以很好地估计密度函数: