给定一个数据样本集,若想得到总体的概率分布,通常有两类方法:参数估计和非参数估计。参数估计需要先假定这个数据样本服从某种分布,再使用数据去拟合分布中的参数,含有较大的主观成分。而非参数估计,即核密度估计(Kernel Density Estimation,KDE),不需要预先假设,从数据本身出发,来估计未知的密度函数。

一、估计过程

1、以每个点的数据+带宽(邻域)作为参数,用核函数估计样本中每个数据点及其附近的概率密度函数

  • 核函数作用:对每个数据点得到光滑的、积分为1的概率密度估计。

  • 核函数种类:uniform,triangular, biweight, triweight, Epanechnikov,normal等。理论上,所有平滑的峰值函数都可以核函数来使用,只要保证该函数曲线下方的面积和为1。
    在这里插入图片描述

  • 带宽(bandwidth):将其理解为直方图中bin的宽度。在KDE中,带宽大小的设定是一种误差和方差的权衡(bias-variance tradeoff),带宽设的越小,误差越小,但方差越大。带宽越小,KDE整体曲线就越陡峭,反之,就越平坦。不同的带宽对拟合结果的影响可能很大。
    在这里插入图片描述
    上图是对同一数据使用不同带宽获得的估计结果。灰色是真实的分布,为标准正态分布。
    为了获得更接近于真实分布的概率密度函数,诞生了自适应/可变带宽核密度估计

在做核密度估计时,涉及两个变量:核函数、带宽。
在这里插入图片描述

一般来说,由于高斯内核方便的数学性值,常使用高斯内核进行核密度估计。
2、将所有拟合出的分布取平均,如果某些数据点比较重要,也可以求加权平均,得到整个数据集的概率密度
在这里插入图片描述

先对每个点做拟合,最后相加求平均得到整体概率密度函数。
3、归一化,保证估计出的密度函数积分为1。

二、核密度估计之带宽的选择

一个好的估计应该是拟合的概率密度和我们计算的概率密度比较接近。首先,定义一个误差函数作为度量标准,常见平均积分平方误差(mean intergrated squared error),最小化误差作为bin的确定准则。
具体公式参见:核密度估计 Kernel Density Estimation(KDE)

  • 自适应/可变带宽核密度估计

对应于固定带宽核密度估计,自适应带宽核密度估计就是带宽不固定,根据样本情况而变,具体取决于估计的位置(balloon estimator)或样本点(逐点估计pointwise estimator)。
自适应带宽的核密度估计方法是在固定带宽核密度函数的基础上,通过修正带宽参数而得到的,为每个数据点对应一个带宽。
具体公式参见:核密度估计与自适应带宽的核密度估计

三、KDE在sklearn中的实现

官方文档:sklearn.neighbors.KernelDensity

class sklearn.neighbors.KernelDensity(*, bandwidth=1.0, algorithm='auto', kernel='gaussian', 
metric='euclidean', atol=0, rtol=0, breadth_first=True, leaf_size=40, metric_params=None)
import numpy as np
rng = np.random.RandomState(42)
X = rng.random_sample((100, 3))
kde = KernelDensity(kernel='gaussian', bandwidth=0.5).fit(X)
log_density = kde.score_samples(X[:3])
log_density

score_samples返回数据中每个点处的概率密度函数。

四、使用网格搜索寻找似然估计最大的带宽

在python中,参数调优通常通过网格搜索和交叉验证完成 。

from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import LeaveOneOut,KFold
import numpy as np

# 定义一组待选择的带宽
bandwidths = 10**np.linspace(-1,1,100)
# 留一法
grid = GridSearchCV(KernelDensity(kernel='gaussian'),
                   {'bandwidth':bandwidths},
                    cv=LeaveOneOut())
# K折交叉验证
kfold = KFold(n_splits=10)
grid = GridSearchCV(KernelDensity(kernel='gaussian'),
                   {'bandwidth':bandwidths},
                    cv=kfold)
                  
# data为要拟合的数据
grid.fit(data)
# 最优带宽为似然估计最大化时的带宽
best_width = grid.best_params

实际使用时,使用K折交叉验证更好一些,留一法测试成本过大。

除文中提到的链接外,还参考了以下:
非参数估计:核密度估计KDE
十三、Sklearn核密度估计
感谢各位大佬分享~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