Этот ответ был значительно изменен по сравнению с первоначальной формой. Недостатки моего первоначального ответа будут обсуждаться ниже, но если вы хотите примерно увидеть, как этот ответ выглядел до того, как я сделал крупное редактирование, взгляните на следующую записную книжку: https://nbviewer.jupyter.org/github. /dmarx/data_generation_demo/blob/54be78fb5b68218971d2568f1680b4f783c0a79a/demo.ipynb
P(X)P(X|Y)∝P(Y|X)P(X)P(Y|X)X
Оценка максимального правдоподобия
... и почему это не работает здесь
В моем первоначальном ответе я предложил использовать метод MCMC для оценки максимального правдоподобия. Как правило, MLE является хорошим подходом к поиску «оптимальных» решений условных вероятностей, но здесь у нас есть проблема: поскольку мы используем дискриминационную модель (в данном случае случайный лес), наши вероятности рассчитываются относительно границ решения , На самом деле не имеет смысла говорить об «оптимальном» решении для модели, подобной этой, потому что, как только мы отойдем достаточно далеко от границы класса, модель будет просто предсказывать их для всего. Если у нас достаточно классов, некоторые из них могут быть полностью «окружены», и в этом случае это не будет проблемой, но классы на границе наших данных будут «максимизированы» значениями, которые не всегда выполнима.
Чтобы продемонстрировать, я собираюсь использовать некоторый удобный код, который вы можете найти здесь , который предоставляет GenerativeSampler
класс, который оборачивает код из моего исходного ответа, некоторый дополнительный код для этого лучшего решения и некоторые дополнительные функции, с которыми я играл (некоторые из которых работают некоторые, которые этого не делают, что я, вероятно, не буду вдаваться в здесь.
np.random.seed(123)
sampler = GenerativeSampler(model=RFC, X=X, y=y,
target_class=2,
prior=None,
class_err_prob=0.05, # <-- the score we use for candidates that aren't predicted as the target class
rw_std=.05, # <-- controls the step size of the random walk proposal
verbose=True,
use_empirical=False)
samples, _ = sampler.run_chain(n=5000)
burn = 1000
thin = 20
X_s = pca.transform(samples[burn::thin,:])
# Plot the iris data
col=['r','b','g']
for i in range(3):
plt.scatter(*X_r[y==i,:].T, c=col[i], marker='x')
plt.plot(*X_s.T, 'k')
plt.scatter(*X_s.T, c=np.arange(X_s.shape[0]))
plt.colorbar()
plt.show()
В этой визуализации x - это реальные данные, а интересующий нас класс - зеленый. Точки, соединенные линией, представляют собой образцы, которые мы нарисовали, и их цвет соответствует порядку, в котором они были взяты, с их «утонченной» последовательностью, обозначенной цветовой меткой справа.
Как вы можете видеть, сэмплер довольно быстро отклонился от данных, а затем просто повисает довольно далеко от значений пространства пространственных объектов, которые соответствуют любым реальным наблюдениям. Очевидно, это проблема.
Один из способов обмануть нас - изменить функцию предложения, чтобы функции могли принимать значения, которые мы действительно наблюдали в данных. Давайте попробуем это и посмотрим, как это меняет поведение нашего результата.
np.random.seed(123)
sampler = GenerativeSampler(model=RFC, X=X, y=y,
target_class=2,
prior=None,
class_err_prob=0.05,
verbose=True,
use_empirical=True) # <-- magic happening under the hood
samples, _ = sampler.run_chain(n=5000)
X_s = pca.transform(samples[burn::thin,:])
# Constrain attention to just the target class this time
i=2
plt.scatter(*X_r[y==i,:].T, c='k', marker='x')
plt.scatter(*X_s.T, c='g', alpha=0.3)
#plt.colorbar()
plt.show()
sns.kdeplot(X_s, cmap=sns.dark_palette('green', as_cmap=True))
plt.scatter(*X_r[y==i,:].T, c='k', marker='x')
plt.show()
X
P(X)P(Y|X)P(X)P(Y|X)P(X)
Введите правило Байеса
После того, как вы травили меня быть менее рука Волнистым с математикой здесь, я играл с этим изрядным (отсюда мне строящим GenerativeSampler
вещью), и я столкнулся проблемы , которые я выложил выше. Я чувствовал себя очень, очень глупо , когда я сделал эту реализацию, но , очевидно , что вы просите призывы к применению правила Байеса , и я прошу прощения за то , что пренебрежительно ранее.
Если вы не знакомы с правилом Байеса, это выглядит так:
P(B|A)=P(A|B)P(B)P(A)
Во многих приложениях знаменатель является константой, которая действует как масштабирующий член, чтобы гарантировать, что числитель интегрируется в 1, поэтому правило часто пересчитывается таким образом:
P(B|A)∝P(A|B)P(B)
Или в простом английском языке: «задний пропорционально предшествующие времена правдоподобного».
Выглядит знакомо? Как насчет сейчас:
P(X|Y)∝P(Y|X)P(X)
Да, это именно то, к чему мы работали ранее, построив оценку для MLE, которая привязана к наблюдаемому распределению данных. Я никогда не думал о правилах Байеса таким образом, но это имеет смысл, поэтому спасибо, что дали мне возможность открыть для себя эту новую перспективу.
P(Y)
Итак, сделав это понимание того, что нам нужно включить априор для данных, давайте сделаем это путем подгонки стандартного KDE и посмотрим, как это изменит наш результат.
np.random.seed(123)
sampler = GenerativeSampler(model=RFC, X=X, y=y,
target_class=2,
prior='kde', # <-- the new hotness
class_err_prob=0.05,
rw_std=.05, # <-- back to the random walk proposal
verbose=True,
use_empirical=False)
samples, _ = sampler.run_chain(n=5000)
burn = 1000
thin = 20
X_s = pca.transform(samples[burn::thin,:])
# Plot the iris data
col=['r','b','g']
for i in range(3):
plt.scatter(*X_r[y==i,:].T, c=col[i], marker='x')
plt.plot(*X_s.T, 'k--')
plt.scatter(*X_s.T, c=np.arange(X_s.shape[0]), alpha=0.2)
plt.colorbar()
plt.show()
XP(X|Y)
# MAP estimation
from sklearn.neighbors import KernelDensity
from sklearn.model_selection import GridSearchCV
from scipy.optimize import minimize
grid = GridSearchCV(KernelDensity(), {'bandwidth': np.linspace(0.1, 1.0, 30)}, cv=10, refit=True)
kde = grid.fit(samples[burn::thin,:]).best_estimator_
def map_objective(x):
try:
score = kde.score_samples(x)
except ValueError:
score = kde.score_samples(x.reshape(1,-1))
return -score
x_map = minimize(map_objective, samples[-1,:].reshape(1,-1)).x
print(x_map)
x_map_r = pca.transform(x_map.reshape(1,-1))[0]
col=['r','b','g']
for i in range(3):
plt.scatter(*X_r[y==i,:].T, c=col[i], marker='x')
sns.kdeplot(*X_s.T, cmap=sns.dark_palette('green', as_cmap=True))
plt.scatter(x_map_r[0], x_map_r[1], c='k', marker='x', s=150)
plt.show()
И вот, у вас это есть: большой черный 'X' - это наша оценка MAP (эти контуры - KDE на задней стороне).