Дисперсия Sci-kit Learn KDE увеличивается на единицу

Я пытаюсь подобрать оценку плотности ядра для некоторых обычных данных с помощью 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

Дисперсия увеличивается на единицу. Я делаю что-то невероятно глупое здесь?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
123
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что вы не указали правильный 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)

true density function, kernels and sum of kernels with h=1

На этом графике показана расчетная и истинная плотность в некоторых точках [-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)

true density function, kernels and sum of kernels with h=2

Отдельные функции ядра намного более плавные, и, как следствие, оценка плотность намного более гладкая, слишком. Причина этого кроется в формулировке оператор сглаживания. Ядро называется

1/h K((x - xi)/h)

что в случае ядра Гаусса означает вычисление плотности нормального распределения со средним значением xi и дисперсией h. Следовательно: чем выше h, тем более сглажена каждая оценка плотности!

В случае sklearn наилучшая пропускная способность может быть оценена с помощью, например, поиска по сетке путем измерения качества оценки плотности. Этот пример покажет вам, как это сделать. Если вы выбрали хорошую полосу пропускания, вы можете довольно хорошо оценить функцию плотности:

density function estimates with different bandwidth values

Другие вопросы по теме