Ta odpowiedź została znacznie zmodyfikowana w stosunku do pierwotnej postaci. Wady mojej oryginalnej odpowiedzi zostaną omówione poniżej, ale jeśli chcesz z grubsza zobaczyć, jak ta odpowiedź wyglądała, zanim dokonałem dużej edycji, spójrz na następujący notatnik: 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
Oszacowanie maksymalnego prawdopodobieństwa
... i dlaczego tu nie działa
W mojej pierwotnej odpowiedzi techniką, którą zasugerowałem, było użycie MCMC w celu oszacowania maksymalnego prawdopodobieństwa. Ogólnie rzecz biorąc, MLE jest dobrym podejściem do znalezienia „optymalnych” rozwiązań prawdopodobieństw warunkowych, ale mamy tutaj problem: ponieważ stosujemy model dyskryminacyjny (w tym przypadku las losowy), nasze prawdopodobieństwa są obliczane w odniesieniu do granic decyzji . Mówienie o „optymalnym” rozwiązaniu takiego modelu nie ma sensu, ponieważ gdy znajdziemy się wystarczająco daleko od granicy klasy, model po prostu przewidzi takie dla wszystkiego. Jeśli mamy wystarczającą liczbę klas, niektóre z nich mogą być całkowicie „otoczone”, w takim przypadku nie będzie to stanowić problemu, ale klasy na granicy naszych danych zostaną „zmaksymalizowane” przez wartości, które niekoniecznie są wykonalne.
Aby to zademonstrować, wykorzystam kod wygody, który można znaleźć tutaj , który zapewnia GenerativeSampler
klasę, która otacza kod z mojej oryginalnej odpowiedzi, trochę dodatkowego kodu dla tego lepszego rozwiązania oraz kilka dodatkowych funkcji, z którymi się bawiłem (niektóre z nich działają , niektóre które nie), których prawdopodobnie nie będę tutaj wchodził.
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()
W tej wizualizacji x to prawdziwe dane, a klasa, którą jesteśmy zainteresowani, jest zielona. Kropki połączone linią to narysowane przez nas próbki, a ich kolor odpowiada kolejności, w jakiej zostały pobrane, a ich „cieńsza” pozycja sekwencji jest podana na etykiecie paska koloru po prawej stronie.
Jak widać, próbnik dość szybko oddzielił się od danych, a następnie po prostu odstaje dość daleko od wartości przestrzeni cech, które odpowiadają rzeczywistym obserwacjom. Oczywiście jest to problem.
Jednym ze sposobów, w jaki możemy oszukiwać, jest zmiana naszej funkcji propozycji, aby umożliwić funkcjom przyjmowanie wartości, które faktycznie zaobserwowaliśmy w danych. Spróbujmy i zobaczmy, jak to zmienia zachowanie naszego wyniku.
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)
Wprowadź regułę Bayesa
Po tym, jak zaśmieciliście mnie, że mam mniej kłopotów z matematyką, bawiłem się z tym dość sporą kwotą (stąd budowałem ten GenerativeSampler
przedmiot) i napotkałem problemy, które przedstawiłem powyżej. Kiedy zdałem sobie z tego sprawę, czułem się naprawdę, naprawdę głupio, ale oczywiście to, o co prosisz o wezwania do zastosowania reguły Bayesa, przepraszam za wcześniejsze lekceważenie.
Jeśli nie znasz zasady Bayesa, wygląda to tak:
P(B|A)=P(A|B)P(B)P(A)
W wielu aplikacjach mianownik jest stałą, która działa jak składnik skalujący, aby zapewnić, że licznik zintegruje się z 1, więc reguła jest często przekształcana w ten sposób:
P(B|A)∝P(A|B)P(B)
Lub zwykłym angielskim: „a posterior jest proporcjonalne do prawdopodobieństwa wcześniejszego”.
Wygląda podobnie? A teraz:
P(X|Y)∝P(Y|X)P(X)
Tak, dokładnie nad tym pracowaliśmy wcześniej, konstruując oszacowanie dla MLE, które jest zakotwiczone w obserwowanym rozkładzie danych. Nigdy nie myślałem o rządzeniu Bayes w ten sposób, ale ma to sens, więc dziękuję za umożliwienie mi odkrycia tej nowej perspektywy.
P(Y)
Po zapoznaniu się z tym wnioskiem, że musimy uwzględnić wcześniejsze dane, zróbmy to, instalując standardowe KDE i zobaczmy, jak to zmienia nasz wynik.
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()
I oto masz: duży czarny „X” jest naszym oszacowaniem na mapie (te kontury są KDE tylnego).