sábado, 19 de diciembre de 2020

Seleccionar y entrenar un modelo de Machine Learning

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)

sábado, 12 de diciembre de 2020

AWS in a nutshell 7: Administración de Costes

Exploraremos las herramientas de administración de costos para ayudar a minimizar los costos de AWS, así como la administración central de escritorios y servicios de aplicaciones. 

7.1 Explorador de costos y la GUI

AWS in a nutshell 7: Administración de Costes

AWS  Administración de Costes

7.2 Explorador de costos en CLI y Powershell

Podemos utilizar AWS CLI para acceder al explorador de costos de los servicios web de Amazon. Como su nombre indica, permite determinar de dónde proviene la mayor parte de nuestros cargos. Desglosa los cargos mensuales de computación en la nube.

Se hace por comandos

También podemos usar Microsoft PowerShell para recuperar datos del explorador de costos de AWS.

7.3 Presupuestos de costos

Podemos configurar los presupuestos de costos de AWS con un valor de umbral especificado, por ejemplo 400 €, de este modo nos asegurarnos de realizar un seguimiento de los cargos mensuales, también hay alarmas que se activan cuando excedemos el valor de umbral configurado.

Accedemos desde la consola de AWS  → [Usuario] → My Billing Dashboard → budgets y pulsamos botón crerate a budget → Cost Budget 

7.4 Presupuestos de uso

La mayoría de la gente asocia la tarea de crear presupuestos con valores monetarios. Y aunque eso es importante, y podemos crear esos presupuestos de costos en AWS, también podemos crear presupuestos basados en el uso de recursos en la nube. En nuestro caso, se tratará de cuántas horas se ejecutan las instancias EC2 en un período de un mes.

Consola → [Usuario]  → My Billing Dashboard → budgets y pulsamos botón crerate a budget → Usage Budget 

7.5 Pagos del solicitante del depósito de S3

En ocasiones, es posible que tengamos contenido almacenado en un depósito S3 que es requerido por un tercero o por una organización empresarial asociada, y es posible que deseemos cobrarles por esa transferencia de datos, ya que la necesitan y están incurriendo en cargos como resultado de ese uso.

Consola → S3  elegimos nuestro bucket S3 y pulsamos pestaña propierties → Requester Pays → Enabled Requester Pays 

7.6 Instancias puntuales

Las instancias puntuales son una excelente manera de ahorrar dinero cuando necesitamos activar potencia de cómputo, para ejecutar una tarea o algunas pruebas rápidas, pero luego ya no lo necesitamos. Lo único es que la capacidad informática no está garantizada y es por eso que no utilizaremos instancias puntuales para ninguna carga de trabajo de misión crítica.

Consola  → Spot Requests  pulsamos borón requests Spot instances  y Flexible Workloads

7.7 Instancias reservadas

Al igual que las instancias puntuales, en AWS, el uso de instancias reservadas puede permitirnos ahorrar mucho dinero en lo que respecta a la potencia informática de EC2 subyacente. Sin embargo, la diferencia es que con las instancias reservadas, tenemos un compromiso inicial de uno o hasta tres años. Nos permite obtener capacidad informática disponible con un gran descuento, pero con las instancias puntuales realmente no hay ningún compromiso.

Consola → EC2 → Reserved Instances → Purchased Reserved Instances

7.8 Amazon WorkSpaces

Amazon Workspaces es una solución VDI, Virtual Desktop Infrastructure, mediante la cual los podemos usar muchos tipos diferentes de dispositivos cliente para establecer una conexión con nuestro escritorio, que está alojado en Amazon Web Services, ya sea que ese escritorio ejecute Linux o Windows con aplicaciones. 

