sábado, 16 de enero de 2021

Clasificaciones Multiclase y Multietiqueta en Machine Learning

Anteriormente hemos visto la clasificación binaria donde clasificábamos entre dos tipos, es decir un objeto dado es un objeto de nuestra clasificación, o no lo es. Y tomábamos como ejemplo el 7, un número dado es un 7 o no lo es. Pues bien en la clasificación multiclase, dado un numero tenemos que clasificarlo dentro de las clases posibles, es decir, o es un 0, o un 1, o un 2, etc.

Hay algoritmos estrictamente binarios, como la Regresión Logística, o los clasificadores SVM (Support Vector Machine), otros algoritmos como los SGD, los Random Forest y los naive Bayes son multiclasificadores. Aunque también se pueden utilizar clasificadores binarios para multiclasificación, por ejemplo podemos entrenar 10 modelos SVM, uno para clasificar los ceros, otro para los unos, otro para los doses, etc, y dada una instancia clasificarla con la que mayor puntuación obtenga en los 10 clasificadores, esto se llama estrategia OvR (One versus de rest).

Otra estrategia podría ser entrenar los modelos para distinguir entre 0 y 1, otro modelo entre 0 y 2, otro entre 1 y 2, etc esta estrategia se llama OvO (One versus one). Para esta estrategia, si tenemos N clases necesitamos N x (N-1)/2 modelos, en el caso de 0 a 9 tenemos ¡45 modelos a entrenar! Aunque tiene la ventaja de que cada modelo sólo necesita un subset para entrenarse.

SVM tiene un desempeño pobre con datasets muy grandes, en este caso es mejor utilizar OvO pues es más rápido entrenando cada trozo de subset.

Si no hemos hecho el post anterior, para que funcione el código siguiente, es necesario ejecutar antes este código:

# Python ≥3.5 is required

import sys

assert sys.version_info >= (3, 5)

 

# Scikit-Learn ≥0.20 is required

import sklearn

assert sklearn.__version__ >= "0.20"

 

# Common imports

import numpy as np

import os

 

# to make this notebook's output stable across runs

np.random.seed(42)

 

# To plot pretty figures

%matplotlib inline

import matplotlib as mpl

import matplotlib.pyplot as plt

mpl.rc('axes', labelsize=14)

mpl.rc('xtick', labelsize=12)

mpl.rc('ytick', labelsize=12)

 

#importar tensorflow y el script input_data

import tensorflow as tf

import input_data

 

#importamos el dataset mnist

from sklearn.datasets import fetch_openml

mnist = fetch_openml('mnist_784', version=1)

mnist.keys()

 

#importamos matplotlib para generar graficos

%matplotlib inline

import matplotlib as mpl

import matplotlib.pyplot as plt

some_digit = X[0]

 

#definimos X e y

X, y = mnist["data"], mnist["target"]

 

from sklearn.svm import SVC

svm_clf = SVC(gamma="auto", random_state=42)

svm_clf.fit(X_train[:1000], y_train[:1000]) # y_train

svm_clf.predict([some_digit])


En Scikit-Learn podemos lanzar un algoritmo OvO utilizando OneVsOneClassifier y OvR con OneVrRestClassifier basta con crear una instancia y pasar un clasificador como constructor. Por ejemplo el siguiente código crea un clasificador multiclase basado en SVC usando OvR.

from sklearn.multiclass import OneVsRestClassifier

ovr_clf = OneVsRestClassifier(SVC(gamma="auto", random_state=42))

ovr_clf.fit(X_train[:1000], y_train[:1000])

ovr_clf.predict([some_digit])


Devuelve 0 pues en el post anterior definimos un cero del dataset con some_digit = X[234]

len(ovr_clf.estimators_)

Devuelve 

10

Entrenar un SGDClassifier es aún más fácil

from sklearn.linear_model import SGDClassifier

sgd_clf = SGDClassifier(max_iter=1000, tol=1e-3, random_state=42)

sgd_clf.fit(X_train, y_train)

sgd_clf.predict([some_digit])

Esta vez Sckit-Learn no ha corrido OvR ni OvO porque SGD soporta clasificación multiclase el método decision_function() devuelve un valor para cada clase, vamos a ver las puntuaciones obtenidas

sgd_clf.decision_function([some_digit])

Devuelve

