machine learning - predicciones - Conjunto de diferentes tipos de regresores utilizando scikit-learn(o cualquier otro framework de Python)
python machine learning pdf (4)
Estoy tratando de resolver la tarea de regresión. Descubrí que 3 modelos funcionan muy bien para diferentes subconjuntos de datos: LassoLARS, SVR y Gradient Tree Boosting. Noté que cuando hago predicciones usando todos estos 3 modelos y luego hago una tabla de ''salida verdadera'' y salidas de mis 3 modelos, veo que cada vez al menos uno de los modelos está realmente cerca de la salida verdadera, aunque otros 2 podría estar relativamente lejos.
Cuando calculo el error mínimo posible (si tomo la predicción del "mejor" predictor para cada ejemplo de prueba) me sale un error que es mucho menor que el error de cualquier modelo solo. Así que pensé en tratar de combinar las predicciones de estos 3 modelos diferentes en algún tipo de conjunto. La pregunta es, ¿cómo hacer esto correctamente? Todos mis 3 modelos se construyen y ajustan usando scikit-learn, ¿proporciona algún tipo de método que podría usarse para agrupar modelos en conjunto? El problema aquí es que no quiero solo promediar las predicciones de los tres modelos, quiero hacer esto con la ponderación, donde la ponderación debería determinarse en función de las propiedades del ejemplo específico.
Incluso si scikit-learn no proporciona tal funcionalidad, sería bueno si alguien sabe cómo abordar esta tarea en forma personalizada, es decir, determinar la ponderación de cada modelo para cada ejemplo en los datos. Creo que podría hacerse por un regresor separado construido sobre estos 3 modelos, que intentarán obtener pesos óptimos para cada uno de los 3 modelos, pero no estoy seguro de si esta es la mejor manera de hacerlo.
Este es un problema conocido e interesante (y a menudo doloroso) con predicciones jerárquicas. Un problema con el entrenamiento de un número de predictores sobre los datos del tren, luego entrenar un predictor más alto sobre ellos, otra vez usando los datos del tren, tiene que ver con la descomposición del sesgo-varianza.
Supongamos que tiene dos predictores, uno esencialmente una versión sobreajustada del otro, luego el primero aparecerá sobre el conjunto de trenes para ser mejor que el segundo. El predictor combinado favorecerá al primero sin una razón real, simplemente porque no puede distinguir el ajuste excesivo de la predicción verdadera de alta calidad.
La forma conocida de lidiar con esto es preparar, para cada fila en los datos del tren, para cada uno de los predictores, una predicción para la fila, basada en un modelo no apto para esta fila. Para la versión sobreajustada, por ejemplo, esto no producirá un buen resultado para la fila, en promedio. El predictor combinado será capaz de evaluar mejor un modelo justo para combinar los predictores de bajo nivel.
Shahar Azulay y yo escribimos una etapa de transformación para hacer frente a esto:
class Stacker(object):
"""
A transformer applying fitting a predictor `pred` to data in a way
that will allow a higher-up predictor to build a model utilizing both this
and other predictors correctly.
The fit_transform(self, x, y) of this class will create a column matrix, whose
each row contains the prediction of `pred` fitted on other rows than this one.
This allows a higher-level predictor to correctly fit a model on this, and other
column matrices obtained from other lower-level predictors.
The fit(self, x, y) and transform(self, x_) methods, will fit `pred` on all
of `x`, and transform the output of `x_` (which is either `x` or not) using the fitted
`pred`.
Arguments:
pred: A lower-level predictor to stack.
cv_fn: Function taking `x`, and returning a cross-validation object. In `fit_transform`
th train and test indices of the object will be iterated over. For each iteration, `pred` will
be fitted to the `x` and `y` with rows corresponding to the
train indices, and the test indices of the output will be obtained
by predicting on the corresponding indices of `x`.
"""
def __init__(self, pred, cv_fn=lambda x: sklearn.cross_validation.LeaveOneOut(x.shape[0])):
self._pred, self._cv_fn = pred, cv_fn
def fit_transform(self, x, y):
x_trans = self._train_transform(x, y)
self.fit(x, y)
return x_trans
def fit(self, x, y):
"""
Same signature as any sklearn transformer.
"""
self._pred.fit(x, y)
return self
def transform(self, x):
"""
Same signature as any sklearn transformer.
"""
return self._test_transform(x)
def _train_transform(self, x, y):
x_trans = np.nan * np.ones((x.shape[0], 1))
all_te = set()
for tr, te in self._cv_fn(x):
all_te = all_te | set(te)
x_trans[te, 0] = self._pred.fit(x[tr, :], y[tr]).predict(x[te, :])
if all_te != set(range(x.shape[0])):
warnings.warn(''Not all indices covered by Stacker'', sklearn.exceptions.FitFailedWarning)
return x_trans
def _test_transform(self, x):
return self._pred.predict(x)
Aquí hay un ejemplo de la mejora del entorno descrito en la respuesta de @MaximHaytovich.
Primero, alguna configuración:
from sklearn import linear_model
from sklearn import cross_validation
from sklearn import ensemble
from sklearn import metrics
y = np.random.randn(100)
x0 = (y + 0.1 * np.random.randn(100)).reshape((100, 1))
x1 = (y + 0.1 * np.random.randn(100)).reshape((100, 1))
x = np.zeros((100, 2))
Tenga en cuenta que x0
y x1
son solo versiones ruidosas de y
. Usaremos las primeras 80 filas para el tren, y las últimas 20 para la prueba.
Estos son los dos predictores: un refuerzo de gradiente de mayor varianza y un predictor lineal:
g = ensemble.GradientBoostingRegressor()
l = linear_model.LinearRegression()
Aquí está la metodología sugerida en la respuesta:
g.fit(x0[: 80, :], y[: 80])
l.fit(x1[: 80, :], y[: 80])
x[:, 0] = g.predict(x0)
x[:, 1] = l.predict(x1)
>>> metrics.r2_score(
y[80: ],
linear_model.LinearRegression().fit(x[: 80, :], y[: 80]).predict(x[80: , :]))
0.940017788444
Ahora, usando el apilamiento:
x[: 80, 0] = Stacker(g).fit_transform(x0[: 80, :], y[: 80])[:, 0]
x[: 80, 1] = Stacker(l).fit_transform(x1[: 80, :], y[: 80])[:, 0]
u = linear_model.LinearRegression().fit(x[: 80, :], y[: 80])
x[80: , 0] = Stacker(g).fit(x0[: 80, :], y[: 80]).transform(x0[80:, :])
x[80: , 1] = Stacker(l).fit(x1[: 80, :], y[: 80]).transform(x1[80:, :])
>>> metrics.r2_score(
y[80: ],
u.predict(x[80:, :]))
0.992196564279
La predicción de apilamiento es mejor. Se da cuenta de que el amplificador de gradiente no es tan bueno.
Lo que describes se llama "apilamiento", que aún no se implementó en scikit-learn, pero creo que las contribuciones serían bienvenidas. Un conjunto que solo promedia llegará pronto: https://github.com/scikit-learn/scikit-learn/pull/4161
Ok, después de pasar un tiempo en Google ''apilando'' (como lo mencionó @andreas anteriormente) descubrí cómo podía hacer la ponderación en python incluso con scikit-learn. Considere lo siguiente:
Entreno un conjunto de mis modelos de regresión (como se menciona SVR, LassoLars y GradientBoostingRegressor). Luego los ejecuto a todos en los datos de entrenamiento (los mismos datos que se utilizaron para el entrenamiento de cada uno de estos 3 regresores). Obtengo predicciones de ejemplos con cada uno de mis algoritmos y guardo estos 3 resultados en un marco de datos de pandas con las columnas ''predictedSVR'', ''predictedLASSO'' y ''predictedGBR''. Y agrego la columna final en este archivo de datos que llamo ''predicho'', que es un valor de predicción real.
Luego, entreno una regresión lineal en este nuevo marco de datos:
#df - dataframe with results of 3 regressors and true output
from sklearn linear_model
stacker= linear_model.LinearRegression()
stacker.fit(df[[''predictedSVR'', ''predictedLASSO'', ''predictedGBR'']], df[''predicted''])
Entonces, cuando quiero hacer una predicción para un nuevo ejemplo, simplemente ejecuto cada uno de mis 3 regresores por separado y luego lo hago:
stacker.predict()
en las salidas de mis 3 regresores. Y obtén un resultado
El problema aquí es que estoy encontrando pesos óptimos para los regresores en promedio, los pesos serán los mismos para cada ejemplo en el que intentaré hacer la predicción.
Última respuesta, pero quería agregar un punto práctico para este tipo de enfoque de regresión apilada (que utilizo con frecuencia en mi trabajo).
Es posible que desee elegir un algoritmo para el apilador que permita positivo = verdadero (por ejemplo, ElasticNet). He descubierto que, cuando tienes un modelo relativamente más fuerte, el modelo LinearRegression () sin restricciones a menudo ajusta un mayor coeficiente positivo al más fuerte y un coeficiente negativo al modelo más débil.
A menos que realmente creas que tu modelo más débil tiene poder predictivo negativo, este no es un resultado útil. Muy similar a tener alta multi-colinealidad entre las características de un modelo de regresión regular. Causa todo tipo de efectos de borde.
Este comentario se aplica más significativamente a situaciones de datos ruidosos. Si intenta obtener un RSQ de 0.9-0.95-0.99, probablemente desee descartar el modelo que estaba obteniendo una ponderación negativa.