Diese Antwort wurde gegenüber der ursprünglichen Form erheblich geändert. Die Fehler meiner ursprünglichen Antwort werden unten erläutert. Wenn Sie jedoch ungefähr sehen möchten, wie diese Antwort aussah, bevor ich die große Bearbeitung vorgenommen habe, schauen Sie sich das folgende Notizbuch an: 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
Maximum-Likelihood-Schätzung
... und warum es hier nicht funktioniert
In meiner ursprünglichen Antwort schlug ich vor, MCMC zu verwenden, um eine Maximum-Likelihood-Schätzung durchzuführen. Im Allgemeinen ist MLE ein guter Ansatz, um die "optimalen" Lösungen für bedingte Wahrscheinlichkeiten zu finden, aber wir haben hier ein Problem: Da wir ein Unterscheidungsmodell (in diesem Fall eine zufällige Gesamtstruktur) verwenden, werden unsere Wahrscheinlichkeiten relativ zu Entscheidungsgrenzen berechnet . Es ist eigentlich nicht sinnvoll, über eine "optimale" Lösung für ein solches Modell zu sprechen, da das Modell, sobald wir uns weit genug von der Klassengrenze entfernt haben, nur eine für alles vorhersagt. Wenn wir genügend Klassen haben, sind einige von ihnen möglicherweise vollständig "umgeben". In diesem Fall ist dies kein Problem, aber Klassen an der Grenze unserer Daten werden durch Werte "maximiert", die nicht unbedingt machbar sind.
Um dies zu demonstrieren, werde ich einen praktischen Code nutzen, den Sie hier finden. Er enthält die GenerativeSampler
Klasse, die Code aus meiner ursprünglichen Antwort umschließt, zusätzlichen Code für diese bessere Lösung und einige zusätzliche Funktionen, mit denen ich herumgespielt habe (einige davon funktionieren) , einige, die es nicht tun), auf die ich hier wahrscheinlich nicht eingehen werde.
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()
In dieser Visualisierung sind die x die realen Daten, und die Klasse, an der wir interessiert sind, ist grün. Die linienverbundenen Punkte sind die Proben, die wir gezeichnet haben, und ihre Farbe entspricht der Reihenfolge, in der sie abgetastet wurden, wobei ihre "verdünnte" Sequenzposition durch das Farbbalkenetikett auf der rechten Seite angegeben wird.
Wie Sie sehen können, ist der Sampler ziemlich schnell von den Daten abgewichen und hängt dann im Grunde genommen ziemlich weit von den Werten des Feature-Space entfernt, die realen Beobachtungen entsprechen. Dies ist eindeutig ein Problem.
Eine Möglichkeit, die wir betrügen können, besteht darin, unsere Vorschlagsfunktion so zu ändern, dass nur Features Werte annehmen können, die wir tatsächlich in den Daten beobachtet haben. Versuchen wir das und sehen, wie sich das Verhalten unseres Ergebnisses ändert.
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.)
Geben Sie die Bayes-Regel ein
Nachdem Sie mich dazu gebracht hatten, mit der Mathematik hier weniger handgewellt zu sein, spielte ich eine ganze Menge damit herum (daher baue ich das GenerativeSampler
Ding) und stieß auf die Probleme, die ich oben dargelegt hatte . Ich fühlte mich wirklich sehr, sehr dumm, als ich diese Erkenntnis machte, aber offensichtlich fordern Sie, dass Sie eine Anwendung der Bayes-Regel fordern, und ich entschuldige mich dafür, dass ich früher abgewiesen habe.
Wenn Sie mit der Bayes-Regel nicht vertraut sind, sieht sie folgendermaßen aus:
P.( B | A ) = P.( A | B ) P.( B )P.( A )
In vielen Anwendungen ist der Nenner eine Konstante, die als Skalierungsterm fungiert, um sicherzustellen, dass der Zähler auf 1 integriert wird. Daher wird die Regel häufig folgendermaßen angepasst:
P.( B | A ) ∝ P.( A | B ) P.( B )
Oder im Klartext: "Der hintere Teil ist proportional zu den früheren Zeiten der Wahrscheinlichkeit".
Ähnlich aussehend? Wie wäre es jetzt:
P.( X.| Y.) ∝ P.( Y.| X.) P.( X.)
Ja, genau daran haben wir früher gearbeitet, indem wir eine Schätzung für die MLE erstellt haben, die in der beobachteten Verteilung der Daten verankert ist. Ich habe noch nie über die Bayes-Regel auf diese Weise nachgedacht, aber es macht Sinn. Vielen Dank, dass Sie mir die Gelegenheit gegeben haben, diese neue Perspektive zu entdecken.
P.( Y.)
Nachdem wir diese Erkenntnis gewonnen haben, dass wir einen Prior für die Daten einfügen müssen, lassen Sie uns dies tun, indem wir eine Standard-KDE anpassen und sehen, wie dies unser Ergebnis verändert.
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()
Und da haben Sie es: Das große schwarze 'X' ist unsere MAP-Schätzung (diese Konturen sind die KDE des Seitenzahns).