array([[  3816.04556036, -32544.8070744 ,  -8613.62680163,

         -4792.16972022, -10146.9498229 ,  -4250.27483221,

        -21170.5186733 , -22260.53591175,  -2863.12785007,

        -16907.17784199]])

Podemos ver que el clasificador está bastante acertado con su predicción, pues todas las puntuaciones son negativas, mientras que la del 0 es positiva. 

Ahora queremos evaluar este clasificador, para ellos utilizamos validación cruzada como siempre. Utilizaremos la función cross_val_score() para utilizar la exactitud del clasificador  SGDClassifier

from sklearn.model_selection import cross_val_score

cross_val_score(sgd_clf,X_train,y_train,cv=3,scoring="accuracy")

Devuelve

array([0.87365, 0.85835, 0.8689 ])

Esto es un 85% como mínimo en todos los testeos, si hubiésemos utilizado un clasificador aleatorio obtendríamos un 10%, no está mal, pero podemos mejorarlo simplemente escalando las entradas 

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()

X_train_scaled = scaler.fit_transform(X_train.astype(np.float64))

cross_val_score(sgd_clf, X_train_scaled, y_train, cv=3, scoring="accuracy")

Análisis de error

Primero echaremos un vistazo a la matriz de confusión, necesitamos hacer predicciones utilizando la función cross_val_predict() y luego llamando a la función matriz de confusión confusión_matrix() Las filas muestran los números etiquetados (reales) y las columnas los números predichos,

y_train_pred = cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3)

conf_mx = confusion_matrix(y_train, y_train_pred)

conf_mx

Devuelve

array([[5577,    0,   22,    5,    8,   43,   36,    6,  225,    1],

       [   0, 6400,   37,   24,    4,   44,    4,    7,  212,   10],

       [  27,   27, 5220,   92,   73,   27,   67,   36,  378,   11],

       [  22,   17,  117, 5227,    2,  203,   27,   40,  403,   73],

       [  12,   14,   41,    9, 5182,   12,   34,   27,  347,  164],

       [  27,   15,   30,  168,   53, 4444,   75,   14,  535,   60],

       [  30,   15,   42,    3,   44,   97, 5552,    3,  131,    1],

       [  21,   10,   51,   30,   49,   12,    3, 5684,  195,  210],

       [  17,   63,   48,   86,    3,  126,   25,   10, 5429,   44],

       [  25,   18,   30,   64,  118,   36,    1,  179,  371, 5107]])

Hay un montón de números, si queremos una representación gráfica podemos hacer 

plt.matshow(conf_mx, cmap=plt.cm.gray)

plt.show()


Matriz de error



Esta representación de la matriz de confusión es bastante mejor, pues vemos que las imágenes pertenecientes a la diagonal se han clasificado correctamente, es decir la mayoría de ceros se ha clasificado como cero, los unos como uno, etc. Podemos ver que el 5 es un poco más oscuro que el resto, lo que significa que hay unas pocas imágenes de 5 que se han clasificado erróneamente.

Vamos a eliminar la diagonal para centrarnos en los errores

row_sums = conf_mx.sum(axis=1, keepdims=True)

norm_conf_mx = conf_mx / row_sums

np.fill_diagonal(norm_conf_mx, 0)

plt.matshow(norm_conf_mx, cmap=plt.cm.gray)

save_fig("confusion_matrix_errors_plot", tight_layout=False)

plt.show()

 



Podemos ver claramente los diferentes tipos de errores que ha cometido el clasificador. Recuerda que las filas representan los números reales, mientras que las columnas representas los números predichos. La columna del 8 es bastante clara, lo que indica que muchos números han sido clasificados de forma errónea como ocho, sin embargo la fila 8 es bastante oscura, lo que indica que la mayoría de los ochos se han clasificado correctamente como 8.

Esta matriz de confusión no tiene porqué ser simétrica, también podemos ver que se clasifican muchos 5 como 3 y muchos 3 como 5.

Echando un vistazo a esta matriz podemos ver que nuestros esfuerzos se deben centrar en reducir los falsos ochos y tratar de solucionar los intercambios entre 3 y 5. Centrémonos en este último punto. Por ejemplo vamos a mostrar los 3 y 5 que se han clasificado mal,

cl_a, cl_b = 3, 5

X_aa = X_train[(y_train == cl_a) & (y_train_pred == cl_a)]

X_ab = X_train[(y_train == cl_a) & (y_train_pred == cl_b)]

X_ba = X_train[(y_train == cl_b) & (y_train_pred == cl_a)]

X_bb = X_train[(y_train == cl_b) & (y_train_pred == cl_b)]

 

plt.figure(figsize=(8,8))

plt.subplot(221); plot_digits(X_aa[:25], images_per_row=5)

plt.subplot(222); plot_digits(X_ab[:25], images_per_row=5)

plt.subplot(223); plot_digits(X_ba[:25], images_per_row=5)

plt.subplot(224); plot_digits(X_bb[:25], images_per_row=5)

save_fig("analisis de erorres de 3 y 5")

plt.show()



Los dos bloques de la izquierda muestran dígitos clasificados como 3 y los dos bloques de la derecha los clasificados como 5. Algunos dígitos se han clasificado erróneamente (arriba-derecha y abajo izquierda)

Clasificación Multietiqueta (Multilabel)


Hasta ahora hemos asignado cada instancia a una sola clase, pero puede ser que deseemos clasificar en múltiples clases cada instancia. Por ejemplo si deseamos clasificar diferentes personas en diferentes fotos el clasificador deberá ser entrenado para reconocer digamos a Pepe, María y Luis y de este modo si en una foto sale María y Pepe el clasificador devolverá [1,1,0] significa que en dicha foto salen Pepe y María pero no Luis. Tal sistema de clasificación se llama clasificación multietiqueta.

El siguiente código genera un array multietiqueta que contiene dos etiquetas por cada dígito. La primera etiqueta indica si el número es grande o no (7,8 o 9) y el segundo indica si es o no impar.

from sklearn.neighbors import KNeighborsClassifier

y_train_large = (y_train >= 7)

y_train_odd = (y_train % 2 == 1)

y_multilabel = np.c_[y_train_large, y_train_odd]

knn_clf = KNeighborsClassifier()

knn_clf.fit(X_train, y_multilabel)

Las siguientes líneas crean una instancia KNeighborsClassifier  que devuelve dos etiquetas.

knn_clf.predict([some_digit])

Este es el resultado

array([[False, False]])

Una aproximación es medir la puntuación F1 para cada etiqueta individual, el código a continuación calcula la puntuación media F1

ojo la ejecución de esta línea puede tardar horas

y_train_knn_pred = cross_val_predict(knn_clf, X_train, y_multilabel, cv=3)

f1_score(y_multilabel, y_train_knn_pred, average="macro")

0.976410265560605

Clasificación Multioutput

Es una generalización de la clasificación multilabel, para ilustrarlo vamos a construir un sistema que quita el ruido de las imágenes

noise = np.random.randint(0, 100, (len(X_train), 784))

X_train_mod = X_train + noise

noise = np.random.randint(0, 100, (len(X_test), 784))

X_test_mod = X_test + noise

y_train_mod = X_train

y_test_mod = X_test

some_index = 0

plt.subplot(121); plot_digit(X_test_mod[some_index])

plt.subplot(122); plot_digit(y_test_mod[some_index])

save_fig("ejemplo_de_ruido")

plt.show()

A la izquierda se muestra la imagen con ruido y a la derecha sin ruido

 

Clasificación Multioutput (Machine Learning)

Finalmente entrenaremos el clasificador para limpiar aún más esta imagen.

knn_clf.fit(X_train_mod, y_train_mod)

clean_digit = knn_clf.predict([X_test_mod[some_index]])

plot_digit(clean_digit)

save_fig("digito_limpio")

Vemos que finalmente queda bastante cercana al objetivo.

Clasificación Multioutput ML



sábado, 9 de enero de 2021

AWS in a nutshell 8: servicios administrados por desarrolladores

 8.1 Requisitos de diseño de aplicaciones en la nube

La clave para el uso exitoso de los servicios web de Amazon para una empresa consisten en elegir las soluciones adecuadas que se correspondan con sus requisitos comerciales. También significa pensar en cosas como la migración, la migración de datos o sistemas locales, incluidas las bases de datos, a la nube de AWS, para rentabilizar la inversión. O, en algunos casos, refactorizar elementos que no se pueden migrar simplemente con pocos o ningún cambio en la nube. Y por último la optimización de costes.

8.2 Elastic Beanstalk

