非参数估计之 kernel density estimation (核密度估计)
在概率密度估计过程中,如果我们队随机变量的分布是已知的,那么可以直接使用参数估计的方法进行估计,如最大似然估计方法。
然而在实际情况中,随机变量的参数是未知的,因此需要进行非参数估计.核密度估计是非参数估计的一种方法,也就是大家经常听见的parzen 窗方法了.
本文主要介绍 非参数估计的过程以及 parzen窗方法估计概率密度的过程.
非参数估计
过程
如图1所示,对于一个未知的概率密度函数,某一个随机变量落在区域里的概率可以表示成如下形式:
如果足够窄,我们可以用来表示的一个平均后的结果。假设我们现在有个样本,
且他们服从独立同分布,那么个样本中的个落在区域中的概率可以表示成下面的公式:
由上面的公式我们可以得到的期望为:
同时,当足够大时,我们可以近似地认为可以作为的一个近似值。
然后,假设足够大,足够小,并且假设是连续的,那么我们可以得到:
其中是区域中的一个点,是的面积(体积),结合上述4个式子,得:
因此,某一小区域内的概率密度函数就可以用上述公式表示了。
问题
我们再看一下公式(6):
显然我们估计的这个概率密度是一个平滑的结果,即当选择的越大,估计的结果和真实结果相比就越平滑;因此看起来我们需要把设置的小一点,然而如果我们把选择的过小,也会出现问题:太小的会导致这块小区域里面没有一个点落在里面,因此就会得到该点的概率密度为0;另外,假设刚好有一个点落在了这个小区域里,那由于V过于小,我们计算得到的概率密度可能也会趋近于无穷,两个结果对于我们来说都是没有太大意义的.
从 实际的角度来看,我们获取的数据量一定是有限的,因此体积不可能取到无穷小,我们可以总结下,使用非参数概率密度估计有以下两方面限制,且是不可避免的:
- 在有限数据下,使用非参数估计方法计算的概率密度一定是真实概率密度平滑后的结果.
- 在有限数据下,体积趋于无穷小计算的概率密度没有意义.
从 理论的角度来看,我们希望知道如果有无限多的采样数据,那么上述两个限制条件应该怎样克服?假设我们使用下面的方法来估计点 处的概率密度: 构造一系列包含 的区域 , 其中 中包含一个样本,中包含 个样本.则:
其中表示第次估计结果,如果要求能够收敛到,则需要满足下面三个条件:
parzen窗法
原理
假设是一个维的超立方体(hypercube),且其边长为, 那么我们可以用如下公式表示:
然后我们再定义一个窗函数(window function):
定义了一个以圆点为中心的单位超立方体,这样我们就可以用来表示体积内的样本个数:
好了,有了和,直接把他们带入公式(6),我们可以得到parzen窗法的计算公式:
我们发现这个不仅可以是上述的单位超立方体的形式,只要其满足如下两个约束就可以,因此也就出现了各种各样更能表现样本属性的窗函数,比如用的非常多的高斯窗.
解释
网上经常会见到这样的解释: 某一点的概率密度是其他样本点在这一点的概率密度分布的平均值.还有这样一张图:
上面一句话可以这样解释:
我们定义核函数:
那么某一点的概率密度可以用如下函数来表示:
从公式(13)(14)我们可以看出,当很大的时候,就是一个 矮胖的函数,由公式(14)将每个样本点在点处的贡献取平均之后,点处的概率密度就是一个非常平滑的结果; 当太小的时候,就是一个 高瘦的函数,由公式(14)将每个样本点在点处的贡献取平均之后,点处的概率密度就是一个受噪声影响非常大的值,因此估计的概率密度平滑性就很差,反而和真实值差的很远.这两点和1.2节总结的两点缺陷正好吻合.
仿真实验
如下我做了两个仿真
- 第一个是生成了均值是0,方差是2的服从高斯分布的数据,分别使用bandwidth为0.1, 1, 5三个值进行估计
- 第二个是生成了100个服从高斯混合分布的数据,分别是均值为0,方差为1以及均值为5,方差为1的两个高斯混合模型,两者相互独立。
这里核函数选的是高斯核。
bandwidth = 0.1 | bandwidth = 1 | bandwidth = 5 |
---|---|---|
|
|
|
bandwidth = 0.1 | bandwidth = 0.5 | bandwidth = 1 |
{
|
|
|
可以很明显得看到估计的概率密度是如何受到bandwidth影响的,当bandwidth选择的太小,则估计的密度函数受到噪声影响很大,这种结果是不能用的;当bandwidth选择过大,则估计的概率密度又太过于平滑。总之,无论bandwidth过大还是过小,其结果都和实际情况相差的很远,因此合理地选择bandwidth是很重要的。
下面是高斯混合分布密度估计的代码。
import matplotlib.pyplot as plt
import numpy as np
from sklearn.neighbors.kde import KernelDensity
from scipy.stats import norm
if __name__ == "__main__":
np.random.seed(1)
x = np.concatenate((np.random.normal(0, 1, int(0.3*100)), np.random.normal(5, 1, int(0.7*100))))[:, np.newaxis]
plot_x = np.linspace(-5, 10, 1000)[:, np.newaxis]
true_dens = 0.3*norm(0, 1).pdf(plot_x) + 0.7*norm(5, 1).pdf(plot_x)
log_dens = KernelDensity(bandwidth=1).fit(x).score_samples(plot_x)
plt.figure(),
plt.fill(plot_x, true_dens, fc=\'#AAAAFF\', label=\'true_density\')
plt.plot(plot_x, np.exp(log_dens), \'r\', label=\'estimated_density\')
for _ in range(x.shape[0]):
plt.plot(x[:, 0], np.zeros(x.shape[0])-0.01, \'g*\')
plt.legend()
plt.show()
参考资料
[1.] sklearn:Simple 1D Kernel Density Estimation
[2.] Richard O. Duda, Peter E. Hart, and David G. Stork. 2000. Pattern Classification (2nd Edition). Wiley-Interscience, New York, NY, USA.