Nuestra infraestructura de escritorio virtual, dentro de la organización. Amazon Workspace ofrece escritorios virtuales Windows o Linux a los usuarios. Los usuarios nos conectamos a escritorios virtuales de Windows o Linux alojados en la nube de Amazon Web Services. Para configurarlo, una de las primeras cosas que debemos hacer es especificar el tipo de servicio de directorio, que queremos, por ejemplo, un servicio de directorio activo completo en la nube o un servicio de directorio activo simple, y la Asociación de VPC.

Amazon WorkSpaces

Consola → workspaces →  Botón launch workspaces

7.9 Gestión de Amazon WorkSpaces

Una vez hemos creado un Amazon WorkSpace o un escritorio virtual basado en la nube para un usuario, podemos realizar cambios en él. 

Consola → workspaces →  Botón Actions  Edit users

Gestión de Amazon WorkSpaces

7-10 Imágenes de Amazon WorkSpaces

Podemos crear imágenes personalizadas del espacio de trabajo de Amazon. 

pulsando create Image en el menú de arriba

7-11 AppStream

Amazon AppStream es una solución de aplicación mediante la cual es esencialmente una herramienta administrada. Las aplicaciones se ejecutan en Amazon Web Services en instancias, pero eso está oculto para nosotros. Todo lo que necesitamos saber, es que podemos establecer una conexión desde nuestro navegador web para transmitir o ejecutar la aplicación. Podemos estar usando un PC, un Mac, o un Chromebook, un dispositivo Android, o un dispositivo iOS; no importa. Tenemos una manera fácil de ejecutar nuestras aplicaciones, al menos las aplicaciones que tengamos a nuestra disposición. Los datos que resultan del uso de la aplicación también se pueden almacenar en AWS dependiendo de cómo lo hayamos configurado en el administrador.

Consola → AppStream → images

7-12 Flotas y pilas de AppStream

Cuando tenemos una imagen de aplicación personalizada en la que hemos colocado aplicaciones y las publicamos con el asistente de imagen, la siguiente tarea es  asignarle a una flota. 

Flotas y pilas de AppStream

Flotas y pilas de AppStream


Flotas y pilas de AppStream

7-13 AppStream en acción

Amazon AppStream permite a los usuarios ejecutar aplicaciones que están realmente alojadas en la nube de AWS a través de un navegador web habilitado para HTML 5 o, si es necesario, podemos instalar el cliente AppStream, que es necesario si deseamos utilizar cosas como más de dos monitores para ejecutar nuestra tu aplicación. 

Consola → AppStream → User Pool

sábado, 5 de diciembre de 2020

Preparación de datos para los algoritmos de Machine Learning

Vamos a aprender a preparar nuestro set de datos para que pueda ser manejado fácilmente por los algoritmos de Machine Learning, en vez de hacerlo manualmente siempre es interesante tener escritos nuestros scripts que nos ayuden en esta tarea. De este modo, podremos reproducir estas transformaciones  en otros sets de datos  o en el mismo set de datos cada vez que dispongamos de nuevos datos y nos permitirá acumular una librería de transformaciones que podremos utilizar en ocasiones futuras.

Limpieza de datos

La mayoría de algoritmos de Machine Learning ML, no trabajan bien con datos faltantes, de modo que crearemos algunas funciones que lo tengan en cuenta para hacer la correspondiente limpieza de valores faltantes. Podemos tomar varios caminos:
Eliminar las filas con datos faltantes
Eliminar los conjuntos de filas con datos faltantes (Por ej. si estamos trabajando con números de teléfono de una ciudad, en vez de eliminar sólo la fila sin información, podemos optar por eliminar el distrito entero de la ciudad en el que pertenezcan los datos faltantes)
Asignar un valor a los datos faltantes (Por ej. Ponerlos a cero o asignarles el valor medio que le corresponda, la mediana, etc.)
En nuestro ejemplo, tenemos un archivo .csv con datos planos del registro de temperaturas del observatorio de Madrid-Retiro, con datos para 1853 sólo para los meses de enero y octubre, el resto están vacíos.

Preparación de datos para los Algoritmos de Machine Learning