Я пытаюсь подобрать оценку плотности ядра для некоторых обычных данных с помощью 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
Дисперсия увеличивается на единицу. Я делаю что-то невероятно глупое здесь?
Проблема в том, что вы не указали правильный bandwidth
для масштабирования отдельных функций плотности, поэтому вы чрезмерно сглаживаете оценочную функцию плотности. Поскольку данные вашего примера следуют нормальному распределению, пропускная способность
>>> h = ((4 * np.std(x)**5) / (3 * len(x)))**(1/5)
>>> h
0.33549590926904804
будет оптимальным. Объяснение можно найти в Википедии.
>>> 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 наилучшая пропускная способность может быть оценена с помощью, например, поиска по сетке путем измерения качества оценки плотности. Этот пример покажет вам, как это сделать. Если вы выбрали хорошую полосу пропускания, вы можете довольно хорошо оценить функцию плотности: