Esta respuesta se ha modificado significativamente de su forma original. Los defectos de mi respuesta original se analizarán a continuación, pero si desea ver más o menos cómo se veía esta respuesta antes de realizar la gran edición, eche un vistazo al siguiente cuaderno: 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
Estimación de máxima verosimilitud
... y por qué no funciona aquí
En mi respuesta original, la técnica que sugerí era usar MCMC para realizar la estimación de máxima verosimilitud. En general, MLE es un buen enfoque para encontrar las soluciones "óptimas" para las probabilidades condicionales, pero tenemos un problema aquí: porque estamos usando un modelo discriminativo (un bosque aleatorio en este caso) nuestras probabilidades se calculan en relación con los límites de decisión . En realidad, no tiene sentido hablar de una solución "óptima" para un modelo como este porque una vez que nos alejemos lo suficiente del límite de la clase, el modelo solo pronosticará las opciones para todo. Si tenemos suficientes clases, algunas de ellas podrían estar completamente "rodeadas", en cuyo caso esto no será un problema, pero las clases en el límite de nuestros datos serán "maximizadas" por valores que no son necesariamente factibles.
Para demostrarlo, voy a aprovechar el código de conveniencia que puede encontrar aquí , que proporciona la GenerativeSampler
clase que envuelve el código de mi respuesta original, algún código adicional para esta mejor solución y algunas características adicionales con las que estaba jugando (algunas que funcionan , algunos que no) en lo que probablemente no entraré aquí.
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()
En esta visualización, las x son los datos reales, y la clase que nos interesa es verde. Los puntos conectados a la línea son las muestras que dibujamos, y su color corresponde al orden en que fueron muestreados, con su posición de secuencia "adelgazada" dada por la etiqueta de la barra de color a la derecha.
Como puede ver, la muestra divergió de los datos con bastante rapidez y luego, básicamente, se queda bastante lejos de los valores del espacio de características que corresponden a cualquier observación real. Claramente esto es un problema.
Una forma de hacer trampa es cambiar nuestra función de propuesta para permitir que las características tomen valores que realmente observamos en los datos. Probemos eso y veamos cómo eso cambia el comportamiento de nuestro resultado.
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)
Introduzca la regla de Bayes
Después de que me acosabas para ser menos hábil con las matemáticas aquí, jugué con esto bastante (de ahí que construyera la GenerativeSampler
cosa), y me encontré con los problemas que expuse anteriormente. Me sentí muy, muy estúpido cuando me di cuenta de esto, pero obviamente lo que estás pidiendo pide una aplicación de la regla de Bayes y me disculpo por ser despectivo antes.
Si no está familiarizado con la regla bayes, se ve así:
P(B|A)=P(A|B)P(B)P(A)
En muchas aplicaciones, el denominador es una constante que actúa como un término de escala para garantizar que el numerador se integre a 1, por lo que la regla a menudo se reformula de la siguiente manera:
P(B|A)∝P(A|B)P(B)
O en inglés simple: "el posterior es proporcional a los tiempos anteriores la probabilidad".
¿Parecer familiar? Que tal ahora:
P(X|Y)∝P(Y|X)P(X)
Sí, esto es exactamente lo que trabajamos anteriormente al construir una estimación para el MLE que esté anclada a la distribución observada de los datos. Nunca he pensado en Bayes gobernar de esta manera, pero tiene sentido, así que gracias por darme la oportunidad de descubrir esta nueva perspectiva.
P(Y)
Entonces, después de haber hecho esta idea de que necesitamos incorporar un previo para los datos, hagamos eso ajustando un KDE estándar y veamos cómo eso cambia nuestro resultado.
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()
Y ahí lo tiene: la gran 'X' negra es nuestra estimación MAP (esos contornos son el KDE de la parte posterior).