Amazon Web Services, Elastic Beanstalk es una excelente manera de implementar, monitorear e incluso administrar actualizaciones para una aplicación de software, es decir, una aplicación web que se ejecuta en la nube de AWS, sin tener que preocuparse por la infraestructura subyacente. 

Para acceder a este servicio,desde la consola de Amazon tecleamos:

elastic beanstalk → create new aplication → environments → create one

Este es un ejemplo de lo que sería el menú para gestionar nuestra webapp1

servicios administrados por desarrolladores, Elastic Beanstalk

8.3 Puerta de enlace API (API Gateway)

Amazon API Gateway permite a los desarrolladores presentar cualquier API que hayan creado en la nube a los usuarios finales a través de un mecanismo controlado. Y ese intermediario, o mecanismo controlado, es API Gateway. Publica las API establecidas entre la solicitud de la API y la propia definición de la API. También permite la administración de esa conexión y el monitoreo, como observar la latencia de la red o la cantidad de solicitudes de API en un período corto de tiempo. API Gateway  dispone de una amplia documentación que los desarrolladores pueden utilizar para aprender a llamar y utilizar las API.

8.4 Descripción general de AWS Lambda

AWS Lambda es un tipo de solución de desarrollo sin servidor. Permite la ejecución de código. Puede crear funciones de AWS Lambda que tengan activadores que determinen cuándo se ejecutan. Lo bueno de esto es que no tiene servidor, no tenemos que preocuparnos por configurar el servidor subyacente para admitir el código.Y Amazon solo nos cobrará cuando el código se esté ejecutando y por tenerlo alojado. Lo siguiente que se debe tener en cuenta es cómo trabajar con AWS Lambda y cómo lo usamos.

Consola → lambda → Functions -Create Functions

AWS Lambda


8.5 Servicio de contenedor elástico de AWS  (ECS)

Para los desarrolladores, los contenedores de aplicaciones son un gran problema. Ya sea que esté ejecutando un componente de aplicación aislado dentro de un contenedor local o en AWS usando ECS, este es el servicio de contenedor elástico, o incluso si estamos implementando y administrando una aplicación utilizando una solución sin servidor como Elastic Beanstalk, los contenedores de aplicaciones contienen, como su nombre lo indica, los componentes, el software y la configuración necesarios para ejecutar una aplicación o un componente de una aplicación, como un microservicio. Aquí en el diagrama, podemos ver que tenemos numerosos contenedores de aplicaciones, desde la aplicación A hasta la aplicación G.

Consola → ECS → Get Started  (Cancel)  solo para verlo

Consola → ECS → Clusters → create cluster

Servicio de contenedor elástico de AWS  (ECS)

8.6 Registro de contenedores elásticos de Amazon ECR

Amazon Elastic Container Registry, o ECR, es un repositorio centralizado donde podemos organizar o agrupar las imágenes de nuestro contenedor, de forma muy similar a como podríamos organizar archivos en un disco duro colocándolos en carpetas. Todo esto es compatible con Docker y está protegido a través de la red con HTTPS, ya sea que estemos enviando imágenes a nuestro repositorio ECR o extrayendo imágenes de él.

Consola → ECR → Get started → Create repository

8.7 AWS OpsWorks

AWS OpsWorks es una solución de administración de configuración en Amazon Web Services que nos permite implementar no solo la infraestructura subyacente requerida para admitir una aplicación, sino también los componentes de la aplicación. Es realmente una solución DevOps. Se considera plataforma como servicio, o PaaS, y realmente se centra en todo el ciclo de vida de una aplicación, desde su implementación inicial, su uso, su mantenimiento y actualización continuos, hasta su inevitable desmantelamiento.

Consola → opsWorks –> botón add your First Stack

AWS OpsWorks



sábado, 2 de enero de 2021

Clasificadores binarios en Machine Learning


Vamos a tratar algoritmos de clasificación, y además vamos a hacerlo con un dataset de imágenes. En este caso vamos a utilizar números manuscritos y vamos a tratar de clasificar cada número dentro de su categoría. Es decir clasificaremos los unos manuscritos como 1, los doses como 2, etc. De modo que podamos leer números manuscritos de una foto e interpretarlos como números.
Lo primero que vamos a hacer es descargar las librerías adecuadas para tratar nuestro dataset, para ello hacemos