Ya tenemos enmarcado nuestro problema y nuestros datos adaptados para los algoritmos de Machine Learning. Ahora sólo falta alimentar nuestro modelo con estos datos.
Vamos a comenzar con el modelo más sencillo, una regresión lineal. Para ello utilizaremos la función LinearRegression de Scikit-Learn y los datasets adaptados en el post anterior datos_preparados y datos_num_tr
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(datos_preparados, datos_num_tr)
Ahora intentaremos entrenar el modelo con unas pocas instancias de nuestro dataset
# aquí pasamos el pipeline sobre unas pocas instancias de entrenamiento
algunos_datos = datos.iloc[:5]
algunos_datos_num = datos_num.iloc[:5]
algunos_datos_preparados= full_pipeline.transform(algunos_datos)
print("Predicciones:",
lin_reg.predict(algunos_datos_preparados))
con esto obtenemos una
matriz de predicciones
Ahora vamos a probar el error cuadrático sobre todo el conjunto de datos
from sklearn.metrics import
mean_squared_error
datos_predicciones = lin_reg.predict(datos_preparados)
lin_mse = mean_squared_error(datos_num_tr, datos_predicciones)
lin_rmse = np.sqrt(lin_mse)
lin_rmse
En nuestro caso devuelve un error minúsculo, pero eso es porque estamos comparando los datos con ellos mismos, en un caso real debería devolvernos errores mucho más altos.
A continuación vamos a entrenar un modelo de árbol de decisión DecisionTreeRegressor, es un modelo bastante potente para encontrar relaciones no lineales complejas en nuestros datos.
from sklearn.tree import
DecisionTreeRegressor
tree_reg =
DecisionTreeRegressor(random_state=42)
tree_reg.fit(datos_preparados,
datos_num_tr)
Una vez entrenado vamos a evaluarlo con nuestro set de entrenamiento
datos_predicciones = tree_reg.predict(datos_preparados)
tree_mse = mean_squared_error(datos_num_tr, datos_predicciones)
tree_rmse = np.sqrt(tree_mse)
tree_rmse
Nos devuelve 0 ¿significa esto que nuestro modelo es perfecto? En realidad nos indica que es un modelo bastante bueno. Pero hemos utilizado los mismos datos de entrenamiento y de validación, esa es la verdadera razón de que el error sea cero. Lo ideal es entrenar con unos datos y validarlo con un set de datos diferente.
Mejores evaluaciones con validación cruzada
Una forma de dividir nuestro set de datos para entrenamiento y validación es utilizar la función train_test_split() para partir nuestro set de datos. Aunque no es difícil nos llevará algo de trabajo adicional.
Una buena alternativa es la característica K-fold cross-validation de Scikit-Learn. El código que presentamos a continuación, divide nuestro set de datos en 10 trozos de forma aleatoria y evalúa el modelo de árbol de decisión 10 veces tomando un trozo diferente cada vez como entrenamiento y los otros 9 trozos como evaluación.
from sklearn.model_selection import cross_val_score
scores = cross_val_score(tree_reg, datos_preparados, datos_num_tr,
scoring="neg_mean_squared_error", cv=10)
tree_rmse_scores = np.sqrt(-scores)
def display_scores(scores):
print("Scores:", scores)
print("Mean:", scores.mean())
print("Standard
deviation:",
scores.std())
display_scores(tree_rmse_scores)
Esto nos devuelve la mediana y la desviación estándar. En este caso los datos son un poco extraños, 1.1634 en realidad significa 0,00011634 % con una variación de ±0,00001603 lo lógico sería que devolviera datos por ejemplo del tipo 78564.432 para representar 78,564432 % con una desviación estádard de 2546.43 es decir ±2,5464 %
Vamos al realizar el mismo ejemplo con regresión lineal
lin_scores = cross_val_score(lin_reg, datos_preparados, datos_num_tr,
scoring="neg_mean_squared_error", cv=10)
lin_rmse_scores = np.sqrt(-lin_scores)
display_scores(lin_rmse_scores)
Parece que los datos de la regresión lineal son más bajos, por lo que su ejecución será mejor que la del árbol de decisión.
Ahora vamos a probar con RandomForestRegressor, que trabaja entrenando varios árboles de decisión con subsets de datos aleatorios de nuestro dataset. Construir un modelo con otros muchos se llama ensamblaje Ensemble Learning.
from sklearn.ensemble import
RandomForestRegressor
forest_reg =
RandomForestRegressor(n_estimators=100, random_state=42)
forest_reg.fit(datos_preparados,
datos_num_tr)
datos_prediciones = forest_reg.predict(datos_preparados)
forest_mse =
mean_squared_error(datos_num_tr, datos_prediciones)
forest_rmse = np.sqrt(forest_mse)
forest_rmse
from sklearn.model_selection import cross_val_score
forest_scores = cross_val_score(forest_reg, datos_preparados, datos_num_tr, scoring="neg_mean_squared_error", cv=10)
forest_rmse_scores = np.sqrt(-forest_scores)
display_scores(forest_rmse_scores)
En este caso estamos a mitad de camino entre la regresión lineal y el árbol de decisión. Para hacer un estudio completo deberíamos probar otros muchos modelos para varias categorías de algoritmos de Machine Learning como varios Support Vectors Machines con diferentes núcleos y posiblemente una red neuronal, pero sin perder demasiado tiempo retorciendo los parámetros. El objetivo es tener una pequeña lista de entre dos y cinco modelos prometedores (los que mejores resultados de salida han arrojado).
Podemos ver más datos de nuestro dataset con
scores = cross_val_score(lin_reg, datos_preparados, datos_num_tr, scoring="neg_mean_squared_error", cv=10)
pd.Series(np.sqrt(-scores)).describe()
Es una buena práctica guardar los modelos de prueba por si tenemos que recurrir rápidamente a ellos, debemos asegurarnos de guardar los hiperparámetros utilizados para los entrenamientos, los resultados de las validaciones cruzadas y las predicciones. Esto nos permitirá realizar rápidas comparaciones con los modelos entre sí.
Para grabar estos datos podemos utilizar el módulo pickle de Python o la librería joblib que es muy eficiente serializando grandes matrices de NumPy.
Afinar con el modelo
Ahora tenemos una pequeña lista de modelos prometedores, necesitamos ajustarlos, vamos a ver algunas técnicas para ello.
Matriz de Búsqueda
Una forma de ajustar nuestro modelo sería probar a introducir diferentes hiperparámetros manualmente hasta encontrar la mejor combinación de hiperparámetros, pero sería un trabajo muy tedioso. En vez de esto, podemos utilizar la función de Scikit-Learn GridSearchcv, sólo necesitamos decirle que hiperparámetros queremos evaluar. En el siguiente ejemplo experimentaremos con las diferentes combinaciones para evaluar la función RandomForestRegressor.
from sklearn.model_selection import GridSearchCV
param_grid = [
# intenta 12 (3×4) combinaciones de hiperparametros
{'n_estimators': [3, 10, 30], 'max_features': [2, 4, 6, 8]},
# intenta 6 (2×3) combinaciones con bootstrap puesto a false
{'bootstrap': [False], 'n_estimators': [3, 10], 'max_features': [2, 3, 4]},
]
forest_reg =
RandomForestRegressor(random_state=42)
# entrena a lo largo de 5 carpetas, esto es un total de (12+6)*5=90
rondas de entrenamiento
grid_search = GridSearchCV(forest_reg, param_grid, cv=5,
scoring='neg_mean_squared_error',
return_train_score=True)
grid_search.fit(datos_preparados,
datos_num_tr)
grid_search.best_params_
En nuestro caso hemos obtenido de resultado como mejor combinación de
hiperparámetros encontrada
{'max_features': 2, 'n_estimators': 30}
Cuando no tenemos ni idea de qué valor de hiperparámetros debemos probar, podemos aproximarnos intentando potencias de 10 consecutivas (o un número más pequeño si queremos una búsqueda de grano más fino) como hemos visto en este ejemplo con el hiperparámetro n_estimators.
El parámetro param_grid le dice a Scikit-Learn que evalúe 12 combinaciones de los hiperparámetros n_estimators y max_features. (De momento no nos vamos a preocupar por el significado de estos hiperparámetros). En la primera línea y 6 en la segunda.
La función grid search explorará 18 combinaciones de valores de los hiperparámetros para RandomForestRegressor y entrenará el modelo 5 veces. En total seguirá 90 rondas de entrenamiento, lo que podría llevar bastante tiempo, pero cuando termine tendremos una la salida del estilo.
{'max_features': x, 'n_estimators': y}
Donde x e y son los mejores valores que ha evaluado.
Para encontrar directamente los mejores valores podemos hacer
grid_search.best_estimator_
Podemos tener una visión de toda la matriz de resultados para ver como se ha realizado la prueba y que valores se han obtenido.
cvres = grid_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
print(np.sqrt(-mean_score),
params)
Donde vemos que efectivamente, la salida más baja 0,9723 corresponde para los valores de x=2 e Y =30.
{'max_features': 2, 'n_estimators': 30}
Podemos visualizar una matriz con más información y mucho más amigable con esta instrucción
pd.DataFrame(grid_search.cv_results_)
Búsqueda aleatoria
La búsqueda anterior es útil si el rango de los posibles valores de los hiperparámetros es estrecho, en el caso de que estos valores se extiendan sobre un rango amplio, podría ser más interesante hacer una búsqueda aleatoria y utilizar RandomizedSearchcV, esta clase es utilizada igual que GridSearchCV pero en vez de evaluar todas las posibles conbinaciones, selecciona un valor aleatorio para cada hiperparámetro en cada iteración.
from sklearn.model_selection import
RandomizedSearchCV
from scipy.stats import randint
param_distribs = {
'n_estimators': randint(low=1, high=200),
'max_features': randint(low=1, high=8),
}
forest_reg =
RandomForestRegressor(random_state=42)
rnd_search =
RandomizedSearchCV(forest_reg, param_distributions=param_distribs,
n_iter=10, cv=5, scoring='neg_mean_squared_error', random_state=42)
rnd_search.fit(datos_preparados,
datos_num_tr)
cvres = rnd_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
print(np.sqrt(-mean_score),
params)
En este caso vemos que ha hecho un rastreo aleatorio y ha encontrado los mejores valores alrededor de max_features = 3 y n_estimators = 150 esto nos permitiría por ejemplo, hacer una matriz de búsqueda alrededor de estos valores, para afinar aún más nuestra búsqueda.
Analizar los mejores modelos y sus errores
Si queremos tener una buena visión de nuestro problema e inspeccionar que modelos son los más adecuados, podemos encontrar para una función, por ejemplo RandomForestRegressor ¿cuales son los atributos que más adecuados para obtener predicciones precisas?
feature_importances = grid_search.best_estimator_.feature_importances_
feature_importances
Ahora vamos a mostrar esos resultados junto con sus correspondientes nombres de atributo
extra_attribs = ["a", "b", "c"]
cat_encoder = full_pipeline.named_transformers_["cat"]
cat_one_hot_attribs = list(cat_encoder.categories_[0])
attributes = num_attribs + extra_attribs +
cat_one_hot_attribs
sorted(zip(feature_importances,
attributes), reverse=True)
Aquí los atributos más adecuados son los que obtienen un número más alto, En nuestro caso particular, octubre será el mes con los datos más relevantes para encontrar patrones. Esta información puede servirnos también para eliminar columnas que no nos aporten información relevante, en nuestro caso, el carácter anual (cálido, fresco, etc, parece no aportar mucha información adicional)
No hay comentarios:
Publicar un comentario