Enrique Blanco Henríquez

Enrique Blanco Henríquez

Licenciado en Ciencias Físicas y Máster en Astrofísica por la Universidad Complutense de Madrid. Investigador de Inteligencia Artificial en Core Innovation Telefónica Digital España.
AI & Data
Modelos interpretables con Python SHAP
Recientemente se publicó en este blog un interesante artículo sobre la interpretabilidad de modelos de Machine Learning. Los modelos de inteligencia artificial se han vuelto complejos, en especial con el auge del Deep Learning. Este es un problema grave, especialmente cuando los humanos son responsables de las decisiones basadas en soluciones digitales. El objetivo de hacer que los algoritmos sean accesibles, entendible e interpretables es fundamental para lo que se conoce como "Inteligencia Artificial Explicable (XAI)", y en los últimos tres años se ha convertido en un área de investigación muy activa. En este post vamos a realizar un ejemplo simple de cómo dotar de explicabilidad e interpretabilidad a un modelo de análisis de sentimiento de regresión logística lineal usando la librería de Python Shap. Tomando como ejemplo un análisis típico de sentimiento sobre un dataset de críticas de películas, veremos cómo los valores SHAP de cada palabra nos permitirán entender cuáles son las más importantes a la hora de tomar la decisión de si esa crítica es buena o mala. Con un modelo lineal el valor SHAP para la característica $latex i$ para la predicción $latex f(x)$ (asumiendo que las características son independientes) es simplemente: Dado que estamos explicando un modelo de regresión logística, las unidades de los valores SHAP estarán en el espacio log-odds. No nos vamos a complicar para realizar esta prueba de usabilidad; el conjunto de datos que utilizamos es el conjunto de datos clásico de IMDB de este enlace. Para empezar a usar la librería, dentro de nuestro entorno virtual de Python, simplemente hay que ejecutar el siguiente comando en un terminal: pip install shap Importando librerías Para esta rápida prueba, vamos a usar scikit-learn como librería desde la que haremos uso de las funciones necesarias. import sklearn from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report import numpy as np import shap from gensim.parsing.preprocessing import remove_stopwords shap.initjs() Cargando y procesando en IMDB dataset corpus, y = shap.datasets.imdb() Como ejemplo, tenemos la siguiente reseña: corpus[10] This film is one giant pant load. Paul Schrader is utterly lost in his own bad screenplay. And his directing is about as comatose as it can be without his actually having been sleepwalking during the process. <br /><br />The worst though is Woody Harrelson, whom I ordinarily like when he's properly cast... Estamos manejando un dataset perfectamente balanceado, con 12500 reseñas positivas y el mismo número de reseñas negativas. print('Posibles etiquetas para el dataset: ', np.unique(y, return_counts=True)) Posibles etiquetas para el dataset: (array([False, True]), array([12500, 12500])) Antes de empezar con la tokenización de las palabras que componen las reseñas de las películas, debemos eliminar las stopwords de nuestro dataset, sobre lo que ya hablamos con anterioridad en este blog. Estas palabras no añaden información válida a la reseña y son muy frecuentes (conjunciones, preposiciones, pronombres, verbos auxiliares, etc.) Este paso se puede abordar con un simple bucle para todas las reseñas: corpus_cleaned = list() for review in corpus: corpus_cleaned.append(remove_stopwords(review).replace('</br>', '')) Por ejemplo, la reseña corpus[10] queda de la siguiente manera: corpus_cleaned[10] This film giant pant load. Paul Schrader utterly lost bad screenplay. And directing comatose actually having sleepwalking process. </></>The worst Woody Harrelson, I ordinarily like he\'s properly cast... Partición train-test del dataset Una vez nuestro dataset está limpio de stopwords, se debe realizar la partición entrenamiento-testeo (vamos a elegir una razón 80%-20%) y se procede a su transformación TF-IDF con la TfidfVectorizer de sklearn. corpus_train, corpus_test, y_train, y_test = train_test_split(corpus_cleaned, y, test_size=0.2, random_state=42) vectorizer = TfidfVectorizer(min_df=10) X_train = vectorizer.fit_transform(corpus_train).toarray() # sparse also works but Explanation slicing is not yet supported X_test = vectorizer.transform(corpus_test).toarray() Con este paso ya tenemos la partición train-test realizada con 20,000 muestras de entrenamiento y 5,000 muestras de testeo. Cada una de esas muestras o arrays tiene 16,364 elementos. print(X_train.shape) print(X_test.shape) (20000, 16364) (5000, 16364) Entrenando un algoritmo de Regresión Logística model = sklearn.linear_model.LogisticRegression(penalty="l2", C=0.1) model.fit(X_train, y_train) LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, l1_ratio=None, max_iter=100, multi_class='auto', n_jobs=None, penalty='l2', random_state=None, solver='lbfgs', tol=0.0001, verbose=0, warm_start=False) Tras este rápido entrenamiento, el comportamiento de nuestro modelo es aceptable. Se consiguen unas precisiones y recalls en el intervalo 0.82-0.88 para los datos de validación. Para esta partición, el comportamiento es muy similar al obtenido en el train dataset. print(classification_report(y_test, model.predict(X_test))) precision recall f1-score support False 0.88 0.82 0.85 2515 True 0.83 0.88 0.85 2485 accuracy 0.85 5000 macro avg 0.85 0.85 0.85 5000 weighted avg 0.85 0.85 0.85 5000 Ahora bien, ¿en qué palabras se está fijando nuestro sencillo modelo de regresión logística para determinar si una secuencia de palabras pertenece a una reseña buena o mala? Con SHAP, visualizar esta información es muy sencillo. Explicación de nuestro modelo lineal explainer = shap.Explainer(model, X_train, feature_names=vectorizer.get_feature_names()) shap_values = explainer(X_test) Resumen de la contribución de todas las características Para obtener una descripción general de qué características son más importantes para un modelo, se pueden trazar los valores SHAP de cada característica para cada muestra. El gráfico siguiente clasifica las características por la suma de las magnitudes de los valores SHAP en todas las muestras y usa los valores SHAP para mostrar la distribución de los impactos que cada característica tiene en la salida del modelo. El color representa el valor de la característica (rojo alto, azul bajo). shap.plots.beeswarm(shap_values) Figura 1. Beeswarm summary plot de la contribución de las características más relevantes para el modelo. Podemos observar cómo altos valores de frecuencias en palabras como bad o worst tienen un impacto negativo importante en la toma de decisiones del modelo. Si esas palabras aparecen en una reseña, es bastante probable que la misma sea mala. Por el contrario, palabras como great, best o love son indicativos de que la reseña tiene altas probabilidades de ser positivas. Al final, el criterio de nuestro modelo de clasificación termina siendo bastante simple y fácilmente interpretable. Explicación de la predicción del sentimiento de la segunda reseña ind = 1 print("Reseña Positiva" if y_test[ind] else "Reseña Negativa") print(corpus_test[ind]) Reseña Positiva The idea ia short film lot information. Interesting, entertaining leaves viewer wanting more. The producer produced short film excellent quality compared short film I seen. I rated film highest possible rating. I recommend shown office managers ... Otra forma de visualizar la contribución de cada componente a la decisión final del modelo sobre una secuencia de muestra es el force plot (descrito en el paper Nature BME paper): shap.plots.force(shap_values[ind]) Figura 2. Force plot de la contribución de las palabras más relevantes a la toma de decisión. Otra librería similar es LIME, la cual es capaz de explicar cualquier clasificador, con dos o más clases. Todo lo que necesitamos es que el clasificador implemente una función que tome texto sin formato o una matriz numérica y genere una probabilidad para cada clase, también integrado para clasificadores de scikit-learn. Jugaremos con esta librería en próximos posts. Para mantenerte al día con el área de Internet of Things de Telefónica visita nuestra página web o síguenos en Twitter, LinkedIn y YouTube
27 de abril de 2021
AI & Data
Optimizando los hiperparámetros de una red neuronal con TensorBoard
Al crear modelos de Machine Learning se deben investigar múltiples parámetros como el número de nodos de las capas, diferentes optimizadores, valores de tasas de aprendizaje, distintas tasas de dropout, etc. Por lo tanto, un paso importante en el flujo de trabajo del Machine Learning es identificar los mejores hiperparámetros para el problema que se está abordando, lo que a menudo implica un gran número de pruebas. Este proceso se conoce como Optimización de hiperparámetros. Para poder realizarlo, TensorFlow 2.0 proporciona el panel de HParams en TensorBoard, que nos permitirá visualizar el comportamiento de nuestro modelo en función de los valores que definen nuestro modelo en nuestro dashboard. Hace unos meses escribimos un breve post sobre TensorBoard, un kit de herramientas de TensorFlow que permite monitorizar métricas como la precisión y la función de pérdida durante el entrenamiento de modelos profundos. En este anterior artículo investigamos, dentro de las funcionalidades más básicas de TensorBoard, cómo se podía monitorizar las métricas de un modelo de Machine Learning, la evolución de los pesos y sesgos de nuestra arquitectura, visualizar el grafo de nuestro modelo e incluso realizar un sencillo análisis de matriz de confusión de nuestro modelo una vez estuviera entrenado. En el presente post vamos a probar a encontrar los hiperparámetros de una red neuronal recurrente dedicada a multivariate time series forecasting. Para más detalles sobre cómo abordar un problema de este tipo, puede leer el siguiente enlace. Figura 1. Grafo del modelo recurrente usado para la predicción de time series en el dashboard de TensorBoard. Definiendo los valores de los hiperparámetros a probar Con TensorBoard, se puede realizar un seguimiento de la precisión y la pérdida del modelo en cada época; y también con diferentes valores de hiperparámetros. La precisión de seguimiento para diferentes los diferentes valores nos ayudará a ajustar el modelo más rápido. A continuación, se muestra un ejemplo de cómo listar los intervalos de valores para cada hiperparámetro. # ## Create hyperparameters HP_INPUT_WIDTH = hp.HParam('num_input_width', hp.Discrete([12, 24, 48, 96])) HP_OUTPUT_WIDTH = hp.HParam('num_output_width', hp.Discrete([6, 12, 24, 48])) HP_RNN_UNITS = hp.HParam('num_rnn_units', hp.Discrete([30, 40, 60, 80, 100])) HP_DROPOUT = hp.HParam('dropout', hp.Discrete([0.05, 0.1, 0.2])) HP_OPTIMIZER = hp.HParam('optimizer', hp.Discrete(['adam'])) HP_LEARNING_RATE = hp.HParam('learning_rate', hp.Discrete([0.001])) METRIC_ACCURACY = 'accuracy' Creando, compilando y ajustando el modelo Suponiendo que ya tenemos realizada la partición de nuestro dataset de time series en train, test y/o validación y que ese dataset está convenientemente preprocesado, definimos la arquitectura del modelo (haciendo uso de Keras) junto con algunos callbacks como EarlyStopping o el propio callback de TensorBoard que nos permitirá loguear el desempeño de nuestra red para cada combinación posible de hiperparámetros. def create_model(hparams): # Define model architecture input_shape = (hparams[HP_INPUT_WIDTH], num_features) inp = tf.keras.layers.Input(input_shape) x = tf.keras.layers.LSTM(hparams[HP_RNN_UNITS], return_sequences=False)(inp) x = tf.keras.layers.Flatten()(x) x = get_dropout(x, p=hparams[HP_DROPOUT], mc=True) # Shape => [batch, 1, out_steps*features] x = tf.keras.layers.Dense(hparams[HP_OUTPUT_WIDTH]*num_features, kernel_initializer=tf.keras.initializers.VarianceScaling())(x) out = tf.keras.layers.Reshape([hparams[HP_OUTPUT_WIDTH], num_features])(x) # Build model model = tf.keras.models.Model(inputs=inp, outputs=out) # Setting the optimizer and learning rate optimizer = hparams[HP_OPTIMIZER] learning_rate = hparams[HP_LEARNING_RATE] if optimizer == 'adam': optimizer = tf.optimizers.Adam(learning_rate=learning_rate) else: raise ValueError("Unexpected optimizer name: %r" % (optimizer_name,)) # Define Early Stopping early_stopping = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, mode='min') # Define path where TensorBoard logs will be stored log_dir = os.path.join(tensorboard_path, 'lstm_verbose', datetime.datetime.now().strftime("%Y%m%d-%H%M%S")) with tf.summary.create_file_writer(log_dir).as_default(): hp.hparams_config( hparams= [ HP_INPUT_WIDTH, HP_OUTPUT_WIDTH, HP_RNN_UNITS, HP_DROPOUT, HP_OPTIMIZER, HP_LEARNING_RATE ], metrics=[hp.Metric(METRIC_ACCURACY, display_name='accuracy')], ) # We compile the model model.compile(loss=tf.losses.MeanSquaredError(), optimizer=optimizer, metrics=[ tf.metrics.MeanAbsoluteError(), tf.keras.metrics.MeanSquaredError(), 'accuracy' ]) # Define a Window Generator to feed the neural network multi_window = WindowGenerator(input_width=hparams[HP_INPUT_WIDTH], label_width=hparams[HP_OUTPUT_WIDTH], shift=hparams[HP_OUTPUT_WIDTH]) # Fitting the model history = model.fit(multi_window.train, epochs=max_epochs, validation_data=multi_window.val, callbacks=[early_stopping,\ tf.keras.callbacks.TensorBoard(log_dir), hp.KerasCallback(log_dir, hparams)]) return history.history['val_accuracy'][-1] Para cada ejecución del modelo, necesitamos registrar el resumen de hparams con el hiperparámetro y la precisión de las épocas finales. Necesitamos convertir la precisión de validación de la última época en un valor escalar. def run(run_dir, hparams): with tf.summary.create_file_writer(run_dir).as_default(): hp.hparams(hparams) # record the values used in this trial accuracy = create_model(hparams) # converting to tf scalar accuracy = tf.reshape(tf.convert_to_tensor(accuracy), []).numpy() tf.summary.scalar(METRIC_ACCURACY, accuracy, step=1) Ejecutando el modelo con diferentes valores de hiperparámetros En este paso se usa Grid Search para probar todas las combinaciones posibles. session_num = 0 for num_input_units in HP_INPUT_WIDTH.domain.values: for num_output_units in HP_OUTPUT_WIDTH.domain.values: for num_rnn_units in HP_RNN_UNITS.domain.values: for dropout_rate in HP_DROPOUT.domain.values: for optimizer in HP_OPTIMIZER.domain.values: for learning_rate in HP_LEARNING_RATE.domain.values: hparams = { HP_INPUT_WIDTH: num_input_units, HP_OUTPUT_WIDTH: num_output_units, HP_RNN_UNITS: num_rnn_units, HP_DROPOUT: dropout_rate, HP_OPTIMIZER: optimizer, HP_LEARNING_RATE: learning_rate, } run_name = "run-%d" % session_num print('--- Starting trial: %s' % run_name) print({h.name: hparams[h] for h in hparams}) run(os.path.join(tensorboard_path, 'lstm_mc_hgs_verbose', datetime.datetime.now().strftime("%Y%m%d-%H%M%S")), hparams) session_num += 1 Visualizando los resultados de la búsqueda Ejecutamos en un terminal (tras haber activado nuestro entorno virtual de Python) y ejecutamos el siguiente comando: tensorboard --logdir "/logs/lstm_verbose/" --port 6006 Si nos vamos a http://localhost:6006/ podremos acceder al dashboard de TensorBoard. Si se ha logueado todo correctamente, en el dashboard se tendrá disponible una pestaña llamada HParams, que muestra las ejecuciones individuales para cada combinación de hiperparámetros ordenadas de acuerdo a los valores de METRIC_ACCURACY especificada arriba. Figura 2. Resumen de experimentos realizados con estrategia de Grid Search en HParams con TensorBoard. Figura 3. Parallel Coordinates View de HParams en el dashboard de TensorBoard. Figura 4. Scatter Plot Matrix View de HParams en el dashboard de TensorBoard. Como se ve en la figura anterior, el panel izquierdo del panel proporciona capacidades de filtrado que están activas en todas las vistas en el panel de HParams: Filtrar qué hiperparámetros/métricas se muestran en el panel; Filtrar qué valores de hiperparámetros/métricas se muestran en el panel; Filtrar por estado de ejecución (en ejecución o terminadas); Ordenar por hiperparámetro/métrica en la vista de tabla; Número de grupos de sesiones para mostrar (útil para el rendimiento cuando hay muchos experimentos). El panel de HParams tiene tres vistas diferentes, con diversa información útil: La vista de tabla (Figura 2) enumera las ejecuciones, sus hiperparámetros y sus métricas. La pestaña de coordenadas paralelas (Figura 3) muestra cada tramo como una línea que pasa por un eje para cada hiperparámetro y métrica. La vista de gráfico de dispersión (Figura 4) muestra gráficos que comparan cada hiperparámetro/métrica con cada métrica. Esto puede ayudar a identificar correlaciones. Conclusión Os invitamos a intentar realizar un experimento similar al expuesto en este post con la red convolucional que construimos en Supervisa tu entrenamiento de redes neuronales con TensorBoard para Fashion MNIST, probando a modificar el número de filtros, el tamaño de kernel, el número de unidades en las capas densas finales y/o distintos optimizadores distintos de Adam. Para más detalle sobre Hyperparameter Tuning, puede consultar el tutorial de TensorFlow.
24 de marzo de 2021
AI & Data
Clustering de series temporales con Python
La proliferación y la ubicuidad de los datos con dependencia temporal en un amplio abanico de disciplinas genera un interés sustancial en el análisis y la extracción de series temporales. La agrupación o clustering es uno de los métodos de extracción de datos más populares, no sólo por su poder de exploración, sino también como paso previo al procesamiento de otras técnicas. tslearn es una librería de Machine Learning de Python para series temporales que ofrece herramientas para el preprocesamiento y la extracción de características, así como modelos dedicados para clustering, clasificación y regresión. En esta rápida prueba de concepto, vamos a realizar una tarea de clustering de un dataset de una sola variable bien conocido. Importando librerías Para empezar, importamos las librerías que vamos a usar: import tslearn import numpy as np import time import matplotlib.pyplot as plt Con TimeSeriesScalerMeanVariance procesaremos las time series para que tengan una media nula y una desviación típica igual a 1. from tslearn.preprocessing import TimeSeriesScalerMeanVariance Obteniendo un dataset Afortunadamente, tslearn nos ofrece un gran número de series temporales con las que podemos empezar a trastear. Podemos acceder a los UCR/UEA time series datasets de la siguiente manera. from tslearn.datasets import UCR_UEA_datasets En el siguiente enlace se puede consultar la extensa lista de datasets de series temporales a la que podemos acceder. Como se puede ver, todos estos datasets están convenientemente etiquetados, por lo que podremos comprobar la bondad de nuestro modelos una vez que hayamos terminado de clusterizar las secuencias. En este artículo vamos a trabajar con el dataset ECG5000. Este dataset contiene 20 horas de registros de electrocardiogramas (ECGs). En total se cuentan con 5,000 pares de ECGs y etiquetas. Fue publicado en "Goldberger AL, Amaral LAN, Glass L, Hausdorff JM, Ivanov PCh, Mark RG, Mietus JE, Moody GB, Peng C-K, Stanley HE. PhysioBank, PhysioToolkit, and PhysioNet: Components of a New Research Resource for Complex Physiologic Signals. Circulation 101(23)". Figura 1. Muestra de electrocardiogramas de ECG5000. Fuente. Es muy simple acceder a las particiones de entrenamiento y testeo del dataset que nos interesa. X_test, y_test, X_train, y_train = UCR_UEA_datasets().load_dataset("ECG5000") Las etiquetas del dataset tienen el siguiente significado: 1 = N - Normal 2 = SVEB - Supraventricular ectopic beats 3 = VEB - Ventricular ectopic beats 4 = Fusion beat 5 = Unknown beat Entendiendo el dataset Lo primero que hay que comprobar es la distribución de etiquetas en el dataset. labels, counts = np.unique(y_train, return_counts=True) _labels = ['Normal', 'SVEB', 'VEB', 'Fusion', 'Unknown'] plt.div(figsize=(8, 6)) plt.bar(_labels, counts, align='center') plt.gca().set_xticks(_labels) plt.grid() plt.ylabel('No. samples') plt.xlabel('Beat type') plt.show() Figura 2. Histograma de clases en ECG5000. Como se puede observar, el dataset está muy desbalanceado. Hay muy pocas muestras de las clases y_train > 2 en comparación con los latidos normales o SVEB. Por ello, para simplificar el análisis, vamos a partir el dataset en latidos normales (y_train | y_test = 0) y latidos anómalos (y_train | y_test = 1) y_test[y_test == 1] = 0 y_test[y_test > 1] = 1 y_train[y_train == 1] = 0 y_train[y_train > 1] = 1 El número de muestras pertenecientes a cada clase sigue estando desbalanceado, aunque no de manera tan extrema como antes. Esto nos permitirá clusterizar en dos grupos con mejores resultados. labels, counts = np.unique(y_train, return_counts=True) _labels = ['Normal', 'Anomalous'] plt.div(figsize=(8, 6)) plt.bar(_labels, counts, align='center') plt.gca().set_xticks(_labels) plt.grid() plt.ylabel('No. samples') plt.xlabel('Beat type') plt.show() Figura 3. Histograma de clases adaptadas para ECG5000. Preprocesado del dataset y visualización Debemos escalar nuestras series temporales para poder obtener buenos resultados de clustering. X_train = TimeSeriesScalerMeanVariance().fit_transform(X_train) X_test = TimeSeriesScalerMeanVariance().fit_transform(X_test) Como vemos, contamos con 4500 secuencias de 140 elementos de una sola variable para entrenar y 500 secuencias de igual longitud para testear. print(X_train.shape) print(X_test.shape) (4500, 140, 1) (500, 140, 1) size = X_train.shape[1] n_classes = len(set(y_train)) plt.div(figsize=(10, 10)) for i, cl in enumerate(set(y_train)): plt.subplot(n_classes, 1, i + 1) for ts in X_train[y_train == cl]: if cl == 0: plt.plot(ts.ravel(), color="green" , alpha=.15) else: plt.plot(ts.ravel(), color="red" , alpha=.15) plt.xlim(0, size - 1) plt.grid() plt.suptitle("Training time series") plt.show() Figura 4. ECG5000 Training dataset (normal - green ; anomalous - red) plt.div(figsize=(10, 10)) for i, cl in enumerate(set(y_train)): plt.subplot(n_classes, 1, i + 1) for ts in X_test[y_test == cl]: if cl == 0: plt.plot(ts.ravel(), color="green" , alpha=.15) else: plt.plot(ts.ravel(), color="red" , alpha=.15) plt.xlim(0, size - 1) plt.grid() plt.suptitle("Test dataset time series") plt.show() Figura 5. ECG5000 Test dataset (normal - green ; anomalous - red) A simple vista, únicamente en base a la forma, es sencillo distinguir entre los latidos de un individuo sano y los de un paciente con una patología (sin especificar). Clustering de secuencias Vamos a probar dos algoritmos de clustering: K-Means y K-Shape. Para el caso de K-Means, vamos a centrarnos únicamente en la métrica "euclidean". Hay otras dos alternativas para este hiperparámetro: "dtw" y "softdtw". Se deja como ejercicio al lector, si éste lo desea. Aviso: los tiempos de ejecución en estos dos casos son ostensiblemente más largos. from tslearn.clustering import TimeSeriesKMeans, KShape, silhouette_score Time Series K-Means para dos clusters start_time = time.time() km_euc = TimeSeriesKMeans(n_clusters=2, verbose=2, n_init=10, metric="euclidean").fit(X_train) labels_euc = km_euc.labels_ print("--- %s seconds ---" % (time.time() - start_time)) ... --- 9.683111190795898 seconds --- Time Series K-Shape para dos clusters start_time = time.time() ks = KShape(n_clusters=2, verbose=True, n_init=10).fit(X_train) labels_kshape = ks.labels_ print("--- %s seconds ---" % (time.time() - start_time)) ... --- 91.33036160469055 seconds --- Las silhouette scores de ambas soluciones son muy similares: silhouette_score(X_train, labels_euc, metric="euclidean") 0.45911902366303975 silhouette_score(X_train, labels_kshape, metric="euclidean") 0.4550914094152607 Resultados de los agrupamientos Dado que contamos con las etiquetas del dataset, podemos comprobar cómo de bien hemos agrupado los electrocardiogramas entre pacientes sanos o con patologías. from sklearn.metrics import accuracy_score print('Training dataset accuracy for TimeSeriesKMeans clustering with euclidean metric: ', accuracy_score(y_train, labels_euc)) print('Training dataset accuracy for KShape clustering: ', accuracy_score(y_train, labels_kshape)) Training dataset accuracy for TimeSeriesKMeans clustering with euclidean metric: 0.9411 Training dataset accuracy for KShape clustering: 0.9391 print('Test dataset accuracy for TimeSeriesKMeans clustering with euclidean metric: ', accuracy_score(y_test, km_euc.predict(X_test))) print('Test dataset accuracy for KShape clustering: ', accuracy_score(y_test, ks.predict(X_test))) Test dataset accuracy for TimeSeriesKMeans clustering with euclidean metric: 0.958 Test dataset accuracy for KShape clustering: 0.954 La clasificación parece satisfactoria. Como vemos, tenemos una precisión cercana al 96% en el test dataset. Vamos a analizar, con matrices de confusión, los errores de Tipo I y Tipo II en los que nuestros modelos de clustering está incurriendo. Estos conceptos ya se trataron en los siguientes posts: Video Post #11: Tipos de error en Machine Learning y Tipos de error en Machine Learning, ¿los conoces?. from sklearn.metrics import confusion_matrix confusion_matrix(y_train, labels_euc) NormalAnomalousNormal256364Anomalous2011672 Tabla 1. Training dataset TimeSeriesKMeans confusion matrix. confusion_matrix(y_train, labels_kshape) NormalAnomalousNormal254582Anomalous1921681 Tabla 2. Training dataset KShape confusion matrix. confusion_matrix(y_test, km_euc.predict(X_test)) NormalAnomalousNormal2839Anomalous12196 Tabla 3. Test dataset TimeSeriesKMeans confusion matrix. confusion_matrix(y_test, ks.predict(X_test)) NormalAnomalousNormal28311Anomalous12196 Tabla 4. Test dataset KShape confusion matrix. El desempeño de ambos algoritmos es muy parecido. Dado que estamos trabajando con datos clínicos de pacientes, lo que deseamos es tener el menor error de tipo I posible. Es decir, debemos minimizar la posibilidad de que a una persona con una patología cardiaca no se le diagnostique correctamente, considerándolo una persona sana. Para ambas aproximaciones, esto sólo ocurre en 12 muestras en el test dataset. Representando los clusters y sus centroides Con las siguientes líneas de código podremos representar los distintos grupos de series temporales junto con los centroides que los definen. Estos centroides se consiguen con model.cluster_centers_. def plot_groups(model): if model == 'kmeans-euclidean': model = km_euc elif model == 'kshape': model = ks else: sys.exit('Please, provide a valid model string.') labels = model.labels_ plt.div(figsize=(10, 10)) for yi in range(2): plt.subplot(2, 1, 1 + yi) for xx in X_train[labels == yi]: plt.plot(xx.ravel(), "k-", alpha=.15) plt.plot(model.cluster_centers_[yi].ravel(), "r-") plt.xlim(0, size-1) if yi == 0: plt.title("Normal") else: plt.title("Anomalous") plt.grid() plt.tight_layout() plt.show() plot_groups(model='kmeans-euclidean') Figura 6. Secuencias del training dataset para latidos normales (arriba) y anómalos (abajo) para TimeSeriesKMeans. En rojo, se observan los centroides de los dos clusters. def compare_cluster_centers(): plt.div(figsize=(10, 10)) for yi in range(2): plt.subplot(2, 1, 1 + yi) plt.plot(km_euc.cluster_centers_[yi].ravel(), "-", alpha=1., label='K-Means euclidean') #plt.plot(km_softdtw.cluster_centers_[yi].ravel(), "-", alpha=0.5, label='K-Means softdtw') plt.plot(ks.cluster_centers_[yi].ravel(), "-", alpha=.5, label='K-Shape') if yi == 0: plt.title("Normal") else: plt.title("Anomalous") plt.xlim(0, size-1) plt.grid() plt.legend() plt.tight_layout() plt.show() Figura 7. Secuencias del training dataset para latidos normales (arriba) y anómalos (abajo) para KShape. En rojo, se observan los centroides de los dos clusters. Conclusiones Los centroides de los clusters son bastante similares comparando entre los dos algoritmos usados, tal y como se preveía dadas las tasas de acierto y error mostradas por las matrices de confusión. def compare_cluster_centers(): plt.div(figsize=(10, 10)) for yi in range(2): plt.subplot(2, 1, 1 + yi) plt.plot(km_euc.cluster_centers_[yi].ravel(), "-", alpha=1., label='K-Means euclidean') plt.plot(ks.cluster_centers_[yi].ravel(), "-", alpha=.5, label='K-Shape') if yi == 0: plt.title("Normal") else: plt.title("Anomalous") plt.xlim(0, size-1) plt.grid() plt.legend() plt.tight_layout() plt.show() compare_cluster_centers_classes() Figura 8. Perfiles de los centroides para latidos normales (arriba) y anómalos (abajo) con TimeSeriesKMeans (azul) y KShape (amarillo). def compare_cluster_centers_classes(): plt.div(figsize=(10, 6)) plt.plot(km_euc.cluster_centers_[0].ravel(), "-", c='green', alpha=.75, label='Normal') plt.plot(km_euc.cluster_centers_[1].ravel(), "-", c='red', alpha=.75, label='Anomalous') plt.xlim(0, size-1) plt.title("Cluster Centers comparison per classes") plt.tight_layout() plt.legend() plt.grid() plt.show() compare_cluster_centers_classes() Figura 9. Diferencias en el perfil de los centroides para TimeSeriesKMeans. Resulta muy interesante ver cómo con unas pocas líneas de código en Python, se puede obtener un perfil promedio de latido que nos permite diferenciar entre un ritmo cardíaco normal y un individuo con una patología. Otra opción alternativa a la clusterización de series temporales de este tipo podría abordarse a través de una reducción de dimensionalidad con PCA. Por ejemplo, se podría convertir el training dataset en un pandas dataframe de 4500 filas y 140 columnas y, sobre ese array, realizar un Principal Component Analysis. Con 2 componentes mantendríamos un 65% de varianza y con 3 componentes subiríamos hasta el 75%. Con esta aproximación y también de manera muy rápida, podríamos encontrar patrones comunes entre secuencias muy bien agrupados. Figura 10. Scatterplot de las primeras 2 componentes tras realizar PCA en el training dataset de ECG5000. Figura 11. Scatterplot 3D de las primeras 3 componentes tras realizar PCA en el training dataset de ECG5000. Os invitamos a probar a usar tslearn en problemas de clasificación, clustering y regresión de series temporales con éste y otros de los muchos datasets disponibles.
13 de enero de 2021
AI & Data
Supervisa tu entrenamiento de redes neuronales con TensorBoard
TensorBoard es una herramienta que permite monitorizar métricas como la precisión y la función de pérdida durante el entrenamiento de modelos profundos tanto en el conjunto de entrenamiento como en los conjuntos de validación y/o testeo. Esta herramienta también resulta bastante fácil de usar y se puede visualizar e interactuar con ella a través del navegador. A continuación, resumimos algunos de los aspectos que se van a cubrir en este artículo: Monitorización de escalares durante el entrenamiento; Visualización de imágenes; Comprobación de los pesos de los modelos y los sesgos; Visualización de la arquitectura del modelo; Análisis de la matriz de confusión del modelo. Con TensorBoard ya instalado, podemos usarlo en un Notebook Jupyter, en Google Colab o en el entorno virtual de vuestro proyecto. Nosotros trabajaremos en un Jupyter Notebook en Colab. %load_ext tensorboard Una vez cargada la extensión de TensorBoard, establecemos la ruta del directorio de logs. Aquí es donde el TensorBoard almacenará todos los registros durante el entrenamiento. En el caso de que se necesite recargar la extensión de TensorBoard, el comando indicado a continuación resultará útil. %reload_ext tensorboard Cargando un dataset Vamos a empezar a explorar TensorBoard con un ejemplo de clasificación de imágenes simple haciendo uso de Deep Learning. Usaremos uno de los datasets más sencillos y conocidos que hay: fashion-MNIST y trabajaremos con Keras. Se nos ofrecen imágenes de 28x28 de 10 tipos distintos de prendas en color blanco y negro. import numpy as np import random import matplotlib.pyplot as plt import os import datetime import tensorflow as tf (X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data() X_train, X_test = X_train / 255., X_test / 255. Tamaño del training dataset: (60000, 28, 28) Tamaño del test dataset: (10000, 28, 28) Tamaño de cada imagen: (28, 28) Número de clases: 10 Si le echamos un vistazo al dataset, podemos ver que el mismo está formado por un conjunto de prendas: for i in range(0, 9): plt.subplot(331+i) # plot of 3 rows and 3 columns plt.imshow(X_train[i], cmap='gray') # gray scale Cada entero indicado en las etiquetas del dataset corresponde a las siguientes categorías: "top", "trouser", "pullover", "dress", "coat", "sandal", "shirt", "sneaker", "bag", "ankle boot" Figura 1. Muestra de 9 imágenes del Fashin-MNIST dataset. Necesitaremos hacer un reshape de las muestras de entrenamiento y testeo para poder alimentar un modelo convolucional de clasificación. Veremos a continuación cómo construirlo. Creando un modelo muy sencillo con Keras Vamos a crear un modelo convolucional simple que tenga 10 unidades de salida con activación softmax. No es nuestra intención conseguir precisiones muy altas en este artículo. # Model model = tf.keras.models.Sequential() # Add convolution 2D model.add(tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(tf.keras.layers.MaxPooling2D((2, 2))) model.add(tf.keras.layers.Conv2D(64, kernel_size=(3, 3), activation='relu')) model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2))) model.add(tf.keras.layers.Conv2D(128, kernel_size=(3, 3), activation='relu')) model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(128, activation='relu')) model.add(tf.keras.layers.Dense(10, activation='softmax')) model.summary() Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 26, 26, 32) 320 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 13, 13, 32) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 11, 11, 64) 18496 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 3, 3, 128) 73856 _________________________________________________________________ flatten (Flatten) (None, 1152) 0 _________________________________________________________________ dense (Dense) (None, 128) 147584 _________________________________________________________________ dense_1 (Dense) (None, 10) 1290 ================================================================= Total params: 241,546 Trainable params: 241,546 Non-trainable params: 0 Compilamos el modelo model.compile(optimizer='Adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) Cómo usar el callback de TensorBoard El siguiente paso es definir el callback TensorBoard durante el método de ajuste del modelo. Esta llamada es responsable de registrar eventos como Histogramas de Activación, Gráficos de Resumen de Métricas, Perfiles y Visualizaciones de Gráficos de Entrenamiento. from tensorflow.keras.callbacks import TensorBoard Ahora podemos crear el callback del TensorBoard y especificar el directorio de registro usando log_dir. El callback de TensorBoard también toma otros parámetros: histogram_freq es la frecuencia con la que se calculan los histogramas de activación y peso para las capas del modelo. Poner esto a 0 significa que los histogramas no serán calculados. Para que esto funcione, hay que definir los datos de validación. write_graph determina si el gráfico será visualizado en TensorBoard. write_images cuando se ajusta a True, los pesos del modelo se visualizan como una imagen en TensorBoard. update_freq determina cómo las pérdidas y las métricas se escriben en el TensorBoard. Cuando se establece en un número entero, digamos 100, las pérdidas y métricas se registran cada 100 lotes. Cuando se establece en un bath, las pérdidas y métricas se establecen después de cada lote. Cuando se establece en la época se escriben después de cada época. profile_batch determina qué batches serán perfilados. Por defecto, se perfila el segundo batch. También se puede establecer, por ejemplo de 5 y a 10, para perfilar los lotes de 5 a 10, es decir, profile_batch='5,10′ . Si se establece profile_batch en 0, se desactiva la creación de perfiles. embeddings_freq la frecuencia con la que se visualizarán las capas de embedding. Ponerlo a cero significa que esos embeddings no se visualizarán. Podríamos también definir un callback de checkpoint en el caso de que queramos almacenar el aprendizaje de nuestro modelo al final de cada época. callback = [ TensorBoard(log_dir=os.path.join("./logs", datetime.datetime.now().strftime("%Y%m%d-%H%M%S")), histogram_freq=1, write_graph=True, write_images=True, update_freq='epoch', embeddings_freq=1) ] El siguiente paso es ajustar el modelo pasándole el callback. tf.keras.backend.clear_session() model.fit(X_train, y_train, epochs=15, batch_size=32, validation_data=(X_test, y_test), callbacks=callback) A partir de la época 5 empezamos a sufrir overfitting, pero lo hacemos con una precisión sobre el conjunto de validación del 90.47%. Epoch 5/15 1875/1875 [==============================] - 6s 3ms/step - loss: 0.2050 - accuracy: 0.9225 - val_loss: 0.2538 - val_accuracy: 0.9047 En este punto deberíamos haber parado nuestro entrenamiento, de haber implementado un callback de EarlyStopping y otro de ModelCheckpoint. Visualizando la evolución de los escalares durante el entrenamiento Ejecutando en nuestro Jupyter: %tensorboard --logdir logs Video 1. Explorando escalares, pesos, grafos y distribuciones con TensorBoard. Escalares TensorBoard La pestaña Scalars muestra los cambios en la función pérdida y las métricas durante las épocas tanto en entrenamiento como en validación. Se puede usar para rastrear otros valores escalares como la tasa de aprendizaje y la velocidad de entrenamiento. Imágenes de TensorBoard También podemos analizar las imágenes que muestran los pesos de la red. La barra deslizante muestra los pesos a lo largo de todas las épocas. Grafos de TensorBoard Además, se nos permite comprobar el grafo del modelo, analizando la relación entre capas y la topología global de la red. Distribuciones e Histogramas en TensorBoard Podemos consultar las distribuciones e histogramas de los tensores, tanto en pesos como en bias para cada época. Embedding Projector de TensorBoard Puede utilizar el proyector de TensorBoard para visualizar cualquier representación vectorial. Por ejemplo, Word Embeddings o imágenes. Sobre esta funcionalidad, que podéis encontrar bajo en menú desplegable Inactivo, ya hablamos en este artículo anterior. AI OF THINGS Analiza tus datos con Embedding Projector de Tensorflow 26 de febrero de 2020 Ejecutando TensorBoard en remoto Cuando se está trabajando con TensorBoard en un servidor remoto, se puede usar SSH tunneling para dirigir un puerto de ese servidor a un puerto en tu máquina local (por ejemplo, el 6006). Esto se podría hacer de la siguiente manera: ssh -L local_port:localhost:server_port your_username@my_server_ip De esta forma, podremos ejecutar sin ningún problema en nuestro servidor remoto, suponiendo que nuestro puerto en local es el local_port 8000 y el puerto en remoto expuesto es el server_port 8880: tensorboard --logdir=logs --port 8880 De forma que, en nuestro navegador, podremos visualizar TensorBoard en: http://localhost:8000 Representando imágenes de entrenamiento en TensorBoard Además de visualizar los tensores de imagen, también puede visualizar imágenes de nuestro dataset en TensorBoard. Para ilustrar eso, debe convertir los tensores de Fashion-MNIST en imágenes usando numpy. Después de eso, necesitas usar tf.summary.image para trazar las imágenes en Tensorboard. Sabemos que las imágeson son de un tamaño de 28 por 28. Debemos especificar que sólo tenemos un canal, pues las imágenes vienen en escala de grises. Luego, usa file_write para escribir las imágenes en TensorBoard. En este ejemplo, las imágenes en el índice 0 a 20 se escribirán en TensorBoard. logdir = "./logs/train_data/" file_writer = tf.summary.create_file_writer(logdir) with file_writer.as_default(): images = np.reshape(X_train[0:20], (-1, 28, 28, 1)) tf.summary.image("20 Clothes", images, max_outputs=25, step=0) class_names = ["top", "trouser", "pullover", "dress", "coat", "sandal", "shirt", "sneaker", "bag", "ankle boot"] logdir = "logs/plots/" file_writer = tf.summary.create_file_writer(logdir) A continuación, creamos una cuadrícula que contenga las imágenes. En este caso, la cuadrícula tendrá 25 prendas. def image_grid(): div = plt.div(figsize=(12,8)) for i in range(25): plt.subplot(5, 5, i + 1) plt.xlabel(class_names[y_train[i]]) plt.xticks([]) plt.yticks([]) plt.grid(False) plt.imshow(X_train[i].reshape(28,28), cmap=plt.cm.coolwarm) return div div = image_grid() Figura 2. Cuadrícula de muestras del dataset. Ahora convertimos las muestras en una sola imagen para su visualización. def plot_to_image(div): buf = io.BytesIO() plt.savefig(buf, format='png') plt.close(div) buf.seek(0) clothes = tf.image.decode_png(buf.getvalue(), channels=4) clothes = tf.expand_dims(digit, 0) return clothes with file_writer.as_default(): tf.summary.image("Fashion MNIST", plot_to_image(div), step=0) Vídeo 2. Representando muestras del dataset en TensorBoard. Representando la matriz de confusión en TensorBoard Se puede generar la matriz de confusión para todas las épocas. Primero, definimos una función que devolverá una figura de Matplotlib que contenga la matriz de confusión. import itertools def plot_confusion_matrix(cm, class_names): div = plt.div(figsize=(8, 8)) plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Accent) plt.title("Confusion matrix") plt.colorbar() tick_marks = np.arange(len(class_names)) plt.xticks(tick_marks, class_names, rotation=45) plt.yticks(tick_marks, class_names) cm = np.around(cm.astype('float') / cm.sum(axis=1)[:, np.newaxis], decimals=2) threshold = cm.max() / 2. for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): color = "white" if cm[i, j] > threshold else "black" plt.text(j, i, cm[i, j], horizontalalignment="center", color=color) plt.tight_layout() plt.ylabel('True label') plt.xlabel('Predicted label') return div !rm -rf "./logs/*" logdir = "logs" file_writer_cm = tf.summary.create_file_writer(logdir) from sklearn import metrics class_names = ["top", "trouser", "pullover", "dress", "coat", "sandal", "shirt", "sneaker", "bag", "ankle boot"] def log_confusion_matrix(epoch, logs): predictions = model.predict(X_test) predictions = np.argmax(predictions, axis=1) cm = metrics.confusion_matrix(y_test, predictions) div = plot_confusion_matrix(cm, class_names=class_names) cm_image = plot_to_image(div) with file_writer_cm.as_default(): tf.summary.image("Confusion Matrix", cm_image, step=epoch) A continuación debemos definir un callback LambdaCallback. LambdaCallback generará la matriz de confusión en cada época. Dado que el modelo ya se ha entrenado con anterioridad, es recomendable reiniciar la ejecución y asegurarse de que volvemos a hacer un fitting desde el inicio. callbacks = [ TensorBoard(log_dir=logdir, histogram_freq=1, write_graph=True, write_images=True, update_freq='epoch', profile_batch=2, embeddings_freq=1), keras.callbacks.LambdaCallback(on_epoch_end=log_confusion_matrix) ] tf.keras.backend.clear_session() model.fit(X_train, y_train, epochs=15, batch_size=128, validation_data=(X_test, y_test), callbacks=callbacks) Vídeo 3. Matriz de confusión de un modelo de clasificación con TensorBoard. Como podemos observar, resulta muy útil usar está herramienta como ayuda para mantener un registro de todos las métricas relacionadas con el entrenamiento de nuestras redes neuronales. En un artículo posterior trataremos la optimización de hiperparámetros de nuestra red. TensorBoard también se puede usar para visualizar la evolución de la precisión y métricas de performance de los modelos con la finalidad de poder elegir los parámetros que se pueden considerar como óptimos para el dataset que tenemos disponible y el problema que queremos resolver.
11 de noviembre de 2020
AI & Data
IA para detectar EPIs y Distancia Social
Dadas las circunstancias actuales, de la pandemia que estamos viviendo, desde el departamento de Ideas Locas seguimos interesándonos por todas aquellas soluciones que puedan ofrecer algún tipo de ayuda, en especial cuando están relacionadas con Machine Learning y Deep learning. En el anterior artículo sobre IA, hablamos de cómo se podría abordar la detección de afecciones respiratorias haciendo uso de Transfer Learning. En este artículo traemos una serie de soluciones que, haciendo uso de visión artificial, nos permiten determinar: si un sujeto lleva puesta, o no, una mascarilla; si esa persona lleva, o no, guantes; si dentro de un determinado entorno se está manteniendo la distancia de seguridad. En el artículo realizado a finales de mayo por Fran Ramírez en elladodelmal pudimos ver una prueba de concepto (PoC) adaptada a Raspberry Pi para la detección de rostros con mascarilla, para que, cualquier persona con un pequeño dispositivo en casa, pueda hacer uso de esta PoC. Detección de mascarillas en rostro Empecemos viendo cómo podemos crear un motor de visión artificial que nos permita determinar si una persona lleva la protección facial adecuada. El código lo tenéis disponible en el siguiente repositorio. Para realizar un detector de máscara facial personalizado, necesitamos dividir nuestro proyecto en dos fases distintas: Entrenamiento: aquí nos enfocaremos en cargar nuestro conjunto de datos de detección de máscara facial desde el disco, entrenar un modelo (usando Keras / TensorFlow) en este conjunto de datos y luego serializar el detector de máscara facial en local; Despliegue: Una vez que el detector de máscara facial está entrenado, podemos pasar a cargar el detector de máscara, realizar la detección de la cara y luego clasificar cada cara como Mask o No Mask. Dataset El conjunto de datos usados se ha obtenido del siguiente repositorio y se ha completado con algunas imágenes más de individuos de distintas etnias para garantizar un modelo más robusto. En total, el conjunto de datos consta de 1.442 imágenes que pertenecen a dos clases: Con máscara: 723 imágenes Sin máscara: 719 imágenes Este método es, en realidad, mucho más fácil de lo que parece una vez que aplicamos puntos de referencia faciales al problema. Los puntos de referencia faciales nos permiten inferir automáticamente la ubicación de las estructuras faciales, que incluyen: ojos, cejas, nariz, boca y contorno del rostro. Para obtener la Region of Interest (ROI) que enmarca el rostro: Entendemos que incluir la máscara en las imágenes y tapar ciertos landmarks ayudará al modelo a discretizar las muestras satisfactoriamente. Sobre el dataset disponible se hace data augmentation durante el entrenamiento: Rotación aleatoria de cada imagen de hasta 20 grados; Desplazamiento en altura y anchura de hasta el 20% de la dimensión de la imagen; Horizontal flipping; 'zoom_range' y 'shear_range' de 0.15 Entrenando el modelo Se usa Keras y TensorFlow para entrenar a un clasificador para detectar automáticamente si una persona usa una máscara o no. Para llevar a cabo esta tarea, ajustaremos la arquitectura MobileNet V2, una arquitectura altamente eficiente que se puede aplicar a dispositivos integrados con capacidad computacional limitada (por ejemplo, Raspberry Pi, Google Coral, NVIDIA Jetson Nano, etc.). Ejecutando el siguiente comando: $ python train_mask_detector.py --dataset dataset Se lanza un entrenamiento que terminará cuando la condición de EarlyStopping implementada se cumpla. Lo que estamos haciendo es un fine-tuning de MobileNetv2 para ahorrar tiempo y garantizar una buena predicción. Se obtienen dos checkpoints tras el entrenamiento: mask_detector_model.h5 y mask_detector.model. Actualmente, la precisión sobre el test dataset está alrededor del 98%, lo suficientemente alta como para considerar que el modelo de clasificación es de buena calidad. Detectando en vídeo Nuestro detector de mascarillas COVID-19 también puede funcionar en tiempo real. Simplemente ejecutamos en terminal: $ python detect_mask_video.py Para poder servir en video, desde la webcam de nuestro dispositivo, las predicciones de nuestro modelo. En el siguiente vídeo encontraréis un ejemplo de detección en tiempo real. Vídeo 1:Comprobando el uso de mascarillas en tiempo real. Detección de guantes Continuamos con un modelo que "trackea" las manos de un sujeto en un vídeo y, por otro lado, determina si lleva puestos, o no, guantes. El código está disponible en este siguiente enlace. Para ello, se hace uso del siguiente repositorio y de un modelo entrenado en Teachable Machine, herramienta de la que ya hemos hablado en este blog, cuyo checkpoint también se facilita en el código enlazado. En la carpeta Teachable Machine Gloves Detection se incluye todo el material descargable de ese servicio. Para entrenar este último modelo de Machine Learning, se han tomado aproximadamente unas 1000 fotografías de manos con guantes (gloves-on) y otras 1000 fotografías de manos sin guantes (gloves-off) en diversas posiciones, siguiendo una estrategia similar de data augmentation a la especificada en el apartado anterior. Para poder servir de manera rápida este modelo lanzamos una predicción sobre cada imagen servida por la webcam y lo mezclamos con el modelo de detección y seguimiento de manos. Detectando en vídeo Para poder hacer uso de la PoC tras hacer un setup del entorno virtual necesario, simplemente se tiene que ejecutar el script. $ python detect_single_threaded_gloves.py Un ejemplo rápido de cómo funciona se facilita en el siguiente vídeo. Video 2. Tracking de manos y detección de guantes a través de la webcam. Comprobando que se respeta la distancia social De manera muy rápida hemos investigado la posibilidad de medir la distancia social COVID-19 usando OpenCV, Deep Learning. Este código está basado en el siguiente artículo de pyimagesearch. Los pasos para construir un detector de distanciamiento social incluyen: Aplicar la detección de objetos para detectar a todas las personas (y solo a las personas) en una transmisión de video Calcular las distancias por pares entre todas las personas detectadas De acuerdo a las distancias recogidas, verificar si dos personas están separadas por menos de N píxeles. Todos los detalles del código implementado lo tenéis en este tercer repositorio de GitHub. Cálculo de las distancias entre sujetos Para su uso, simplemente ejecutamos: $ python social_distance_detector.py --input input.mp4 \ --output output.avi --display 0 Aspectos a mejorar El detector de distanciamiento social no aprovecha una calibración de cámara adecuada, lo que significa que, para medir la distancia entre individuos, lo que realmente estamos haciendo (hasta ahora) es medir las distancias en píxeles. Claramente, es una buena solución si se trata de un estudio en 2D pero, en este caso, como estudiamos un caso tridimensional, este aspecto debía mejorar. Por lo tanto, el primer paso para esta mejora de nuestro detector de distanciamiento social es utilizar una calibración de cámara adecuada. Una vez hecho esto, podremos calcular unidades de medida reales (en lugar de píxeles). En segundo lugar, debe considerar aplicar una transformación de arriba hacia abajo de su ángulo de visión. En este punto Marta Vilá, recientemente incorporada al equipo de Ideas Locas, se puso manos a la obra y desarrolló una rutina que nos permite corregir la distorsión introducida en la perspectiva de la imagen. Figura 1. Corrección vista aérea desde una cámara en vivo de la Plaza de España en Roma. Además, nos topamos con otra implementación que permitía estimar el grado de ocupación de un volumen definido por el usuario sobre un video. El repositorio con todo el código necesario puede encontrarse en el siguiente enlace. Video 3. Monitorización de la distancia de seguridad en un entorno elegido por el usuario. Cómo habéis visto, siguiendo siempre la misma estrategia de entrenamiento de modelos de clasificación de imágenes y haciendo uso de librerías como Keras u OpenCV, podemos crear o aprovechar herramientas de manera muy rápida que pueden ayudarnos a controlar ciertos entornos. Juntando todas soluciones, por ejemplo, podemos impedir la entrada a una sala de reuniones a una persona si no lleva mascarilla y/o guantes o incluso, si, dentro de un entorno controlado, mantienen la distancia de seguridad.
23 de junio de 2020
AI & Data
¿Qué es el self-supervised learning?
Self-supervised learning (o aprendizaje auto-supervisado en castellano) es un término que se refiere a un tipo de aprendizaje no supervisado enmarcado dentro de un problema de aprendizaje supervisado. Es una técnica de aprendizaje relativamente reciente donde los datos de entrenamiento se etiquetan de forma autónoma. Los datos se etiquetan encontrando y explotando las relaciones (o correlaciones) entre diferentes inputs al modelo. Este tipo de aprendizaje está ganando cada vez más visibilidad en investigación en Deep Learning. Este artículo tiene como finalidad hacer una breve presentación del self-supervised learning y cómo éste está dejando su impronta en el Machine Learning. Los distintos tipos de aprendizaje En ocasiones, explicar la diferencia entre el aprendizaje no supervisado, semi-supervisado y totalmente supervisado puede resultar arduo, por no hablar del aprendizaje reforzado. Os recordamos, por si necesitáis poneros en contexto, que en el blog se ha escrito sobre los dos principales paradigmas de aprendizaje que nos podemos encontrar en el mundo del Machine Learning. Sobre aprendizaje semi-supervisado también podéis consultar este post. No debemos confundir self-supervised learning con el aprendizaje no supervisado. Tal y como declaraba Yan LeCun el año pasado a través de Twitter: Ahora lo llamo "aprendizaje auto-supervisado", porque "no supervisado" es un término que se presta a confusión. Yan LeCun. 30 de abril de 2019. Twitter. En self-supervised learning, el sistema aprende a predecir parte de su entrada a partir de otras partes de su entrada. En otras palabras, una parte de la entrada neuronal a una red se utiliza como elemento de supervisión para un predictor alimentado con la parte restante del input. Este tipo de aprendizaje utiliza muchas más señales de supervisión que el aprendizaje supervisado, y mucho más que el aprendizaje reforzado. Es por eso que llamarlo "sin supervisión" es totalmente engañoso. Se puede aprender más sobre la estructura del mundo a través del aprendizaje auto-supervisado que de los otros dos paradigmas. La principal razón: los datos son ilimitados y el feedback proporcionado por cada ejemplo es enorme. El paradigma del aprendizaje supervisado El aprendizaje supervisado es un paradigma arduo, que requiere recolectar cantidades masivas de datos, limpiarlos, llevar a cabo un etiquetado manual, entrenar y perfeccionar un modelo diseñado específicamente para el caso de uso de clasificación o regresión que desee resolver, y luego usarlo para predecir etiquetas para datos desconocidos. Por ejemplo, con imágenes, recopilamos un conjunto de datos de imágenes grandes, etiquetamos los objetos en imágenes manualmente, entrenamos la red y luego la usamos para un caso de uso específico. Este tipo de aprendizaje, si bien es fácil de entender, dista bastante de la manera de aprender que tendría, por ejemplo, una persona. Aprendemos principalmente de manera no supervisada y reforzada, utilizando la curiosidad y el resultado de prueba-error. También aprendemos de manera supervisada, pero podemos aprender de muchas menos muestras, ya que si en algo se distingue el ser humano es en que es bastante bueno a la hora de generalizar y abstraer información. Self-supervised learning Self-supervised learning guarda similitudes con el aprendizaje no supervisado porque el sistema aprende sin usar etiquetas proporcionadas explícitamente como entrada. Pero también difiere de éste porque no estamos aprendiendo la estructura inherente de los datos. El aprendizaje auto-supervisado, a diferencia del aprendizaje no supervisado, no se centra en la agrupación, la reducción de dimensionalidad, los motores de recomendación, la estimación de densidad o la detección de anomalías. Una aplicación al PLN: BERT El aprendizaje auto-supervisado ha tenido un enorme éxito en el procesamiento del lenguaje natural. Por ejemplo, el modelo BERT de Google y técnicas similares producen excelentes representaciones de texto. Fig. 2. A great summary of how self-supervised learning tasks can be constructed (Image source: LeCun’s talk) BERT es un ejemplo claro de caso satisfactorio de aprendizaje auto-supervisado. Se le puede mostrar una secuencia de palabras en la entrada, se oculta un 15% de las palabras y se le pide al algoritmo que prediga las palabras que faltan (o una distribución de palabras). Este es un ejemplo de Autoencoder enmascarado, en sí mismo un caso especial de Denoising Autoencoder. Esta terminología referente a redes profundas ya la hemos tratado en este blog (ver artículo) sobre cómo los Autoencoders funcionan muy bien dentro de un paradigma no supervisado. Fig. 3. Illustration of context encoder. (Image source: Pathak, et al., 2016) Pero el texto es un espacio discreto en el que las distribuciones de probabilidad son fáciles de representar. Una persona es capaz de determinar el significado semántico de una palabra a partir del contexto cuando aparece cerca de otra palabra. Por ejemplo, el adjetivo “fría” da a entender algo diferente si se pone detrás del sustantivo “persona” o detrás del sustantivo “cerveza”. Del mismo modo, en Machine Learning, el algoritmo Word2Vec predice el contexto semántico de una palabra en función de las palabras circundantes. La investigación detrás self-supervised learning sigue el mismo principio de identificación automática, extracción y uso de señales de supervisión. Aplicaciones a otros campos Hasta ahora, enfoques similares también han funcionado para imágenes o videos a pesar de la dificultad a la hora de representar distribuciones en espacios continuos de alta dimensión, como muestra su reciente incorporación como soporte a problemas relativos al mundo de la robótica y del aprendizaje reforzado. En este enlace y en este otro se puede consultar cómo los algoritmos auto-supervisados se pueden utilizar para sacarle el máximo provecho a datos sin etiquetar. Aprendizaje reforzado profundo (Deep Reinforcement Learning) El aprendizaje reforzado profundo (Deep Reinforcement Learning) ha mostrado resultados notables en juegos y simulación. En los últimos años, el aprendizaje por refuerzo ha conquistado muchos juegos que anteriormente se consideraban prohibidos para la inteligencia artificial. Los programas de IA ya han barrido a los campeones mundiales en StarCraft 2, DOTA y el famoso juego de mesa chino Go. Pero la forma en que estos programas de IA aprenden a resolver problemas es drásticamente diferente de la de los humanos. Básicamente, un agente comienza sin ningún tipo de "conocimiento" del entorno y sólo se le proporciona un conjunto básico de acciones que puede realizar en su entorno. Luego, la IA se queda sola para aprender a través de prueba y error cómo generar la mayor cantidad de recompensas (por ejemplo, evitar perder una vida o maximizar la puntuación de un intento). Y aquí es donde el self-supervised learning está demostrando que puede ayudar también a los problemas que tienen cabida dentro del aprendizaje reforzados. Este modelo funciona cuando el espacio del problema es simple y tiene suficiente potencia de cómputo para ejecutar tantas sesiones de prueba y error como sea posible. En la mayoría de los casos, a los agentes de aprendizaje reforzado les lleva una cantidad elevada de intentos para dominar un entorno. Esto es computacionalmente costoso y sólo gigantes como Google se pueden permitir realizar investigaciones de alta calidad al respecto. En el Departamento de Ideas Locas ya profundizamos en el Aprendizaje Reforzado Profundo en esta serie de artículos y webinars.
22 de abril de 2020
AI & Data
Suplantación con un selfie y Deep Learning
A pesar del estado de alerta que estamos viviendo en la actualidad en el departamento Ideas Locas CDCO no descansamos, y seguimos investigando activamente todo lo relacionado con el Machine Learning y el Deep learning. Una de las líneas de investigación del departamento está centrada en el análisis y prevención de las Fake News, en especial aquellas generadas a partir de Deepfakes. Podéis echar un vistazo a esta investigación sobre generación de contenido no legítimo en el siguiente enlace. En el artículo de hoy queremos enseñaros de una manera lúdica lo fácil que resulta suplantar a un individuo haciendo uso única y exclusivamente de una fotografía del rostro o selfie. La mayor parte de las investigaciones centradas en suplantación de identidad con las tecnologías de Deepfakes hacen uso de Variational Autoencoders (VAEs) y General Adversarial Networks (GANs) tomando como fuente de entrenamiento una gran cantidad de imágenes del usuario a suplantar. A mayor número de muestras de la víctima, mayor calidad en el modelo y mayor credibilidad en la suplantación. Si no estáis tan familiarizados con estos términos aquí tenéis este artículo o este otro donde introducimos algunos conceptos básicos sobre este tipo de arquitecturas profundas. Figura 1 : Esquema de una Generative Adversarial Network (GAN) (Fuente) El principal inconveniente de este tipo de arquitecturas es que no sólo se necesitan muchas muestras para entrenarlas sino también mucho tiempo y una elevada capacidad de cómputo. Google Colab Para todo esto vamos a lanzar un Jupyter Notebook en Google Colab que nos permitía hacer un rápido uso de un repositorio muy interesante, como veremos más adelante. En Ideas Locas nos encanta Google Colab porque permite, no sólo realizar pruebas rápidas, sino también hace posible la democratización del Machine Learning y del Data Science para todo el mundo - desde investigadores en Inteligencia Artificial hasta gente no ducha con esta disciplina - y facilita el que podamos disfrutar de la potencia de estos recursos independientemente del dispositivo desde el que se consulte. Simplemente te tienes que preocupar por correr cada una de las celdas de código del Jupyter Notebook desplegado, editarlas si lo deseas y jugar conforme vayamos ganando destreza en el manejo del código. Un proyecto interesante Hace poco nos encontramos con el siguiente proyecto: First Order Motion Model for Image Animation. Esta línea de investigación, muy bien llevada, permite generar una secuencia de vídeo a partir de una imagen fuente siguiendo como patrón el movimiento de un vídeo de referencia. Figura 2. Esquema de la solución presentada por Siarohin et al. (2019) (Fuente) Lo más sorprendente de esta herramienta es que permite obtener resultados de muy buena calidad sin ningún tipo de información relativa a los recursos multimedia usados y en muy poco tiempo. En este enlace os facilitamos la página del proyecto y en este otro el enlace al GitHub donde podéis encontrar todo el código asociado. A continuación, tenéis un ejemplo de lo que se puede conseguir con este repositorio. No es habitual ver a Lisa Gherardini gesticular como Donald Trump. Video 1. Motion transfer from driving video (Donald Trump) to source image (Mona Lisa) Siempre y cuando se mantengan las mismas orientaciones del rostro, el resultado es bastante creíble y de buena calidad. ¿Quieres probarlo? Para poder cambiar la imagen fuente y el video de referencia a vuestro gusto, os hemos preparado un Jupyter de prueba. Podéis abrir el siguiente Gist y clicar en Open in Colab en la parte superior del cuaderno. Siguiendo las instrucciones, seguro que conseguís resultados divertidos que hagan más llevadera la cuarentena.
25 de marzo de 2020
AI & Data
Analiza tus datos con Embedding Projector de Tensorflow
Los datos son el combustible de la Inteligencia Artificial. En estado bruto, sin procesar, tienen una utilidad bastante limitada, pero una vez que son analizados y se procede a estructurarlos y agregarlos, los usos de los datos parecen casi ilimitados. Además, con los datos sucede algo bastante especial, y es que cuantos más datos estén disponibles, mejor. Muchas veces su calidad está en la propia diversidad y cantidad de los datos y esto es muy relevante para la IA. En este artículo, veremos una herramienta llamada Embedded Projector, que nos permite visualizar datos de altas dimensiones fácilmente para que podamos entender qué aprende un modelo sobre los datos. Figura 1. Embedding del término "Linux" junto con varios vecinos de acuerdo al modelo Word2Vec visualizado haciendo uso de t-SNE. Procesado de datos El preprocesamiento de datos constituye una tarea imprescindible en el desempeño de un científico de datos. Para poder obtener un resultado final óptimo tras la aplicación de un modelo a un determinado dataset, es necesario transformar los datos de entrada para que el algoritmo encargado de procesar la información funcione correctamente. Limpieza de datos El primer paso está asociado a la limpieza de los datos – data cleaning –subdividido tal y como se especifica a continuación: Limpieza de datos o data cleansing: se eliminan todos aquellos datos que puedan contener errores. Identificación de características: se seleccionan sólo aquellas características que se ven necesarias para caracterizar los modelos. Corrección de errores en los datos: cualquier fallo en el contenido de un string o un valor numérico en un campo alfanumérico se deben identificar y corregir. Estandarización de los datos: esta fase se destina a normalizar y tanto variables numéricas como categóricas en un dataset. Una vez los datos han sido limpiados, se procede a la integración de los mismos de acuerdo a los formatos definidos para el modelo que deseamos implementar, como por ejemplo integrar todos los datos en un fichero .csv, en una base de datos con una determinada estructura prefijada, en un Pandas Dataframe, etc. Transformación de los datos El siguiente paso corresponde a la transformación de los datos – data transformation – muy similar a la fase de estandarización de data cleaning, donde se corrigen o mitigan inconsistencias presentes en los datos a través de técnicas de normalización, la estandarización o el suavizado de datos. Reducción de datos La fase final se corresponde con la reducción de datos – data reduction – dedicada a la eliminación de información que no sea útil a los modelos de Machine Learning. La reducción de la dimensión implica la transformación de los datos a nuevas dimensiones (siempre a un número menor de dimensiones) de manera que se facilite el descarte de algunas de ellas minimizando la pérdida de información. Los problemas a gran escala provocan varias dimensiones que pueden llegar a ser muy difíciles de visualizar. Algunas de las técnicas más utilizadas de reducción de datos son Principal Component Analysis (PCA) o t-Distributed Stochastic Neighbor Embedding (t-SNE), como se verá a continuación. Aquí es donde entra la necesidad de los Embeddings. Embeddings Los Embeddings son ampliamente utilizados en Machine Learning, desde sistemas de recomendación hasta Procesado de Lenguaje Natural. Los científicos de datos necesitan explorar las propiedades de un dataset, y una forma de realizar el análisis es mediante su visualización. Embedding Projector cuenta una visualización precargada para el conjunto de datos MNIST, el conjunto de datos Iris, Word2Vec y GNMT Interlingua con el que poder jugar y acostumbrarse a la herramienta. De forma adicional, proporciona una opción para cargar sus propios datos de embeddings para su visualización. Uso de la aplicación Embedding Projector Esta aplicación web nos permite consultar rápidamente los Embeddings de nuestro modelo y representarlos en un número menor de dimensiones; en este caso dos o tres. Aquí hay una visualización de las diez mil imágenes del MNIST dataset coloreadas por su etiqueta haciendo uso de PCA. Video 1. Análisis de Principal Component Analysis en 3D para MNIST dataset. En el vídeo anterior observamos que la aplicación que consta de cinco paneles distintos. Panel de datos (arriba a la izquierda) donde podemos elegir qué dataset examinar. Un panel de visualización (centro) es el área donde se muestran los datos. Panel de proyecciones (abajo a la derecha): podemos elegir el tipo de proyección aplicar a la visualización de nuestros datos Panel de inspectores (derecha), donde podemos buscar puntos particulares y ver una lista de vecinos más cercanos en base a distancia euclidiana y distancia de coseno. El panel de marcadores (inmediatamente debajo del panel de datos) nos permite guardar el estado actual - incluidas las coordenadas calculadas de cualquier proyección - como un pequeño archivo que luego se puede compartir. Otra de las proyecciones disponibles dentro de un paradigma no supervisado o semi-supervisado es la mostrada en el siguiente video. Video 2. Análisis de t-SNE para MNIST dataset en 3D y 2D. t-SNE o t-distributed Stochastic Neighbour Embedding visualiza datos de alta dimensión al dar a cada punto de datos una ubicación en un mapa bidimensional o tridimensional. De esta forma se encuentran grupos en los datos. Arriba se observa cómo los dígitos similares están claramente agrupados en una misma vecindad. Otras proyecciones que tenemos disponibles son: Custom es una proyección lineal sobre los ejes horizontal y vertical especificada utilizando las etiquetas de datos. Las proyecciones personalizadas ayudan principalmente a descifrar las "direcciones" significativas en los conjuntos de datos. UMAP: acrónimo de Uniform Manifold Approximation and Projection for Dimension Reduction. t-SNE es una técnica excelente para visualizar conjuntos de datos de alta dimensión, pero tiene ciertos inconvenientes, como un alto tiempo de cálculo y pérdida de información a gran escala. UMAP, por otro lado, trata de superar estas limitaciones, ya que puede manejar conjuntos de datos bastante grandes con facilidad y al mismo tiempo preservar la estructura de datos local y global. A continuación podéis ver cómo se agrupan las muestras haciendo uso de UMAP. Video 3. UMAP en acción para MNIST dataset en 3D. Conclusión La herramienta Embedding Projector facilita la tarea de visualizar Embeddings de datasets de alta dimensionalidad, permitiendo abstraernos de la complejidad de los algoritmos involucrados. Os animamos a investigar al máximo esta herramienta, a jugar con los Embeddings disponibles e incluso, a probar con embeddings creados por vosotros.
26 de febrero de 2020
AI & Data
Crea tu modelo Machine Learning con Teachable Machine
Hace unos pocos meses, Google liberó Teachable Machine, una herramienta y un recurso sorprendentes para Machine Learning dirigida al público general. Esta herramienta nos permite abstraernos de la definición del modelo, algoritmo y tratamiento de datos y centrarnos sólo en el despliegue del modelo que generemos. Funciona en el navegador, con una webcam o con archivos que tengamos alojados en local o en Google Drive, y en pocos minutos puede comprender rápidamente cómo un modelo "aprende" a través de una simple demostración de clasificación. Figura 1. Teachable Machine webpage. En marzo del año pasado realizamos una serie de artículos donde mostramos cómo aplicar un algoritmo de clasificación muy sencillo haciendo uso de Random Forest a través de los estimadores de Scikit-Learn. Podéis consultar la serie aquí o en este otro enlace. Para diferenciarnos un poco de la extensa literatura soportada en datasets muy conocidos y usados, decidimos clasificar los datos del Sloan Digital Sky Survey (SDSS) DR14, relacionado con el mundo de la astrofísica, donde se recogen datos fotométricos de estrellas, galaxias y quasars. Podéis encontrar el dataset en el siguiente enlace. Para abrir boca, vamos a usar datos no tan usuales en la demostración del excelente desempeño de este servicio. Recurriremos al Galaxy Zoo classification dataset. Obtener y entender los datos Por poner en antecedentes, Galaxy Zoo es un proyecto de crowdsourcing, donde se pide a los usuarios que describan la morfología de las galaxias en base a imágenes. Se les hacen preguntas como "¿Cómo de redonda es la galaxia?" y "¿Tiene una protuberancia central?". Las respuestas de los usuarios determinan qué pregunta se hará a continuación. Las preguntas forman un árbol de decisión que se muestra en la figura a continuación, tomada de Willett et al. 2013. Figura 2. Árbol de decisión de clasificación de galaxias (Willett et al. 2013) Cuando muchos usuarios han clasificado la misma imagen, sus respuestas se pueden agregar en un conjunto de probabilidades para cada respuesta. A menudo, no todos los usuarios estarán de acuerdo con todas sus respuestas, por lo que es útil cuantificar esta incertidumbre. Determinar la morfología de un objeto es una tarea muy subjetiva. El objetivo del desafío Galaxy Zoo es predecir estas probabilidades a partir de las imágenes de galaxias que se muestran a los usuarios. En otras palabras, construya un modelo de cómo "la multitud" percibe y clasifica estas imágenes. El dataset, consistente en más de 60,000 imágenes, se puedes descargar en el siguiente enlace. Esto significa que estaríamos ante un problema de regresión, no un problema de clasificación: no tenemos que determinar a qué clases pertenecen las galaxias, sino la fracción de personas que las clasificaría como tal. Sin embargo, dada la funcionalidad de Teachable Machine, nos vamos a tomar una licencia y vamos a procesar las imágenes que tenemos para poder separarlas en varias clases morfológicas que se intenten adaptar lo más posible a la secuencia del Hubble. Esta secuencia es un esquema de clasificación morfológica para galaxias. Figura 3. Hubble Tunning Fork. Grosso modo, en su definición más simple, las galaxias: elípticas tienen distribuciones de luz suaves y sin características y aparecen como elipses en las imágenes. espirales consisten en un disco aplanado, con estrellas formando una estructura (generalmente de dos brazos) y una concentración central de estrellas conocida como el bulbo galáctico, que es similar en apariencia a una galaxia elíptica. lenticulares también consiste en un brillante bulbo central rodeado por una estructura extendida, similar a un disco pero, a diferencia de galaxias espirales, sin estructura espiral visible. Ordenando las imágenes Como comentamos con anterioridad, hay alrededor de 60,000 imágenes en el conjunto de entrenamiento, en formato JPEG en color con dimensiones 424x424. Figura 4. Ejemplos de imágenes de galaxias en el Galaxy Zoo dataset. Con esas fotografías también se nos facilita un fichero CSV con el resultado de las clasificaciones realizadas por los usuarios. Hemos usado ese CSV para ordenar las imágenes en base a la clase con voto mayoritario. Haciendo uso del siguiente script de Python, todas las imágenes de la carpeta 'data/' quedarán convenientemente ordenadas. import os import numpy as np import csv import shutil dir_path = os.path.dirname(os.path.realpath(__file__)) path = os.path.join(dir_path, 'data') files = os.listdir(path) def get_all_solutions(): ### Import solutions file and load into all_solutions all_solutions = {} with open('training_solutions_rev1.csv', 'r') as f: reader = csv.reader(f, delimiter=",") labels = next(reader)[1:-1] for i, line in enumerate(reader): all_solutions[line[0]] = labels[np.argmax([float(x) for x in line[1:]])] return all_solutions, labels def move_pics(): """ Classify images by its label from training_solutions_rev1.csv """ mapping, labels = get_all_solutions() for type in labels: if not os.path.exists(os.path.join(path, type)): try: os.mkdir(os.path.join(path, type)) except OSError: print("Creation of the directory %s failed" % path) else: print("Successfully created the directory %s " % path) for file in files: shutil.move(os.path.join(path, file), os.path.join(path, mapping[file.split('.')[0]], file)) if __name__=="__main__": move_pics() Lo que nos hemos encontrado es que el 85% de las galaxias han acabado mayoritariamente agrupadas en 3 directorios. La discriminación de cada grupo es clara: elípticas, espirales o irregulares. Entrenando el modelo Para comenzar a entrenar el modelo de clasificación, primero tenemos que crear diferentes categorías o clases para enseñarla. Figura 5. Vista del comienzo de un proyecto de clasificación de imágenes. Vamos a hacer una prueba rápida creando sólo dos clases: una para galaxias elípticas (Class 1.1) y otra para galaxias con forma espiral (Class 1.2), fácilmente reconocibles a simple vista. Figura 6. Una vez subidas las imágenes, podemos prepararnos para comenzar el entrenamiento (definición de los hiperparámetros más básicos) Con las clases o etiquetas definidas, subimos desde local las muestras de cada clase para que pueda comenzar el entrenamiento. Elegimos 200 imágenes con resolución 424x424 por clase. Ahora que tenemos las clases listas, procedemos a entrenar. Vídeo 1. Entrenamiento de modelo de clasificación. Como se puede observar, el modelo tarda en entrenar sólo un minuto. No sólo la rapidez en el entrenamiento es excelente (quizás me quede corto con este adjetivo), sino que te ofrecen métricas dinámicas valiosas como la precisión y pérdida del training y test dataset y la precisión por clase observada. Nuestro modelo se estanca en el aprendizaje alrededor de la decimoquinta época, amagando con sufrir de overfitting. Pero el resultado es bastante bueno considerando que es una prueba muy rápida con los hiperparámetros por defecto. Las precisiones para la clasificación de las clases son del 87% para Class 1.1 y del 100% para Class 1.2. Esto se puede interpretar con el hecho de que algunas galaxias elípticas pueden tener una forma con excentricidad algo más elevada de lo normal y se confundan con las espirales. Estas últimas son las que mejor reconoce el modelo, pues como ya vimos en la Figura 4 (medio) son las más fácilmente reconocibles. Testeando el modelo Esta tarea es también muy sencilla de realizar. Vídeo 2. Testeo de modelo de clasificación. Como se puede ver en el Video 2, cogiendo dos imágenes al azar entre aquellas que no se han usado para el entrenamiento, nuestro modelo es capaz de clasificarlas correctamente. Tenemos la opción de cargar recursos para testeo tanto desde local o Google Drive como desde nuestra webcam. Obteniendo el modelo para despliegue Si fuera poco, se puede exportar su modelo como modelo Tensorflow.js y alojarlo en Teachable Machine de forma gratuita, para que pueda llamarlo a cualquier sitio web o aplicación. También puede convertirlo a Tensorflow y Tensorflow Lite y descargarlo para uso local. Las posibilidades son innumerables con gracias a esta funcionalidad. Video 3. Posibilidades de exportación de modelos de clasificación en Teaching Machine Conclusión Por supuesto, en nuestra prueba no hemos llegado a las precisiones que lograron los vencedores del challenge (cuya aproximación mediante redes neuronales convolucionales se explica en este link), pero sí que nos ha permitido mostrar cómo de simple es usar esta herramienta y el poco tiempo que hemos invertido en entrenar el modelo. Hay que destacar que sólo en entrenar un modelo convolucional, hay que esperar bastante más tiempo que los dos minutos que hemos invertido en obtener nuestro resultado, sin hablar del preprocesado, organización en batches, etc. Si os pica la curiosidad, os invitamos a probarlo. Puedes intentar hacer tu propio modelo de clasificación en unos pocos minutos. También puedes entrenar y desplegar un modelo basado en sonido o en la pose de un individuo. Han prometido incorporar más funcionalidades en un futuro.
27 de enero de 2020
AI & Data
Transfer Learning: Modelos de reconocimiento facial de humanos aplicados al ganado
Hace unas semanas, en este artículo, realizamos una introducción a la reutilización de modelos de Deep Learning para proyectos propios haciendo uso de Transfer Learning. En este post iremos en más detalle con un pequeño proyecto que estamos desarrollando en Ideas Locas y presentaremos algo de código haciendo uso de Keras. Introducción Este proyecto tiene como misión identificar ganado (vacas, cerdos, etc.) haciendo uso de redes neuronales profundas convolucionales. Las capas convolucionales actúan como extractores de características. Con la intención de ahorrar tiempo, se pretende hacer uso de modelos ya entrenados de Deep Learning dedicados a reconocimiento facial de seres humanos para la identificación de ganado. Para ello, se realiza Transfer Learning y Fine-Tuning de los modelos de Oxford VGGFace a través del endpoint de TensorFlow. El código que se ha generado pero que todavía está pendiente de ser probado soporta tanto los modelos VGG16 como RESNET50 o SENET50: from keras_vggface.vggface import VGGFace # Based on VGG16 architecture vggface = VGGFace(model='vgg16') # or VGGFace() as default # Based on RESNET50 architecture vggface = VGGFace(model='resnet50') # Based on SENET50 architecture vggface = VGGFace(model='senet50') Tanto en su versión: include_top=False que permiten quedarte sólo con la parte convolucional de la red para hacer un stacking superior de un clasificador a placer. Como en la versión: include_top=False la cual incluye la parte de clasificación original con todas las capas densas, lo que lo hace más pesado. Los pesos para los modelos se pueden obtener en el siguiente enlace. Setup del entorno Por sencillez, hacemos uso de Anaconda para la creación de un entorno virtual. Mediante un fichero requirements.txt es sencillo crear uno para evitar problemas de incompatibilidad entre paquetes. $ conda create --name <env> --file requirements.txt Ejemplo de uso Data Cleaning (opcional) El primer paso es trabajar con el dataset de imágenes. Nos encontramos en el caso en el que el número de imágenes es bajo y, además, pueden ser muy similares entre ellas. Para evitar la baja varianza entre imágenes se emplea la medida del índice de similitud estructural (SSIM) para medir la similitud entre fotografías. Esto ayuda a evitar datos muy similares (casi idénticos en las particiones de datos de validación y entrenamiento. En los subdirectorios anidados de ./dataset se checkea una imagen contra todas las demás y se van eliminando aquellas que sean similares por encima de un valor de similitud (entre 0 y 1) indicado por el usuario. $ python ssim.py --dir "dir/to/images" --threshold 0.9 Customización de arquitectura para Transfer Learning En el script donde se lanza el entrenamiento, basado en vgg16, resnet50 o senet50 debemos acabar la red en un clasificador que, por defecto, tiene la siguiente implementación Sequential de Keras: self.x = self.last_layer self.x = Dense(self.hidden_dim_1, activation='relu', name='fc1')(self.x) self.x = Dense(self.hidden_dim_2, activation='relu', name='fc2')(self.x) self.x = Dense(self.hidden_dim_3, activation='relu', name='fc3')(self.x) self.out = Dense(self.nb_class, activation='softmax', name='out')(self.x) Clasificador de capas densas totalmente conectadas que se meten tras la capa flatten de los modelos convolucionales. A continuación, se muestra un fragmento de código donde se define la arquitectura de VGG16: # Block 1 x = Conv2D(64, (3, 3), activation='relu', padding='same', name='conv1_1')( img_input) x = Conv2D(64, (3, 3), activation='relu', padding='same', name='conv1_2')(x) x = MaxPooling2D((2, 2), strides=(2, 2), name='pool1')(x) # Block 2 x = Conv2D(128, (3, 3), activation='relu', padding='same', name='conv2_1')( x) x = Conv2D(128, (3, 3), activation='relu', padding='same', name='conv2_2')( x) x = MaxPooling2D((2, 2), strides=(2, 2), name='pool2')(x) # Block 3 x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_1')( x) x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_2')( x) x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_3')( x) x = MaxPooling2D((2, 2), strides=(2, 2), name='pool3')(x) # Block 4 x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_1')( x) x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_2')( x) x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_3')( x) x = MaxPooling2D((2, 2), strides=(2, 2), name='pool4')(x) # Block 5 x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_1')( x) x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_2')( x) x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_3')( x) x = MaxPooling2D((2, 2), strides=(2, 2), name='pool5')(x) if include_top: # Classification block x = Flatten(name='flatten')(x) x = Dense(4096, name='fc6')(x) x = Activation('relu', name='fc6/relu')(x) x = Dense(4096, name='fc7')(x) x = Activation('relu', name='fc7/relu')(x) x = Dense(classes, name='fc8')(x) x = Activation('softmax', name='fc8/softmax')(x) else: if pooling == 'avg': x = GlobalAveragePooling2D()(x) elif pooling == 'max': x = GlobalMaxPooling2D()(x) Congelación de capas para Fine-Tuning Normalmente, para Transfer Learning y Fine-Tuning de modelos con dataset pequeños, lo que se hace es congelar la arquitectura transferida y entrenar sólamente el clasificador customizado por nosotros. nb_freeze = None indica que no se congela ninguna capa. Todas las capas son entrenables. nb_freeze = 10 indica que se congelan las 10 primeras capas. Las restantes son entrenables por defecto. nb_freeze = -4 indica que se congelan todas menos las 4 últimas capas. Las restantes son entrenables por defecto. Dataset El dataset sobre el que se desea entrenar debe situarse en la carpeta ./dataset. Para cada clase, se deben agrupar todas las imágenes en subdirectorios. Los batches de entrenamiento, validación, así como el número de clases a predecir y, por lo tanto, la arquitectura de salida del modelo, están definidas tanto por el generador ImageDataGenerator() como por la función flow_from_directory() soportadas por Keras. Sobre el dataset disponible se hace data augmentation: Rotación aleatoria de cada imagen de hasta 20 grados; Desplazamiento en altura y anchura de hasta el 5% de la dimensión de la imagen; Horizontal flipping. Logueo del entrenamiento Para el entrenamiento se han definido dos callbacks: EarlyStopping para evitar overfitting o alargar innecesariamente el tiempo de entrenamiento. TensorBoard Callback que permite loguear precisión y funciones de pérdida para su visualización en browser de las curvas de aprendizaje y del grafo creado y entrenado. $ cd logs/folder_tbd/ $ tensorboard --logdir=./ --port 6006 De manera que con sólo ir a tu navegador a http://localhost:6006/ se puede visualizar cómo ha transcurrido el entrenamiento. Ver el siguiente artículo para aclarar dudas sobre el uso de Tensorboard. Testeo De cara al testeo de un modelo ya entrenado con una imagen de muestra, se ejecuta el script testing.py: $ python testing.py --granja test --img "path/to/img" Esta rutina devuelve las predicciones de una imagen en base a todas las clases soportadas por el modelo. La rutina devuelve: $ python testing.py --granja test --img "path/to/img" { "class 0": score 0, "class 1": score 1, ... "class N": score N } Grad-CAM Checking Un inconveniente frecuentemente citado del uso de redes neuronales es que entender exactamente lo que están modelando es muy difícil. Esto se complica aún más utilizando redes profundas. Sin embargo, esto ha comenzado a atraer una gran cantidad de interés de investigación, especialmente para las CNNs para garantizar que la "atención" de la red se centre en las características reales y discriminativas del propio animal, en lugar de otras partes de la imagen que puedan contener información discriminatoria (por ejemplo, una etiqueta de clase, una marca de tiempo impresa en la imagen o un borde sin interés). Para comprobar que nuestro modelo se centra en partes interesantes de la imagen, se puede elegir una imagen de prueba y comprobar qué regiones son de interés para la red neuronal, se puede ejecutar: $ python grad_CAM.py --granja test --model resnet50 --img "path/to/img" Lo cual te devuelve un mapa de calor sobre la imagen de las regiones de interés. Aquí vemos un ejemplo generado a partir de un modelo que distingue entre vacas, osos y gatos. Con un mínimo entrenamiento es capaz de distinguir con un 98% de precisión entre especies en base a estructuras faciales. Figura 1. Región de interés de la imagen de testeo para el modelo. Sabemos que nuestro modelo ha aprendido bien porque se centra en la región del animal que nos interesa, y no en el fondo u otras partes carentes de relevancia. Enlaces de Soporte e Interés: Keras Framework Oxford VGGFace Website Arkhi et al.: Deep Face Recognition VGGFace implementation with Keras Framework Deep Learning For Beginners Using Transfer Learning In Keras Transfer learning from pre-trained models Transfer Learning using Keras Tutorial on using Keras flow_from_directory() and generators Transfer learning and Image classification using Keras on Kaggle kernels Gradient-weighted Class Activation Mapping - Grad-CAM Keras implementation of GradCAM Grad-CAM with keras-vis A Python module for computing the Structural Similarity Image Metric (SSIM) En cuanto tengamos datasets más consistentes continuaremos trabajando en esta prueba de concepto para investigar la eficiencia del Transfer Learning en este tipo de campos.
13 de noviembre de 2019
AI & Data
¿Cómo funciona el algoritmo Backpropagation en una Red Neuronal? Parte II.
En el anterior post de esta serie comenzamos nuestra andadura para intentar resumir el algoritmo de backpropagation. En este nuevo post proseguiremos con una breve explicación de las cuatro ecuaciones fundamentales detrás de backpropagation que nos permitirán una total comprensión del algoritmo. Las cuatro ecuaciones fundamentales de backpropagation Backpropagation se trata de comprender cómo el cambio de los pesos y sesgos en una red impacta en la función de coste. En definitiva, esto significa calcular las derivadas parciales $latex \frac{\partial C}{\partial w_{jk}^l }$ y $latex \frac{\partial C}{ \partial b_j^l}$. Pero para calcularlos, primero introducimos una cantidad intermedia, $latex \delta_j^l$, que podemos llamar el error en la j-ésima neurona de la capa l-ésima. Backpropagation nos dará un procedimiento para calcular el error $latex \delta_j^l$, y luego relacionarlo con $latex \frac{\partial C}{\partial w_{jk}^l }$ y $latex \frac{\partial C}{ \partial b_j^l}$ . Para comprender cómo se define el error, imagine que hay un demonio en nuestra red neuronal: Figura 1: Red neuronal con demonio. El demonio se sienta en la j-ésima neurona en la capa l. A medida que entra la entrada a la neurona, el demonio juega con la operación de la neurona. Agrega un pequeño cambio $latex \Delta z_j^l$ a la ponderación del input de la neurona, de modo que en lugar de generar $latex \sigma(z_j^l )$ la neurona produce $latex \sigma(z_j^l+\Delta z_j^l)$. El cambio se propaga a través de las capas posteriores de la red, lo que finalmente causa que la función de coste cambie en una cantidad $latex \frac{\partial C}{ \partial z_j^l} \Delta z_j^l $. Pero este demonio es un buen demonio y está tratando de ayudarnos a mejorar el coste, es decir, están tratando de encontrar un $latex \delta z_j^l$ que lo reduzca. Suponga que $latex \frac{\partial C}{ \partial z_j^l} $ tiene un valor grande (ya sea positivo o negativo). Entonces el demonio puede reducir el costo bastante eligiendo $latex \Delta z_j^l$ para tener el signo opuesto a $latex \frac{\partial C}{ \partial z_j^l} \Delta z_j^l $ . Por el contrario, si $latex \frac{\partial C}{ \partial z_j^l} \Delta z_j^l $ está cerca de cero, entonces el demonio no puede mejorar mucho la función de coste tocando $latex z_j^l$, por lo que tendrá a la neurona muy cerca del punto de equilibrio. Podemos entonces decir que $latex \frac{\partial C}{ \partial z_j^l} \Delta z_j^l $ es una medida del error en la neurona. Motivados por esta historia, definimos el error $latex \delta_j^l$ de la neurona $latex j$ en la capa $latex l$ por: $latex \delta_j^l = \frac{\partial C}{\partial z_j^l}$ Quizás se pregunte por qué el demonio está cambiando la entrada ponderada $latex z_j^l$. Seguramente sería más natural imaginar al demonio cambiando la activación de salida $latex a_j^l$, con el resultado de que estaríamos usando $latex \partial C$ como nuestra medida de error. De hecho, si haces esto, las cosas funcionarán de manera bastante similar a la discusión a continuación. Pero resulta que la presentación backpropagation es un poco algebraicamente más complicada. Entonces nos quedaremos con $latex \frac{\partial C}{ \partial z_j^l}$ como nuestra medida de error . Plan de ataque Backpropagation se basa en cuatro ecuaciones fundamentales. Juntas, esas ecuaciones nos dan una manera de calcular tanto el error $latex \delta_j^l$ como el gradiente de la función de coste. A continuación, se declaran las cuatro ecuaciones. Sin embargo, tengamos cuidado: no debemos esperar asimilar instantáneamente las ecuaciones. Tal expectativa conducirá a la decepción. De hecho, las ecuaciones de retropropagación son algo complejas, por lo que comprenderlas bien requiere tiempo y paciencia. La buena noticia es que esa paciencia se paga muchas veces. Por lo tanto, la discusión en esta parte es simplemente un comienzo, ayudándole en el camino hacia una comprensión profunda de las ecuaciones. a. BP1: Una ecuación del error en la capa de salida, $latex \delta^L$. $latex \delta_j^l = \frac{\partial C}{\partial a_j^L} \sigma'(z-j^L) $ Esta es una expresión muy natural. El primer término a la derecha, $latex \frac{\partial C}{ \partial a_j^l}$ , solo mide cómo de rápido está cambiando el coste en función de la activación de salida j-ésima. Si, por ejemplo, $latex C$ no depende mucho de una neurona de salida particular, $latex j$, entonces $latex \delta_j^L$ será pequeño, que es lo que podemos esperar. El segundo término a la derecha, $latex \sigma' (z_j^L )$, mide cómo de rápido la función de activación $latex \sigma$ está cambiando en $latex z_j^L$. Tenga en cuenta que todo en la ecuación BP1 se calcula fácilmente. En particular, calculamos $latex z_j^L$ mientras calculamos el comportamiento de la red, y sólo es una pequeña sobrecarga adicional para calcular $latex \sigma' (z_j^L )$. La forma exacta de $latex \frac{\partial C}{ \partial a_j^l}$ , por supuesto, dependerá de la forma de la función de coste. Sin embargo, siempre que se conozca la función de coste, debería haber pocos problemas de cálculo y entonces $latex \frac{\partial C}{ \partial a_j^l}= (a_j^L-y_j) $, que obviamente es fácilmente computable. La ecuación BP1 es una expresión componente para $latex \delta_j^L$ . Es una expresión perfectamente buena, pero no la forma basada en matriz que queremos para la propagación hacia atrás. Sin embargo, es fácil reescribir la ecuación en forma matricial, como: $latex \delta^L=\nabla_a C \odot \sigma'(z^L)$ Puede pensar que $latex \nabla_a C$ expresa la tasa de cambio de $latex C$ con respecto a las activaciones de salida. Es fácil ver que la ecuación anterior y BP1 son equivalentes, y por esa razón a partir de ahora usaremos BP1 indistintamente para referirnos a ambas ecuaciones. Como ejemplo, en el caso del costo cuadrático tenemos $latex \nabla_a C=(a_j^L-y_j)$, por lo que la forma completamente basada en matriz de BP1 se convierte en: $latex \delta^L = (a_j^L-y_j) \odot \sigma'(z^L)$ b. BP2: Una ecuación del error $latex \delta^l$ en términos del error de la siguiente capa, $latex \delta^(l+1)$. $latex \delta^l = ( (w^{l+1} )^T \delta^{l+1}) \odot \sigma'(z^L)$ donde $latex (w^{l+1} )^T$ es la transposición de la matriz de peso $latex w^{l+1}$ para la capa $latex (l + 1)$. Esta ecuación parece complicada, pero cada elemento tiene una buena interpretación. Supongamos que conocemos el error $latex \delta^{l+1}$ en la capa $latex (l + 1)$. Cuando aplicamos la matriz de peso de transposición, $latex (w^{l+1} )^T$ , podemos pensar intuitivamente en esto como mover el error hacia atrás a través de la red, dándonos algún tipo de medida del error en la salida de la capa l-ésima. Luego tomamos el producto Hadamard $latex \odot \sigma' (z^l ) $. Esto mueve el error hacia atrás a través de la función de activación en la capa $latex l$, dándonos el error $latex \delta^l$ en la entrada ponderada a la capa $latex l$. Combinando BP1 y BP2 tenemos el error $latex \delta^l$ de cualquier capa de la red. Empezamos usando BP1 para calcular el error $latex \delta^L$ y aplicando BP2 las $latex n$ veces que deseemos llegaremos a calcular el error $latex \delta^{L-n}$ . c. BP3: Una ecuación para calcula la tasa de cambio del coste con respecto a cualquier bias de la red, $latex \delta_j^l = \frac{\partial C}{\partial b_j^l}$ Es decir, el error $latex \delta_j^l$ es exactamente a la tasa de cambio $latex \frac{\partial C}{ \partial b_j^l}$ . Esta es una gran noticia, ya que BP1 y BP2 ya nos han dicho cómo calcular $latex \delta_j^l$ . Podemos reescribir BP3 en forma abreviada como: $latex \delta = \frac{\partial C}{\partial b}$ Donde se entiende que $latex \delta$ se evalúa en la misma neurona que el bias $latex b$. d. BP4: una ecuación para la tasa de cambio de la función de coste con respecto a cualquier peso en la red, $latex \frac{\partial C}{\partial w_{jk}^l}=(a_k^{l-1} - \delta_j^l)$ Esto nos dice cómo calcular las derivadas parciales $latex \frac{\partial C}{ \partial w_{jk}^l}$ en términos de las cantidades $latex \delta^l$ y $latex a^{l-1}$, que ya sabemos cómo calcular. La ecuación se puede reescribir en una notación menos pesada como índice: $latex \frac{\partial C}{\partial w}=a_{in}\delta_{out}$ donde se entiende que $latex a_{in}$ es la activación de la entrada de neuronas al peso $latex w$, y $latex \delta_{out}$ es el error de la salida de neuronas del peso $latex w$. Al acercarnos para ver sólo el peso $latex w$, y las dos neuronas conectadas por ese peso, podemos representar esto como: Figura 2: Dos neuronas conectadas por ese peso Una buena consecuencia de la ecuación anterior es que cuando la activación $latex a_{in}$ es pequeña, $latex a_{in}\sim 0$ , el término de gradiente $latex \frac{\partial C}{\partial w}$ también tenderá a ser pequeño. En este caso, diremos que el peso aprende lentamente, lo que significa que no cambiará mucho durante el descenso del gradiente. En otras palabras, una consecuencia de BP4 es que los pesos que salen de las neuronas de baja activación aprenden lentamente. Hay otras ideas a lo largo de estas líneas que se pueden obtener de BP1 - BP4. Comencemos mirando la capa de salida. Considere el término $latex \sigma'(z_j^L)$ en BP1. Recuerde de la gráfica de la función sigmoide en el último capítulo que la función $latex \sigma$ se vuelve muy plana cuando $latex \sigma' (z_j^L)$ es aproximadamente 0 o 1. Cuando esto ocurra tendremos $latex \sigma' (z_j^L )\sim 0$. Y así la lección es que un peso en la capa final aprenderá lentamente si la neurona de salida es de baja activación $latex (\sim 0)$ o alta activación $latex (\sim 1)$. En este caso, es común decir que la neurona de salida se ha saturado y, como resultado, el peso ha dejado de aprender (o está aprendiendo lentamente). Observaciones similares también son válidas para los sesgos de la neurona de salida. Podemos obtener información similar para las capas anteriores. En particular, tenga en cuenta el término $latex \sigma' (z_j^L)$ en BP2. Esto significa que es probable que $latex \delta^l$ se vuelva pequeño si la neurona está cerca de la saturación. Y esto, a su vez, significa que cualquier entrada de peso a una neurona saturada aprenderá lentamente. Resumiendo En resumen, hemos aprendido que un peso aprenderá lentamente si la neurona de entrada tiene baja activación o si la neurona de salida se ha saturado, es decir, tiene alta o baja activación. Ninguna de estas observaciones es demasiado sorprendente. Aun así, ayudan a mejorar nuestro modelo mental de lo que está sucediendo a medida que una red neuronal aprende. Figura 3: Resumen de las ecuaciones de backpropagation En el siguiente post veremos cómo estas ecuaciones nos permiten dar con el buscado algoritmo, con el que podremos calcula el gradiente de la función de coste. Para mantenerte al día con LUCA, visita nuestra .
24 de octubre de 2019
AI & Data
Transfer Learning en modelos profundos
En el Departamento de Ideas Locas nos encanta trastear con la Inteligencia Artificial, sobre todo si nos piden algo con aplicaciones fuera de lo común. Justo antes del verano estuvimos familiarizándonos con una práctica muy interesante relacionada con el Deep Learning: Transfer Learning para clasificar imágenes con alta precisión. El objetivo en la clasificación de imágenes no es más que es etiquetar una imagen específica de acuerdo con un conjunto de categorías posibles. Desde una perspectiva de aprendizaje profundo, el problema de clasificación de imágenes se puede resolver mediante Transfer Learning o aprendizaje por transferencia. Éste es un método popular en visión artificial porque nos permite construir modelos precisos ahorrando una gran cantidad de tiempo. Con el aprendizaje de transferencia, en lugar de comenzar el proceso de aprendizaje desde cero, se comienza haciendo uso de patrones o modelos pre-entrenados que se han aprendido al resolver un problema diferente. Principios de Transfer Learning Un modelo pre-entrenado es un modelo que fue entrenado con un ingente conjunto de datos de referencia para resolver un problema similar al que queremos abordar. Debido al coste computacional del entrenamiento de tales modelos, así como en la complejidad a la hora de elegir la arquitectura óptima, el Transfer Learning de modelos bien conocidos y precisos se ha convertido es una práctica común (por ejemplo, VGG-16, VGG-19, ResNet-50, SeNet-50, etc.) Supongamos que, por poner un ejemplo, deseamos clasificar rostros de ganado, pero no existen algoritmos liberados que hagan un trabajo adecuado. Parece algo novedoso, pero desde hace un año el Deep Learning ha encontrado en el sector ganadero un campo de aplicación muy interesante, como ya adelantamos en este post. Con Transfer Learning, podemos construir una red neuronal convolucional ya existente y entrenada, comúnmente utilizada para el reconocimiento facial y modificarlo para entrenar rostros de vacas (por ejemplo). Podríamos descargarnos los pesos de los modelos bien conocidos ya mencionados, de varias decenas o cientos de capas, con un elevado nivel de precisión para clasificar rostros de personas y usarlo para identificar con una precisión similar a una determinada cabeza de ganado. Figura 1. Diferencias en el proceso de aprendizaje entre ML tradicional y Transfer Learning. Fuente Las redes convolucionales (que son las arquitecturas típicas para la resolución de este tipo de problema) actúan, al fin y al cabo, como extractores de características tales como los ojos, las orejas y los morros de una vaca o un cerdo haciendo uso de lo que ese modelo aprendió en su origen clasificando rostros de personas famosas. Redes convolucionales Una CNN (Convolutional Neural Network) típica tiene dos partes: una base convolucional, compuesta por una pila de capas convolucionales y de agrupación. El objetivo principal de la base convolucional es extraer características de la imagen; un clasificador, que generalmente está compuesto por capas completamente conectadas. El objetivo principal del clasificador es clasificar la imagen en función de las características detectadas. Una capa totalmente conectada es aquella en la que sus neuronas tienen conexiones con todas las activaciones en la capa anterior. Un aspecto importante de estos modelos de aprendizaje profundo es que pueden aprender automáticamente representaciones jerárquicas de características. Esto significa que las características calculadas por la primera capa son generales y pueden reutilizarse en diferentes problemas, mientras que las características calculadas por la última capa son específicas y dependen del conjunto de datos y la tarea elegidos. A lo largo de la arquitectura hay una transición donde se pasa de extraer los rasgos más generales a los más específicos. Como resultado, la base convolucional de una CNN, especialmente sus capas inferiores (las que están más cerca de las entradas), se refieren a características generales, mientras que la parte clasificadora y algunas de las capas superiores de la base convolucional se refieren a características especializadas. En la siguiente imagen intentamos mostrar las activaciones de algunos de los filtros de la última capa convolucional de la arquitectura VGG-16 dedicada a la clasificación de rostros de un dataset extenso de personas famosas. Como se puede ver (aunque onírico e inquietante) se distinguen perfectamente las características en la que esta capa convolucional se fija en una imagen (ojos, narices, orejas, bocas…). Figura 2. Visualización de algunos de los filtros de la última capa convolucional (Conv5_3) del modelo VGG-16 de clasificación de rostros. Conceptos asociados Pero volvamos a poner el punto de mira en las fuentes del problema que se intenta resolver: la posible escasez de muestras para entrenar a un modelo y la disparidad entre los datos que tenemos entre manos y aquellos que se usaron en su día para entrenar el modelo que vamos a tomar como base. Una definición formal de Transfer Learning es la siguiente (A Survey on Deep Transfer Learning; Tan, C.; et al. 6 Aug 2018): Contamos con un dominio $latex D$ formado por: un espacio de características $latex X$ y una distribución de probabilidad $latex P(X)$ donde $latex x={x_1, ..., x_n}\in X$ . Dado ese dominio $latex D={X, P(X)}$ y una muestra $latex \tau$ que podemos considerar formada por dos componentes: una etiqueta $latex Y$ y una función objetivo $latex f(\cdot)$ tal que $latex \tau={Y, f(\cdot)}$. Esta función es aprendida a través de un entrenamiento basado en datos agrupados por pares $latex {x_i, y_i}$ donde $latex x_i\in X$ e $latex y_i\in Y$ . Entonces la función $latex f(\cdot)$ se puede usar para predecir una etiqueta de una nueva muestra $latex x$ incluso cuando está en las fronteras de la distribución marginal que define $latex D$ . Estrategias Cuando queremos reutilizar un modelo pre-entrenado para tus propias necesidades, se suele comenzar eliminando el clasificador original para posteriormente agregar un nuevo clasificador que se adapta a tus propósitos. Sólo nos queda ajustar tu modelo de acuerdo con una de tres estrategias por la que deberemos apostar: Estrategia 1: Entrenar todo el modelo. En este caso, utiliza la arquitectura del modelo pre-entrenado y lo entrena de acuerdo con su conjunto de datos. Está aprendiendo el modelo desde cero, por lo que necesitará un gran conjunto de datos (y mucha potencia computacional). Estrategia 2: Entrenar algunas capas y deja las otras congeladas. Como se comentó anteriormente, las capas inferiores abstraen características generales (independientes del problema), mientras que las capas superiores tienen capacidad de abstraer características específicas. Por lo general, si tiene un conjunto de datos pequeño y una gran cantidad de parámetros, deberemos tender a dejar más capas congeladas para evitar un overfitting sobre nuestro dataset. Por el contrario, si el conjunto de datos es grande y el número de parámetros es pequeño, puede mejorar su modelo entrenando más capas para la nueva tarea, ya que el overfitting no será un problema que nos afecte. Estrategia 3: Congelar la base convolucional. La idea principal es mantener la base convolucional en su forma original y luego usar sus salidas para alimentar un clasificador propio. En este caso se está utilizando un modelo pre-entrenado como un mecanismo de extracción de características generales, que puede ser útil si tiene poca potencia computacional, su conjunto de datos es pequeño y/o el dataset empleado en entrenar el modelo pre-entrenado cuenta con una distribución muy similar al que tenemos entre manos. Características de dataset en el plano similaridad-tamaño Estrategia a seguir en función del dataset Guía de aplicación de las estrategias Desde una perspectiva práctica, todo el proceso de aprendizaje de transferencia se puede resumir como sigue. Contamos con un conjunto de dominios $latex D_s$ y $latex D_T$ (fuentes y objetivo respectivamente) donde $latex D_s={X_s, P(X_s)}$; $latex D_T={X_T, P(X_T)}$ y tenemos muestras $latex \tau_s$ y $latex \tau_T$ donde $latex \tau={Y, P(Y|X)}$ : Contamos con un gran número de datos, pero éstos son diferentes del conjunto de datos del modelo previamente entrenado. $latex X_s\neq X_T$ y $latex P(X_s)\neq P(X_T)$. El espacio de características en los dominios de la fuente y el objetivo son diferentes. Las distribuciones de probabilidad marginal no coinciden al ser los dominios de las fuentes y del objetivo diferentes. Esta situación la deberíamos abordar desde la estrategia 1. Como tenemos un gran conjunto de datos, puede entrenar un modelo desde cero y hacer lo que quiera. A pesar de la diferencia de conjunto de datos, en la práctica, aún puede ser útil inicializar su modelo a partir de un modelo previamente entrenado, utilizando su arquitectura y pesos. Tenemos un gran conjunto de datos que, además, es similar al conjunto de datos del modelo previamente entrenado. Este es un escenario ideal. Cualquier estrategia funcionará. Probablemente, la opción más eficiente es la estrategia 2. Dado que tenemos un gran conjunto de datos, el sobreajuste no debería ser un problema, por lo que podemos aprender todo lo que queramos. Sin embargo, dado que los conjuntos de datos son similares, podemos salvarnos de un gran esfuerzo de capacitación aprovechando el conocimiento previo. Por lo tanto, debería ser suficiente entrenar el clasificador y las capas superiores de la base convolucional. Nuestro conjunto de datos es pequeño y diferente del conjunto de datos del modelo previamente entrenado. $latex P(Y_s|X_s)\neq P(Y_T|X_T)$. Las distribuciones de probabilidad condicionales de la fuente y el objetivo son diferentes. Nos encontramos ante esta tesitura cuando el número de muestras de una determinada clase está sin balancear. Se suele aproximar a través de diferentes técnicas de over/under-sampling. La única opción que tiene es la estrategia 2. Será difícil encontrar un equilibrio entre la cantidad de capas para entrenar y congelar. Si profundizamos en la congelación, nuestro modelo podría sufrir overfitting. Si permanece en el extremo poco profundo de su modelo, éste no aprenderá nada útil de la nueva distribución marginal. Se deberían considerar seriamente las técnicas de aumento de datos. El conjunto de datos nuevos es pequeño, pero similar al conjunto de datos del modelo previamente entrenado. La estrategia 3 es la más adecuada. Solo necesita eliminar las últimas capas completamente conectadas (capas de salida), ejecutar el modelo previamente entrenado como un extractor de características fijas y luego usar las características resultantes para entrenar a un nuevo clasificador. Esperamos que esta introducción a la reutilización de modelos de Deep Learning para proyectos propios os haya gustado. En siguientes entregas profundizaremos en un ejemplo entretenido de Transfer Learning donde presentaremos algo de código haciendo uso de Keras.
13 de septiembre de 2019
AI & Data
Azure ML Studio con Ubuntu para Deep Learning
En anteriores posts dentro de este blog se ha mostrado cómo se puede hacer uso de Azure ML Studio para realizar algunos experimentos de Machine Learning. Montar un entorno para entrenar algoritmos bien conocidos de ML y entrenar modelos sencillos queda perfectamente explicado en los enlaces anteriores, pero el presente artículo buscamos profundizar un poco más. Tras ver lo simple que resulta crear entornos de trabajo en Azure, decidimos sacar todo el partido de la computación en la nube para nuestros proyectos de Aprendizaje Profundo. Estos proyectos se caracterizan por requerir tiempos de entrenamiento extensos que pueden verse sustancialmente acortados si contamos con el hardware adecuado (como por ejemplo GPUs). GPU es el acrónimo de Unidad de Procesamiento de Gráficos. Las GPUs se usaban originalmente para representar imágenes, videos y animaciones 2D o 3D. Sin embargo su aplicación se ha extendido en los últimos años al Deep Learning. Una GPU realiza cálculos matemáticos de manera muy rápida y libera a la CPU para hacer otras cosas. Mientras que una CPU utiliza algunos núcleos centrados en el procesamiento secuencial en serie, una GPU tiene miles de núcleos más pequeños creados para múltiples tareas en paralelo. Supongamos que necesitamos una instancia bastante potente para un proyecto de Deep Learning relacionado con el entrenamiento de un modelo generativo, de los cuales ya os hablamos en un artículo anterior. En este caso vamos a necesitar una máquina virtual con un Sistema Operativo Ubuntu y, con al menos, dos GPUs potentes para acelerar los cálculos. Figura 1. NVIDIA® Tesla® M60 GPU accelerator. Fuente. De toda la oferta de instancias ofrecidas, en concreto nos servía esta: Standard NV12 Promo con Linux Ubuntu 18.04. A continuación se puede encontrar un resumen de las principales características de la máquina. Cores 12 (E5-2690v3) GPU 2 x M60 GPU (1/2 Physical Card) Memory 112 GB Disk 380 GB SSD Setup del entorno Una vez que se ha creado la máquina virtual, para comenzar con el setup de la instancia, ejecutamos los siguientes comandos en un terminal: sudo apt update sudo ubuntu-drivers sudo ubuntu-drivers autoinstall sudo reboot Si funciona bien, instalará muchos paquetes deb, como nvidia-driver-390, etc. Luego se debe reiniciar la máquina. Si se reinicia con éxito, abra una terminal y use el comando nvidia-smi. Si le muestra correctamente el estado de la GPU de NVIDIA como se muestra a continuación, entonces ya está correctamente instalado Figura 2. Captura del estado de las dos GPUs Tesla M60 asignadas a NV12 Promo. Asegúrate de que nvidia está deshabilitada Si genera una máquina preinstalada en Ubuntu, el controlador nvidia ya está instalado, pero su activación no está garantizada de forma predeterminada. Esto se resuelve mediante los siguientes comandos: sudo prime-select nvidia sudo reboot Después de eso, con nvidia-smi se podrá consultar el estado de las GPUs. Procedemos a instalar CUDNN y CUDA Toolkit Recomendamos primero instalar Anaconda, que para Linux se realiza de la siguiente manera. wget https://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh bash Anaconda3-5.2.0-Linux-x86_64.sh Anaconda añadirá automáticamente las rutas al fichero .bashrc (en el fichero de arranque) si escribes “yes” justo en ese punto de la instalación. En caso de no funcionar, se pueden añadir manualmente editando el fichero: gedit ~/.bashrc Y luego añadimos la ruta: export PATH="/home/$USER/anaconda3/bin:$PATH" Creamos un entorno virtual haciendo uso de Anaconda para poder trabajar sobre él con el siguiente comando: conda create -n myenv python=3.6 anaconda Activamos el entorno myenv: source activate myenv Y ahora ejecutamos lo siguiente : conda install \ cudatoolkit==9.0 \ cudnn=7.1.2 \ h5py Instalación de NVIDIA Collective Communications Library (NCCL) Por último, hay que instalar NCCL para poder hacer que las GPUs de nuestra máquina trabajen de manera compartida. Seguimos las instrucciones indicadas en el siguiente enlace. Para ello, es imprescindible crearse una cuenta en NVIDIA con el fin de poder descargar los recursos necesarios. Elegimos la versión de NVIDIA Collective Communications Library (NCCL) Download Page adecuada a nuestra instancia. En nuestro caso se eligió NCCL v2.4.7, for CUDA 9.0, May 15, 2019, Local installer for Ubuntu 16.04. No se han encontrado incompatibilidades con Ubuntu 18.04. Figura 3. Instaladores necesarios para NVIDIA Collective Communications Library. Tras todos estos pasos, ya tendríamos una potente instancia con Ubuntu sobre la que se podrían definir, entrenar y testear los modelos de Deep Learning que queramos, no sin antes instalar la librería de Aprendizaje Profundo que más nos convenga. Para mantenerte al día con LUCA visita nuestra página web, o síguenos en Twitter, LinkedIn o YouTube.
3 de julio de 2019
AI & Data
Introducción a Modelos Generativos Profundos
Es muy probable que en las últimas semanas se hayan encontrado con el resultado de una investigación realizada por Tero Karrasm, Samuli Laine y Timo Aila por parte de NVIDIA Corporation. En este paper se indica cómo han entrenado un modelo generativo para generar rostros humanos de gente que no existe, y todo esto sólo descargando imágenes públicas de Flickr de alta calidad y, eso sí, un tiempo extenso de entrenamiento con HW de alto rendimiento. Os dejamos un enlace al vídeo generado tras esta investigación que, con total seguridad, les llamará la atención. https://www.youtube.com/watch?v=kSLJriaOumA A Style-Based Generator Architecture for Generative Adversarial Networks. NVIDIA Corporation. Introducción Pero, ¿qué es un modelo generativo? Un modelo generativo es una arquitectura profunda dotada de algoritmos de aprendizaje que tiene como misión aprender cualquier tipo de distribución de datos, todo esto aplicado dentro del paradigma de aprendizaje no supervisado. Todos los tipos de modelos generativos buscan aprender la verdadera distribución de datos del conjunto de entrenamiento y, una vez logrado, ser capaces generar nuevas muestras con ligeras variaciones. No siempre resulta posible abstraer la distribución exacta de nuestros datos, por lo que, haciendo uso de las redes neuronales, se intenta modelar una distribución lo más similar posible a la distribución verdadera. Fig. 1. Distribución de probabilidad aprendida por el modelo generativo y distribución real. Fuente. Para entrenar un modelo generativo, primero se recopilan una gran cantidad de datos en algún campo, como por ejemplo un elevado número de imágenes para, posteriormente, entrenar una arquitectura dotada de un algoritmo capaz de generar datos similares. Ya que nos movemos en un paradigma no supervisado, el científico de datos se ahorra el tiempo de etiquetar los datos, por lo que se cuenta con una gran información con la que alimentar el modelo. Mientras que un modelo discriminativo es un modelo de probabilidad condicionada de un target $latex Y$ dada una variable independiente $latex x$, que matemáticamente queda representado como $latex P(Y|X=x)$. Los modelos generativos permiten calcular la distribución de probabilidad conjunta entre un observable $latex X$ y una variable $latex Y$ tal que $latex P(X, Y)$. Dos arquitecturas profundas se han erigido como los principales pilares para desarrollar modelos generativos: Variational Autoencoders (VAEs) y Generative Adversarial Networks (GANs). Variational Autoencoders Los VAEs intenta maximizar la similitud entre las imágenes generadas y las imágenes con las que han sido alimentadas registro de datos y las GANs se entrenan con la intención de lograr un equilibrio entre un generador y un discriminador. Los VAEs son modelos generativos sencillos cuya aplicación va desde la generación de imágenes hasta la generación de muestras de audio. De manera más precisa, un Autoencoder es un tipo especial de red neuronal cuyo objetivo es hacer coincidir la entrada que se le proporcionó con la salida con la mayor semejanza posible. Este tipo de arquitecturas pueden extraer patrones de las entradas que realizan en las capas más profundas de su topología. El Autoencoder debe comprimir la información para poder reconstruirla a posteriori. Fig. 2. Arquitectura de un Autoencoder. Fuente. Tras un entrenamiento correcto, el Autoencoder aprenderá a representar los valores de entrada en una forma diferente, pero mucho más compacta. De hecho, haciendo un muestreo en la distribución aprendida en el espacio latente de la arquitectura, se es capaz de generar muestras de datos. A esta variación, haciendo uso del conocido como Reparametrizaton Trick, se le conoce como Variational Autoencoder. Fig. 3. Ejemplo de arquitectura de Variational Autoencoder. Véase el sampling de la distribución del espacio latente. En el siguiente vídeo mostramos cómo un Variational Autencoder aprende a reproducir un rostro con el tiempo. Este vídeo fue generado como parte de una investigación sobre la aplicación de estos modelos generativos del equipo de Ideas Locas CDO para la RootedCON 2019 que tuvo lugar el pasado mes de marzo. Podéis consultar los detalles en este enlace a las diapositivas presentadas y en este otro al paper derivado de la investigación. Generative Adversarial Networks Por otro lado, las GANs son el perfecto ejemplo de una red neuronal que constituye un modelo generativo haciendo uso del paradigma de aprendizaje no supervisado para entrenar dos modelos en paralelo en un juego de suma cero. El principio del entrenamiento de este tipo de redes es fácil a la par que ingenioso. Se lleva a cabo un entrenamiento simultáneo de dos arquitecturas que se alimentan la una a la otra: un modelo generativo $latex G$ captura la distribución de los datos y genera muestras a partir de un modelo estadístico, mientras que un modelo discriminador $latex D$, que se comporta como un clasificador. Éste estima la probabilidad de que una muestra provenga bien de los datos con los que se entrena el modelo o de que hay sido generado por $latex G$. El entrenamiento de este tipo de redes busca entrenar $latex D$ para desenmascarar a $latex G$ maximizando la probabilidad de que $latex D$ esté equivocado. Fig. 4. Generative Adversarial Network example. Cuando el modelo discriminador $latex D$, que funciona como un clasificador binario, rechaza un ejemplo producido por el generador, el modelo generador aprende a refinar cada vez mejor la generación de nuevas muestras. De acuerdo con Goodfellow et al. Generative Adversarial Nets, el problema reside en que este tipo de arquitecturas son difíciles de optimizar. Se intenta encontrar un punto de silla en vez de un mínimo de la función, por lo que el proceso de optimización resulta inestable. Esto ha sido todo sobre esta breve introducción a los modelos generativos. Seguiremos profundizando en el potencial que ofrecen este tipo de tecnologías.
6 de junio de 2019
AI & Data
Cómo detectar Fake News con Machine Learning
Las redes sociales han cambiado drásticamente la forma en que las noticias son generadas, difundidas y consumidas por la sociedad, abriendo oportunidades imprevistas, pero también creando desafíos complejos. Las redes sociales, diarios digitales, si bien son fuentes muy importantes de información, permiten la generación de información altamente sesgada, así como facilitar la rápida difusión de la misma. Haciendo un uso fraudulento de las mismas, se pueden generar con facilidad campañas de información no legítimas que pueden afectar la credibilidad de todo el ecosistema de noticias. Una característica única de las noticias en las redes sociales es que cualquier persona puede registrarse como editor de noticias sin ningún tipo de restricción (por ejemplo, cualquiera puede crear una página de Facebook que diga ser un periódico o una organización de medios de comunicación). En consecuencia, muchos medios digitales tradicionales están migrando cada vez más a las redes sociales. Junto con esta transición, no es sorprendente que haya una creciente preocupación acerca de los editores de noticias "falsos" que publican noticias "falsas", y que a menudo las difunden ampliamente utilizando seguidores "falsos". Como la extensa difusión de noticias falsas puede tener un impacto negativo grave en los individuos y en la sociedad, la falta de estrategias de verificación de hechos escalables es especialmente preocupante. No es sorprendente que los esfuerzos de investigación recientes se dediquen no sólo a comprender mejor este fenómeno, sino también a automatizar la detección de noticias falsas o Fake News. Si bien un enfoque totalmente automatizado para el problema de las noticias falsas puede ser bastante controvertido y aún está abierto para el debate. Una pregunta pertinente que nos podemos hacer es: ¿Cuál es el rendimiento de la predicción de los enfoques y funciones actuales para la detección automática de noticias falsas? La mayoría de los esfuerzos existentes en este espacio son trabajos simultáneos, que identifican patrones recurrentes en noticias falsas después de que ya se hayan difundido, o proponen nuevas características para los motores de clasificación. Por lo tanto, es difícil evaluar el potencial que tienen los modelos supervisados entrenados a partir de las características propuestas en estudios recientes para detectar noticias falsas. En este artículo examinamos brevemente dos servicios que, haciendo uso de clasificadores de aprendizaje supervisado, nos permitirán clasificar la naturaleza del contenido digital que consumimos. Qué son las fake news en 2 minutos ¿Qué técnicas de Machine Learning se utilizan para detectar fake news? Cuando se comienzan a investigar las noticias falsas, podemos observar que hay muchas categorías diferentes en las que clasificar la información no verídica. Hay artículos que son descaradamente falsos, artículos que brindan un evento verídico pero que luego hacen algunas interpretaciones falsas, artículos que se pueden clasificar como pseudocientíficos, artículos que en realidad lo son sólo de opinión disfrazados de noticias, artículos satíricos y artículos que consisten principalmente en tuits y citas de otras personas. Es decir, pueden ser clasificados como " sátira", " falso", " engañoso", “ clickbait”, etc. La detección de noticias falsas es un problema de aprendizaje supervisado donde se aplican técnicas de Procesamiento de Lenguaje Natural ( NLP). En este caso nos encontramos con contenido web que puede obtenerse mediante web scrapping, y a cada noticia se la puede etiquetar en base a tantos criterios como se desee. Una de las condiciones para que los clasificadores de noticias falsas logren un buen desempeño es tener suficientes datos etiquetados. Sin embargo, obtener etiquetas en las que confiar requiere mucho tiempo y trabajo de catalogación. Por lo tanto, los métodos semi-supervisados y no supervisados también se pueden utilizar para la agrupación de noticias en una primera aproximación exploratoria. Figura 1. Identificación de Fake News mediante clasificadores (paradigma supervisado) Existen diversos servicios para la detección de Fake News basados en: Verificación de dominios de origen como fuente legítima o poco sesgada; Análisis de patrones en el contenido de la noticia; Búsqueda de keywords para mejorar la búsqueda, categorización y manejo de información; Los modelos de clasificación más utilizados en la detección de noticias falsas son Support Vector Machine (SVM) y Naive Bayes Classifier (NBC). También se utilizan la Regresión Logística (LR) y los modelos basados Árboles de Decisiones. El Deep Learning también tiene cabida. Muchos tipos de modelos de redes neuronales, como los perceptrones multicapa, también funcionan para la detección de noticias falsas. Las Redes Neuronales Recurrentes (RNN) son muy populares en el procesamiento de lenguaje natural, especialmente en la memoria a largo plazo a largo plazo; Long Short Term Memory (LSTM). Un pequeño experimento del Departamento Ideas Locas En el Departamento de Ideas Locas queríamos ver si era sencillo protegernos de este tipo de noticias, por lo que nos pusimos a investigar. Se encontraron dos servicios elegidos para el procesado y clasificación de noticias: Robinho y Fakebox, tras lo cual generamos un breve script en Python que realizaba scrapping de una página web. Facilitada la url, el script obtenía todo el texto de la página. A continuación, se describen brevemente los dos servicios sobre los que nuestro programa trabaja: Robinho El detector de Fake News de Robinho permite detectar y señalar Fake News, Click Baits y noticias extremadamente sesgadas. Hay varias formas de usar este detector de Fake News: Instalando la extensión para Chrome o Firefox, esto verifica las noticias desde el feed de Twitter y Facebook; Hablando directamente con Robinho en Telegram; Haciendo llamada a su JSON API, que es lo de que se ha hecho uso para el presente artículo. Fakebox Fakebox analiza artículos de noticias para evaluar si es probable que estas sean reales o no. Al observar una variedad de aspectos disponibles de un artículo ( título, contenido y url) utilizando modelos de aprendizaje automático integrados, Fakebox puede identificar con éxito noticias falsas con una precisión superior al 95%. El servicio comprueba los siguientes aspectos de un artículo: Título: los títulos pueden ser clickbait o sesgados; Contenido: el contenido textual de un artículo se puede analizar para determinar si está escrito como una noticia real o no; Nombre de dominio: algunos dominios son conocidos por alojar ciertos tipos de contenido, Fakebox conoce los sitios más populares al respecto. Para hacer uso de este servicio es necesario tener Docker instalado en tu equipo. Figura 2. Instrucciones para hacer uso de Fakebox. Abajo se incluye el script en Python que nos permite obtener una respuesta de ambos servicios a una url facilitada como argumento. Como puede observarse, el script es muy sencillo, no sólo en su implementación sino en su uso. Haciendo un GET y POST al siguiente diccionario de urls: url_main_dict = { 'robinho': 'https://robinho.fakenewsdetector.org/predict', 'fakebox': 'http://localhost:8080/fakebox/check' } Facilitando los siguientes parámetros como llamada: params = dict( url=url, content=parrafos, title=title ) https://gist.github.com/eblancoh/8b416e8ede27bce815b9af422974fa24 https://www.youtube.com/watch?v=b2Dd_UF8SLE Se puede obtener el resultado de ambos servicios en formato JSON para acceder fácilmente a los datos. A continuación, tenéis un breve vídeo que muestra cómo funciona el script con tres noticias diferentes, así como el resultado que arroja para cada url facilitada. Si estáis interesados en probar este simple script, podéis descargarlo desde el siguiente repositorio de Github a través del siguiente comando: git clone https://github.com/eblancoh/fakenews.git Las instrucciones para poder usar este repositorio están indicadas en el fichero README.md. Como podéis ver, fabricarse herramientas sencillas basadas en Inteligencia Artificial para protegernos de contenidos no legítimos es muy sencillo con unas mínimas bases de programación. Para mantenerte al día con LUCA visita nuestra
8 de mayo de 2019
AI & Data
¿Qué es overfitting y cómo evitarlo?
En Machine Learning, describimos el aprendizaje de la función objetivo a partir de los datos de entrenamiento como aprendizaje inductivo. Entendemos que la inducción se refiere al aprendizaje de conceptos generales a partir de ejemplos etiquetados específicos, que es exactamente el problema que los algoritmos de Machine Learning supervisado pretenden resolver. La capacidad de inducción de un modelo se refiere a cómo de preciso es un modelo de ML en entender ejemplos específicos que el mismo no vio cuando el algoritmo del que partía estaba siendo entrenado. El objetivo de un buen modelo de aprendizaje automático es generalizar bien los datos de entrenamiento a cualquier dato del dominio del problema. Esto nos permite hacer predicciones en el futuro sobre los datos que el modelo nunca ha visto. Las principales causas cuando se obtienen malos resultados al entrenar diferentes modelos de Machine Learning son el overfitting o el underfitting de los datos. Cuando entrenamos nuestro modelo intentamos ajustar los datos de entrada entre ellos y con la salida. En función de las características de nuestro dataset y de la elección de las muestras, se puede producir overfitting o “sobreajuste” y underfitting o “subajuste”. Estas dos casuísticas no dejan de ser más que la incapacidad de nuestro modelo de generalizar el dataset provisto. Es muy común que, al comenzar a entrenar el modelo, se caiga en el problema del underfitting. Lo que ocurrirá es que nuestro modelo sólo se ajustará a aprender los casos particulares que le enseñamos y será incapaz de reconocer nuevos datos de entrada. En nuestro conjunto de datos de entrada muchas veces introducimos muestras atípicas (o anómalas) o con ruido en alguna de sus dimensiones, o muestras que pueden no ser del todo representativas. Cuando sobre-entrenamos nuestro modelo y caemos en el overfitting, nuestro algoritmo estará considerando como válidos sólo los datos idénticos a los de nuestro conjunto de entrenamiento y siendo incapaz de distinguir entradas buenas como fiables si se salen un poco de los rangos ya preestablecidos. En la siguiente imagen vemos una simplificación a un ejemplo de regresión que nos permite visualizar el problema del underfitting y el overfitting. Encontrar un equilibrio para una correcta generalización se convierte en necesario. Fig. 1: Over-fitting vs Under-fitting vs ideal fit a model. Fuente Se debe encontrar un punto medio ( sweet spot) en el aprendizaje de nuestro modelo en el que nos aseguremos de que no estamos incurriendo en underfitting u overfitting, lo cual a veces puede resultar una complicada tarea. Otros conceptos con los que nos tenemos que familiarizar son los términos bias y varianza. Suelen resultar confusos para los recién llegados al Machine Learning, pero en realidad son muy intuitivos. De manera muy simple, un alto sesgo o bias indican que el modelo sufre de underfitting o subajuste y una alta varianza indica que el modelo sufre de overfitting. Fig. 2: Graphical Illustration of bias-variance trade-off , Source: Scott Fortmann-Roe., Understanding Bias-Variance Trade-off. Fuente En anteriores artículos, sobre todo aquellos basados en tutoriales de Python sobre aplicación de algoritmos de Machine Learning, habréis visto que uno de los pasos que siempre se dan es realizar una partición del dataset en muestras de entrenamiento y testeo. Esta práctica, muy recomendable, nos sirve para monitorizar el entrenamiento de nuestro algoritmo y para poder prevenir errores en el aprendizaje. Al monitorizar el entrenamiento de un algoritmo, siempre se suelen monitorizar los errores de entrenamiento y testeo para prevenir las condiciones de alto bias y alta varianza. Para reconocer este problema deberemos subdividir nuestro conjunto de datos de entrada para entrenamiento en dos: uno para entrenamiento y otro para la validación que el modelo no conocerá de antemano. Esta división se suele hacer del 70-80% para entrenar y 20-30% para validar. El conjunto de validación deberá tener muestras lo más diversas posibles y en cuantía suficiente. Error de entrenamiento: según se incrementa la complejidad del modelo, el modelo tiende a hacer un overfitting sobre los datos del entrenamiento. El error sobre los datos de entrenamiento irá decreciendo cada vez más. Error de testeo: el error sobre el conjunto de validación es alto tanto en el escenario de underfitting como en el de overfitting. Nos interesa monitorizar este error para quedarnos justo en el punto de entrenamiento en el que este es menor. La siguiente imagen ilustra perfectamente este hecho: Fig. 3: Effect of model complexity on error due to bias and variance. Fuente Condición de alto bias: nos encontramos con underfitting en nuestro modelo. Tanto el error de entrenamiento como el de testeo son elevados. Por más que añadamos datos para que el modelo explore, no se mejora el desempeño. Como solución se podría probar aumentar el número de características a analizar o la complejidad del algoritmo. Condición de alta varianza: nuestro modelo está afectado por overfitting. Ajusta muy bien datos de entrenamiento, pero no es capaz de inferir correctamente los datos de validación, por lo que error de testeo es significativamente mayor que error de entrenamiento. Añadir datos variados ayuda a resolver el problema, así como la reducción de la complejidad del modelo. Si el modelo entrenado con el conjunto de test tiene un 90% de aciertos y con el conjunto de validación tiene un porcentaje muy bajo, nos enfrentamos ante un claro caso de overfitting. Si, por el contrario, en el conjunto de validación sólo se acierta un tipo de clase o el único resultado que se obtiene es siempre el mismo, nos encontramos ante un caso de underfitting. Para intentar que estos problemas nos afecten lo menos posible, podemos llevar a cabo diversas acciones: Por un lado, debemos garantizar de que tenemos una cantidad suficiente de muestras tanto para entrenar el modelo como para validarlo. En el caso de movernos en tareas de clasificación, deberemos contar con clases variadas y equilibradas en cantidad: por ejemplo, en caso de aprendizaje supervisado y suponiendo que tenemos que clasificar diversas clases o categorías, es importante que los datos de entrenamiento estén balanceados y sean representativos de todas las clases, para evitar sesgos innecesarios. Sobre los problemas presentados en el aprendizaje desbalanceado ya hablamos en el nuestra anterior serie Machine Learning y astrofísica: clasificando estrellas, galaxias y quasars. Siempre debemos subdividir nuestro conjunto de datos y mantener una porción del mismo para testar el modelo. Esto nos permitirá evaluar el desempeño del algoritmo y también nos permitirá detectar fácilmente efectos del overfitting o underfitting. Debemos prevenir una cantidad excesiva de dimensiones, con muchas variantes distintas, sin suficientes muestras. A veces conviene eliminar o reducir la cantidad de características que utilizaremos para entrenar el modelo. Una herramienta útil para hacerlo es Principal Component Analysis (PCA) o T-distributed Stochastic Neighbor Embedding (t-SNE). Para mantenerte al día con LUCA visita nuestra .
9 de abril de 2019
AI & Data
Machine Learning y astrofísica (II): Clasificación de estrellas, galaxias y quasars con Ramdom Forest
La semana pasada dejamos encaminada la clasificación de diferentes objetos estelares haciendo uso de Random Forest. Tras haber analizado detenidamente el dataset de SDSS al que nos enfrentamos, toca finalizar esta breve serie con el preprocesado de datos, elección de hiperparámetros, entrenamiento y testeo de nuestro modelo. Preprocesado de datos Para facilitar el entrenamiento de nuestro modelo y evitar posibles problemas numéricos, todas las características numéricas del dataset que van a ser usadas en en entrenamiento son normalizadas en el rango [-1,+1]. https://gist.github.com/eblancoh/4cc4763fd59d0fddd1fec2e8d933caab En las siguientes secciones del artículo vamos a realizar una validación cruzada y un ajuste de hiperparámetros del árbol para poder exprimir al máximo el comportamiento de nuestro algoritmo. Optimización de Hiperparámetros (Hyperparameter Tuning) En Machine Learning, normalmente se realizan dos tareas al mismo tiempo: validación cruzada y ajuste hiperparámetros de los modelos. La validación cruzada es el proceso de capacitar a los modelos un conjunto de datos para que puedan probarlos de manera recursiva con la intención de encontrar la configuración que maximice la precisión del mismo. En esta parte del artículo hacemos uso de una combinación de validación cruzada y el ajuste de parámetros con scikit-learn. Tuning usando Random Search Antes de buscar qué combinación de valores de parámetros conduce al modelo más preciso, debemos especificar los diferentes valores candidatos que queremos probar mediante un simple diccionario. En el siguiente código tenemos una serie de valores de parámetros candidatos, que incluyen varias posibilidades para los siguientes hiperparámetros: n_estimators: número de estimadores en el Random Forest. max_features: número máximo de atributos para la partición de nodos; generalmente < número de características en el dataset. max_depth: número de niveles en cada Decision Tree. min_samples_split: número mínimo de datos en un nodo antes de que este se parta hacia el siguiente nivel. min_samples_leaf: número mínimo de datos en un único nodo. criterion: métrica usada para fijar el stopping criteria de los árboles de decisión. Para limitar la búsqueda, primero se ejecuta una validación cruzada de búsqueda aleatoria. Se realiza una búsqueda aleatoria de parámetros usando k=10 veces la validación cruzada ( cv = 10), en 100 combinaciones diferentes ( n_iter = 100) y con todos los núcleos disponibles al mismo tiempo ( n_jobs = -1). Random Search selecciona una combinación de características al azar en lugar de iterar en cada combinación posible. Una mayor n_iter y cv dan como resultado más combinaciones y menos posibilidad de overfitting respectivamente. Mediante el siguiente comando podemos visualizar los mejores parámetros tras este proceso combinado de validación cruzada y finetuning de parámetros: https://gist.github.com/eblancoh/2fcb60c0412a064500d30c8eca732de5 Entrenando el clasificador Random Forest Con los parámetros óptimos de acuerdo a nuestro dataset, ya podemos definir nuestro estimador de clasificación correctamente. https://gist.github.com/eblancoh/e52d304ccea35ceeb5f09812154cdf5e Testeando y evaluando el modelo Nos encontramos ante un claro caso de Aprendizaje Desbalanceado ( Imbalanced Learning), en el que el número de muestras pertenecientes a alguna(s) clase(s) está menos representado en un determinado dataset. Las clases subrepresentadas crean un problema importante: la precisión (es decir, la proporción de muestras de prueba para las que predijimos la clase correcta) ya no es una buena medida del rendimiento del modelo. Los principales métodos de evaluación son sensibles a los datos desbalanceados cuando las muestras de una clase en un conjunto de datos superan a las muestras de la otra clase o clases: La recall, sensitivity o tasa de verdaderos positivos de un clasificador representan las muestras positivas clasificadas correctamente al número total de muestras positivas, y se estima de acuerdo con la siguiente expresión: R = tp/(tp+fn) Donde tp son los verdaderos positivos y fn son los falsos negativos. Además, los valores predictivos, positivos y negativos, reflejan el rendimiento de la predicción. El valor o la precisión de la predicción positiva representa la proporción de muestras positivas que se clasificaron correctamente según el número total de muestras pronosticadas positivas: P = tp/(tp+fp) Donde fp son las muestras clasificadas erróneamente. Como se indicó anteriormente, la precisión puede ser contribuida en gran medida por un gran número de verdaderos negativos si el conjunto de datos está desbalanceado. La F1-score es una mejor medida para usar si se busca un equilibrio entre precisión y la recall en caso de que se observe una distribución de clases desequilibrada en el conjunto de datos. La recall, la precisión y la F1-score se consideran métricas que deben usarse como referencia de la bondad de nuestro clasificador. f1 = 2·(P·R)/(P+R) sklearn.metrics tiene estas métricas soportadas. El orden de las puntuaciones en la lista F1-score corresponde a la forma en que se codificaron las clases, a las que se puede acceder utilizando el atributo .classes_ del clasificador. https://gist.github.com/eblancoh/c635d788f41f21b54767944f3953280b El desempeño de nuestro árbol es bastante bueno sobre nuestro test dataset, con F1-scores superiores al 90% para todas las clases. La tasa más baja de las tres corresponde a de los Quasars, seguramente debido a la subrepresentación de esa clase en el dataset. Debido al enorme volumen de datos y a la complejidad de los mismos, la astrofísica ha tenido, en los últimos años, al Machine Learning como una herramienta imprescindible para múltiples tareas. En el siguiente enlace se resumen algunos de los algoritmos más usados y su aplicación en este campo. Figura 2. The Sloan Digital Sky Survey: Mapping the Universe Unos de los más llamativos, posiblemente, sean tanto el de la aplicación de algoritmos clásicos de Machine Learning como Deep Learning en la clasificación de galaxias según su morfología (Galaxy Zoo Challenge) o el de la corrección de imágenes astrofísicas haciendo uso de Generative Adversarial Networks (GANs).
12 de marzo de 2019
AI & Data
Machine Learning y astrofísica: clasificando estrellas, galaxias y quasars
En el post de hoy vamos a mostrar cómo aplicar un algoritmo de clasificación muy sencillo haciendo uso de Random Forest a través de los estimadores de Scikit-Learn. Aunque ya hemos mostrado algoritmos de clasificación en otras ocasiones en este blog, vamos a cambiar un poco el enfoque del dataset al que apuntamos para realizar la clasificación. Siempre que se quiere aprender a manejar un determinado algoritmo de Machine Learning, se acaba cayendo en los mismos datasets ( Iris, MNIST, PIMA Indians Diabetes…). Sin embargo, hay otros muchos datasets muy interesantes que pueden hacernos menos monótona la visualización de datos, escalado de datos, el entrenamiento y validación del modelo. Incluso, esto nos podría permitir entender mejor las bases algoritmo al que nos enfrentamos gracias a las novedades que nos ofrece nuestro nuevo dataset. Hace ya un tiempo nos encontramos con un conjunto de datos muy interesante, llamado Sloan Digital Sky Survey (SDSS) DR14, relacionado con el mundo de la astrofísica, donde se recogen datos fotométricos de estrellas, galaxias y quasars. Podéis encontrar el dataset en el siguiente enlace. La fotometría es la ciencia que se encarga de la medida de la luz, como el brillo percibido por el ojo humano. La fotometría proporciona una medida directa del flujo de energía recibido de los objetos celestes en un intervalo de longitud de onda. Con los datos de magnitudes y colores en diferentes bandas fotométricas obtenemos información muy valiosa de los objetos observados. Por ejemplo: Permite clasificar estrellas usando un diagrama color-color. El análisis de curvas de luz (variación temporal de su magnitud) nos informa sobre la naturaleza de estrellas que pueden ser variables o sobre parámetros de sistemas binarios o múltiples. También es ampliamente usada para determinar distancias y tamaños de manera exacta y precisa. Pero vayamos paso por paso para el caso que nos ocupa: ¿qué son estrellas, galaxias y quasars? Estrellas: es una esfera luminosa de plasma que mantiene su forma gracias a su propia gravedad. La estrella más cercana a la Tierra es el Sol. Galaxias: son sistemas unidos por la gravedad y formados por estrellas, restos estelares, gas interestelar, polvo y materia oscura. Grosso modo, las galaxias se clasifican de acuerdo con su morfología visual como elíptica, espiral o irregular. Se cree que muchas galaxias tienen agujeros negros supermasivos en sus centros activos. Quasars: también conocidos como QSO o quasi-stellar objects son Núcleos Galácticos Activos (Active Galactic Nucleus - AGN) extremadamente luminosos. Se ha teorizado que la mayoría de las galaxias grandes contienen un agujero negro central masivo con una masa que va desde millones hasta miles de millones de veces la masa de nuestro Sol. En los quásares y otros tipos de AGN, el agujero negro está rodeado por un disco de acreción gaseoso. A medida que el gas cae hacia el agujero negro, la energía se libera en forma de radiación electromagnética, que se puede observar en todo el espectro electromagnético. El poder irradiado por los quásares es enorme: los quásares más poderosos tienen luminosidades miles de veces más grandes que una galaxia entera como la Vía Láctea. Figura 2. Hubble photograph of a quasar ejecting nearly 5,000 light years from the M87 galaxy (ver fuente) Entendiendo los datos Las características para cada objeto, de los cual tenemos 10,000 muestras, son las siguientes: ra, dec - ascensión recta y declinación respectivamente, de acuerdo al sistema ecuatorial de coordenadas. u, g, r, i, z – filtro (sistema fotométrico o magnitudes astronómicas) run, rerun, camcol, field  - descriptores de campos (es decir, 2048 x 1489 píxeles) dentro de la imagen Desplazamiento al rojo o redshift: aumento de la longitud de onda debido al movimiento del objeto astronómico por efecto Doppler. Cabe destacar que esta cantidad también puede ser negativa, lo que indicaría que el objeto se acerca al observador. En este caso nos encontramos con blueshift o desplazamiento al azul. plate – número de placa mjd – modified Julian date of observation fiberid – optic fiber ID Dado que nuestro dataset ya está incorporado en un Pandas dataframe, podemos recurrir a los comandos df.info() y df.describe() para entender las características de nuestro dataset. https://gist.github.com/eblancoh/a74f0c8e6f6edadd64f0c88cbbed97ca Como vemos no hay datos nulos o missing data, por lo que la limpieza del dataset no será necesaria. Visualizando los datosDistribución de objetosen función del redshift y distancia de luminosidad Empecemos por entender la distribución de nuestros objetos en desplazamiento al rojo. Aunque hay múltiples mecanismos físicos que pueden causar corrimiento al rojo, el más conocido es el debido a la expansión métrica del espacio, que explica la famosa observación de los corrimientos al rojo espectrales de galaxias distantes, quasares y nubes gaseosas intergalácticas se incrementan proporcionalmente con su distancia al observador. Este mecanismo es una característica clave del modelo del Big Bang de la Cosmología física para un Universo en expansión. De acuerdo con el siguiente histograma acumulado y normalizado, parece que en nuestro dataset, la mayoría de objetos están contenidos hasta un redshift ∼0.20. Podemos visualizar también la distribución en coordenadas ecuatoriales de nuestros objetos astronómicos también como función del redshift. Representando en proyección polar ascensión recta y declinación y haciendo distinción por las tres clases soportadas en nuestro dataset, podemos ver que no hay una predominancia de distribución en el cielo en función de su clase. Esto nos va indicando que ni la ascensión recta ni la declinación serán atributos a usar en nuestro clasificador. Al realizar un análisis de redshift para cada clase, podremos observar diferencias sustanciales en función de la clase de cada uno de los objetos. https://gist.github.com/eblancoh/a68bf0a377cfcdee91ba216e9c6a7dad Las estrellas detectadas se encuentran “relativamente” cerca de nosotros. Con total seguridad estas estrellas detectadas estarán localizadas en nuestra galaxia o galaxias cercanas. Vemos que el límite de redshift o blueshift de las estrellas está alrededor de ±0.002, pertenecientes a alguna galaxia lejana. Esto equivale a una distancia de luminosidad aproximada de 9 Mpc, lo cual equivale a una distancia de 2.7×1E20 km ¡Una cantidad increíblemente grande, y estamos hablando sólo de nuestra “vecindad” galáctica! Por otro lado, y como resulta lógico, la detección de objetos más lejanos está sesgada hacia los objetos más luminosos, como son los QSOs seguidos por las galaxias. El objeto más lejano de nuestro dataset es un quasar que está a un redshift de 5.35. Este objeto se encuentra a 51682.89 Mpc. Los fotones provenientes de ese objeto estelar salieron hace 12.695 Gyr, cuando el Universo tenía sólo un 8% de la edad que tiene ahora. Si sentís curiosidad y queréis jugar con el redshift, edades y distancias, recomendamos echéis un vistazo a esta calculadora cosmológica. Como podemos ver, el desplazamiento al rojo de cada clase parece un claro elemento diferenciador en la correcta identificación de un objeto. Distribución de objetos en los diferentes filtros A continuación, se ofrecen las funciones de densidad de probabilidad de cada clase en cada uno de los filtros. https://gist.github.com/eblancoh/0bc1b2eaad33d4912dc3eab3a690e608 Sin duda, esta información fotométrica resultará muy valiosa a la hora de entrenar nuestro algoritmo de clasificación. Matriz de correlación Si representamos la matriz de correlación de las variables que tenemos disponibles en el dataset, seremos capaces de reforzar qué variables utilizar en el entrenamiento del modelo Random Forest de clasificación que vamos a abordar. https://gist.github.com/eblancoh/024889112e833be2ec8eebf23e0b96cd Parece que la característica con mayor correlación respecto a la clase es el desplazamiento al rojo, seguido de las bandas g, u y r. Aunque existe alta correlación entre las bandas, vamos a considerar todos los filtros en nuestro algortimo de clasificación. Por ello, para cada objeto, contaremos con 6 atributos que apuntarán a una única etiqueta. Una vez analizados los datos, quedaría realizar una estandarización de los atributos elegidos, una validación cruzada que nos permita encontrar los parámetros ideales - hyperparameter tuning - de nuestro Random Forest, entrenar y testear el modelo sobre este dataset. Estos puntos se abordan en el siguiente post sobre Machine Learning y astrofísica. ¡Os esperamos!
5 de marzo de 2019
AI & Data
Crea un chatbot meteorológico en NodeJS con Microsoft Bot Framework
El pasado 12 de diciembre de 2018, en el último LUCA Talk del año, tuvimos la oportunidad de ver cómo crear un conjunto muy sencillos de chatbots en node.js haciendo uso de Microsoft Bot Framework. Si no pudisteis uniros, en el siguiente enlace tendréis la oportunidad de ver lo sencillo que resulta crear un bot con funcionalidades muy interesantes con unas mínimas nociones de programación en JavaScript. En este artículo, resumiremos los principales pasos necesarios para crear un chatbot a través de los recursos disponibles con Microsoft Bot Framework y terminaremos mostrando cómo crear un chatbot que nos ofrezca el tiempo actual en una ciudad a elección del usuario. Los chatbots son agentes inteligentes, guiados bien por reglas o por Inteligencia Artificial, diseñados para responder automáticamente a las peticiones de usuario a través de interfaces conversacionales. La inteligencia de los primeros reside en la calidad de su programación. Su capacidad de respuesta está limitada a unos casos restringidos. Los chatbots basados en machine Learning, cada vez más numerosos cuentan con funcionalidades basadas en Inteligencia Artificial, como Natural Language Processing. Aprenden conforme el usuario interacciona con ellos. La comunicación suele ser más humana. Desde 1966 con ELIZA, pasando por ALICE y SmarterChild a principios del S.XXI y con la llegada de IBM con Watson, Apple con Siri y Google con Google Now, en los últimos años el mundo de los chatbots y los asistentes virtuales han iniciado un boom al que se suman Amazon con Alexa, Windows con Cortana, Microsoft con Microsoft Botbuilder, Facebook con Chatfuel, principilamente soportados gracias a su integración en múltiples canales generalistas. Los chatbots son un recurso cada vez más usados en la actualidad. ¿Qué promueve actualmente el desarrollo masivo de estas herramientas? Por un lado, la expansión de las aplicaciones de mensajería. Se estima que aproximadamente 2,500M usuarios tienen al menos una app de mensajería. En 2014 se mandaba la nada desdeñable cantidad de 1,300M mensajes/día; en 2016 ya eran unos 7,600M mensajes/día. Además, nos hallamos en una etapa en la que la evolución del Machine Learning y la Inteligencia Artificial está siendo muy rápida y efectiva. Nos encontramos con una sustancial mejora de funcionalidades soportadas por este tipo de servicios, además de que se ha agilizado el despliegue y la eficiencia de estos bots. Cada vez contamos con ecosistemas más robustos, y cada vez más apps de mensajería fomentan el despliegue tanto de bots propios como customizados por el usuario. Figura 1. Aplicaciones destacadas de chatbots Antes de comenzar con la creación de bots, necesitamos satisfacer los siguientes requisitos en nuestro sistema: Tener instalado Node.js Instalar un editor de código como Visual Studio Code Descargar Bot Framework Emulator, para hacer uso de la misma como aplicación que permite testear los bots tanto en localhost como en remoto. En este breve tutorial nos centraremos en pruebas en local. Una vez que tengamos todos los requisitos satisfechos, podremos lanzar nuestro primer bot de manera rápida y sencilla. 1. Abre un terminal con privilegios elevados. 2. Si aún no contamos con un directorio para bots en JavaScript, creamos uno y nos situamos en el mismo. md myJsBots cd myJsBots 3. Nos aseguramos de que la versión de npm está actualizada. npm install -g npm 4. Después, instalamos Yeoman y el generador para JavaScript. npm install -g yo generator-botbuilder 5. A continuación, podemos usar el generador para crear un bot de eco. yo botbuilder Yeoman solicitará alguna información adicional con la que crear el bot. Si optamos por los valores predeterminados, obtendremos una plantilla básica de Chatbot. Escriba un nombre para el bot. (a la elección del usuario) Escriba una descripción. (a la elección del usuario) Elija el idioma del bot. (JavaScript) Elija la plantilla que se va a usar. (Echo) Si decide crear un bot Basic, necesitará un modelo de lenguaje LUIS. No es lo que nos ocupa en este simple tutorial, por lo que nos restringiremos al tipo Echo. Gracias a la plantilla, el proyecto contiene todo el código necesario para crear el bot en esta guía de inicio rápido. Realmente no necesita escribir ningún código adicional. El generador Yeoman crea una aplicación web de tipo restify. Si vamos a la guía de inicio rápido de restify en su documentación, verá una aplicación similar al archivo index.js generado. A continuación se describe cómo se suele estructurar una aplicación de este tipo. Contamos con cuatro pilares principales: los archivos package.json, .env, index.js, bot.js y un fichero .bot. package.json package.json especifica las dependencias del bot y sus versiones asociadas. Esto viene configurado por la plantilla y el sistema. Figura 2. Ejemplo de fichero package.json archivo .env El archivo .env especifica la información de configuración del bot, como el número de puerto, el identificador de aplicación y la contraseña, entre otras cosas. Si utiliza determinadas tecnologías o si usa este bot en producción, deberá agregar las claves o dirección URL específicas a esta configuración. Para este bot de eco, no obstante, no necesita hacer nada aquí ahora mismo; el identificador de aplicación y la contraseña se pueden dejar sin definir en este momento. Para usar el archivo de configuración .env la plantilla necesita incluir un paquete adicional. En primer lugar, obtenga el paquete dotenv de npm: npm install dotenv Figura 3. Ejemplo de fichero .env index.js El archivo index.js configura el bot y el servicio de hospedaje que reenviará las actividades a la lógica del bot. Por lo general, en la parte superior del archivo index.js encontrarán la serie de módulos o bibliotecas que van a ser necesarios para el funcionamiento del mismo. Estos módulos le darán acceso a un conjunto de funciones que tal vez desee incluir en la aplicación. La siguiente parte carga información del archivo de configuración de bot. En este fichero también se incluyen tanto el adaptador del bot como el servidor HTTP y el e stado del bot. En esta parte se configura el servidor y el adaptador que permiten al bot comunicarse con el usuario y enviar respuestas. El servidor escuchará en el puerto especificado en el archivo de configuración BotConfiguration.bot o volverá al 3978 (que resulta el puerto por defecto) para la conexión con el emulador. El adaptador actúa como el director del bot, dirige la comunicación entrante y saliente y la autenticación, entre otras. También se crea un objeto de estado que usa MemoryStorage como proveedor de almacenamiento. Este estado se define como ConversationState, lo que significa simplemente que mantiene el estado de la conversación. ConversationState almacenará en memoria la información que le interesa, que en este caso es simplemente un contador de turnos. Dentro de este archivo o haciendo llamada a uno diferente, se define la lógica del bot con toda la estructura de los diálogos. Archivo .bot El archivo .bot contiene información, incluidos el punto de conexión, el identificador de aplicación, la contraseña y las referencias a los servicios que el bot utiliza. Este archivo se crea automáticamente al iniciar la creación de un bot desde una plantilla, pero puede crear el suyo propio con el emulador u otras herramientas. Puede especificar el archivo .bot que se va a utilizar al probar el bot con el emulador. Figura 4. Ejemplo de fichero .bot Un archivo .bot no es un requisito para crear bots con el SDK Bot Builder. También puede usar appsettings.json, web.config, env, un almacén de claves o cualquier mecanismo que le convenga para realizar un seguimiento de las referencias de servicio y las claves de las que depende el bot. Sin embargo, para probar el bot con el emulador, necesitará un archivo .bot. La buena noticia es que el emulador puede crear un archivo .bot para las pruebas. Si no haces uso de un asistente como Yeoman, puedes fabricarte fácilmente tu propio fichero .bot. Funcionamiento de los bots. Detalles HTTP, avisos en cascada y enriquecimiento de contenido Detalles HTTP Las actividades llegan al bot desde Bot Framework Service mediante una solicitud POST HTTP. El bot responde a la solicitud POST entrante con un código de estado HTTP 200. Las actividades que se envían desde el bot al canal se envían en una solicitud POST HTTP independiente a Bot Framework Service. Esta se confirma de vuelta con un código de estado HTTP 200. Diálogos y avisos de cascada Dada la naturaleza de la interacción solicitud-respuesta, para implementar un aviso se requieren al menos dos pasos en un diálogo de cascada: uno para enviar el aviso y otro para capturar y procesar la respuesta. Si tiene un aviso adicional, en ocasiones puede combinar estos pasos mediante una única función para procesar primero la respuesta del usuario y luego iniciar el siguiente aviso. Un diálogo de cascada se compone de una secuencia de pasos de cascada. Cada paso es un delegado asincrónico que toma un parámetro de contexto de paso de cascada (también llamado step). Se puede controlar el valor devuelto desde un diálogo, por ejemplo dentro de un paso de la cascada de un diálogo. Dentro de un paso de la cascada, el diálogo proporciona el valor devuelto en la propiedad result del contexto del paso. Una cascada avanza paso a paso en la secuencia con que las funciones están definidas en la matriz. La primera función dentro de una cascada puede recibir los argumentos que se pasan al diálogo. Cualquier función dentro de una cascada puede usar la función next para avanzar al paso siguiente sin pedirle al usuario que intervenga. Incorporación de datos adjuntos de tarjetas enriquecidas a mensajes Varios canales, como Skype y Facebook, admiten el envío de tarjetas gráficas enriquecidas a los usuarios con botones interactivos en los cuales el usuario hace clic para iniciar una acción. El SDK proporciona varias clases de generadores de tarjetas y mensajes que se pueden usar para crear y enviar tarjetas. El servicio Bot Framework Connector representará estas tarjetas con un esquema nativo para el canal, compatible con la comunicación multiplataforma. Si el canal no es compatible con las tarjetas, como SMS, Bot Framework hará todo lo posible para representar una experiencia razonable a los usuarios. Veremos a continuación un ejemplo de gestión de contenidos muy sencillo para aprender a definir este tipo de tarjetas enriquecidas. Weather Chatbot Afortunadamente, hay miles de módulos que se pueden agregar a nuestro proyecto y que pueden hacer casi cualquier cosa. En este proyecto, usaremos los módulos de BotBuilder (esta es la biblioteca de Microsoft que le permite crear robots), Restify (le permite crear una API REST en solo unas pocas líneas de código) y, finalmente, Request (que le permite crear hacer peticiones HTTP a otros sitios) para poder obtener el tiempo en el momento actual haciendo llamada tanto a las APIs de OpenWeatherMap como de AEMET. Figura 5. Lógica del bot de consulta de tiempo creado para el tutorial Creamos un archivo app.js en el directorio de su proyecto, este archivo será el punto de entrada de nuestra aplicación. Contendrá toda la lógica de nuestro robot. Abra el archivo que acaba de crear en el modo de edición, ¡es hora de comenzar a programar! Lo primero que debe hacer es cargar los módulos Node.js que necesitaremos var request = require('request'); var restify = require('restify'); var builder = require('botbuilder'); //------------------------------------------------------ // Configuration //------------------------------------------------------ // Loading your environment variables is a one-liner: require('dotenv').config(); Luego, debemos definir algunas variables de configuración. Para ello, leeremos todos los datos que tenemos almacenados en el fichero .env. Lo que nos interesa es OPEN_WEATHER_MAP_APP_ID y AEMET_APP_ID para poder hacer llamadas a las APIs de los servicios de meteorología. // OpenWeatherMap API Key var openWeatherMapAppId = process.env.OPEN_WEATHER_MAP_APP_ID; // AEMET API Key var aemetApiKey = process.env.AEMET_APP_ID; const inMemoryStorage = new builder.MemoryBotStorage(); Dijimos que el robot era en realidad una API web simple, por lo que necesitaremos un servidor HTTP: Ha llegado el momento de crear el robot al indicar el conector que tendrá que usar. El conector es lo que permite al robot interactuar con los usuarios. Hay varios tipos de conectores, incluido el ConsoleConnector que le permite interactuar con el robot directamente desde la consola. Aquí, usaremos el ChatConnector, que permite conectar el robot al Conector de Bot de Microsoft (Skype, Facebook, Slack, etc.) y al Emulador de canales de Framework de Bot (herramienta para simular un servicio de mensajería para realizar pruebas). //------------------------------------------------------- // Setup //------------------------------------------------------- // Setup Restify Server var server = restify.createServer(); server.listen(process.env.port || process.env.PORT || 3978, function () { console.log('%s listening to %s', server.name, server.url); }); Luego tenemos que decirle a nuestro servidor HTTP que es el robot el que responderá las llamadas REST. // Create connector and bot const connector = new builder.ChatConnector({ appId: process.env.MICROSOFT_APP_ID, appPassword: process.env.MICROSOFT_APP_PASSWORD }); // Listen for messages from users server.post('/api/messages', connector.listen()); Para seleccionar las acciones que se realizarán en función de los mensajes que se le envíen, nuestro robot tendrá que determinar qué intención está oculta detrás de cada mensaje. Aquí empezamos a describir la estructura conversacional que queremos que siga nuestro bot. // Declaring the bot and manually setting the memory storage const bot = new builder.UniversalBot(connector, [ (session, results, next) => { session.preferredLocale('es', (err) => { if (err) { session.error('error'); } session.send("Bienvenido al weatherBot de LUCA"); next(); }); }, (session, results) => { builder.Prompts.text(session, 'Por favor, indique para qué ciudad desea conocer el tiempo en este momento'); //next(); }, (session, results, next) => { openweathermap(results.response, function(success, previsions) { if (!success) return session.send('Se ha producido un error en la llamada a la API de OpenWeatherMaps. Por favor, inténtelo de nuevo.'); var message = 'Actualmente en ' + previsions.city + ' tenemos ' + previsions.description + ' con:nn' + 'Temperatura : ' + previsions.temperature + '°Cnn' + 'Grado de humedad : ' + previsions.humidity + '%nn' + 'Velocidad del viento : ' + previsions.wind + 'km/h'; session.send(message); //session.endConversation() }); next(); }, (session, results) => { aemetapi(function(success, weatherMapUrl){ if (!success) return session.send('Se ha producido un error en la llamada a la API de AEMET. Por favor, inténtelo de nuevo.'); //console.log('URL en el diálogo -->' + weatherMapUrl) const msg = new builder.Message(session).addAttachment(createHeroCard(session, weatherMapUrl)); session.send(msg); session.endConversation() }); } ]).set('storage', inMemoryStorage); En el fragmento de código que se está viendo, deberías haber notado que usamos las funciones openweathermap y aemetapi , que aún no están declaradas. Estas funciones se usarán para recuperar el clima de una ciudad usando la API de Open Weather Map y el mapa de isobaras más reciente del Atlántico Norte, aquí está su código: //------------------------------------------------------- // Call to OpenWeatherMap API //------------------------------------------------------- var openweathermap = function (city, callback){ //console.log(openWeatherMapAppId) var url = 'https://api.openweathermap.org/data/2.5/weather?q=' + city + '&lang=es&units=metric&appid=' + openWeatherMapAppId; request(url, function(err, response, body){ try{ var result = JSON.parse(body); if (result.cod != 200) { callback(false); } else { var previsions = { description : result.weather[0].description, temperature : Math.round(result.main.temp), humidity : result.main.humidity, wind: Math.round(result.wind.speed * 3.6), city : result.name, }; callback(true, previsions); } } catch(e) { callback(false); } }); } //------------------------------------------------------- // Call to AEMET API //------------------------------------------------------- var aemetapi = function (callback){ //console.log(aemetApiKey) var url = 'https://opendata.aemet.es/opendata/api/mapasygraficos/analisis/?api_key=' + aemetApiKey; request(url, function(err, response, body){ try{ var result = JSON.parse(body); //console.log(result) if (result.estado != 200) { callback(false); } else { var weatherMapUrl = result.datos; //console.log('URL del mapa --> ' + weatherMapUrl) callback(true, weatherMapUrl); } } catch(e) { callback(false); } }); } La imagen devuelta por la api de AEMET en forma de URL puede embeberse fácilmente ne una HeroCard. El ejemplo de código que se muestra a continuación es muy intuitivo y permite familiarizarse con el manejo de este tipo de recurso fácilmente: //------------------------------------------------------- // Definition of a HeroCard with buttons for redirecting to Weather Map from AEMET //------------------------------------------------------- function createHeroCard(session, url_map) { return new builder.HeroCard(session) .title('Mapas y Gráficos de Europa y Atlántico Norte') //.subtitle('Using a Chart as Image service...') //.text(statusLabel) .buttons([ builder.CardAction.openUrl(session, url_map, 'Presión en superficie en Europa y Atlántico Norte'), builder.CardAction.openUrl(session, 'https://opendata.aemet.es/dist/index.html?#!/mapas-y-graficos/Mapas_de_an%C3%A1lisis_%C3%9Altima_pasada', 'Detalles sobre la API de AEMET') ]); } Con los recursos presentados en este post, os animamos a crear vuestros chatbots. Como haber visto, con un dos llamadas a dos APIs y un diálogo en cascada muy sencillo se ha podido obtener un chatbot capaz de ofrecer una gran cantidad de información personalizada para el usuario. No te pierdas ninguno de nuestros post. Suscríbete a LUCA Data Speaks. https://rawgit.com/google/code-prettify/master/loader/run_prettify.js?autoload=true&skin=sunburst&lang=js'%20defer%20onload='' defer onload='
14 de enero de 2019
AI & Data
¿Qué es Machine Bias?: Los sesgos en Machine Learning
A medida que el Machine Learning ha ido permeabilizando más nuestra sociedad, se han ido descubriendo diferentes sesgos debido a las sutiles, pero nada desdeñables, consecuencias derivadas de su presencia en múltiples algoritmos. Durante los últimos años, con el creciente uso de este tipo de tecnologías, se han ido descubriendo múltiples sesgos de Machine Learning que nos deberían dar qué pensar. Si bien el aprendizaje automático ofrece una fuente de información muy valiosa y nos dota de herramientas de gran utilidad para comprender el mundo que nos rodea, los sesgos descubiertos podrían resultar contraproducentes para el interés general o beneficiar a algunos en detrimento de otros. ¿Sabías que en 2016 se descubrió que algunos de los algoritmos de LinkedIn tenían un sesgo de género que, por ejemplo, recomendaba empleos mejor remunerados a hombres en vez de a mujeres? Esta casuística se veía reforzada por el hecho de que, en la sociedad actual, los puestos de elevada remuneración están predominantemente ocupados por hombres. De igual manera, cuando buscabas a un usuario femenino en esa red, era habitual que el motor de búsqueda te sugiriera un nombre masculino similar. Un año antes, en 2015, un desarrollador de software advirtió que el servicio de reconocimiento facial de Google había etiquetado las fotos de él con un amigo de color como “gorilas”. Google entonó el mea culpa y declaró que estaba “trabajando en soluciones a largo plazo“. Más de dos años después, uno de esos arreglos corresponde a borrar los términos relativos a gorilas y algunos otros primates del léxico del servicio; una torpe solución a todas luces, lo cual ilustra las dificultades a las que se enfrentan las compañías de tecnología cuando buscan ofrecer servicios de calidad fundamentados en aprendizaje automático. Cada vez son más las empresas que están haciendo uso del Machine Learning para ayudar a la toma de aquellas decisiones que puedan beneficiar al crecimiento de su negocio, desarrollando nuevos programas que configuran algoritmos complejos para trabajar con conjuntos de datos sobre los clientes o el mercado en el que se mueven. No cabe duda del gran potencial que albergan estas nuevas tecnologías, que no sólo permite aumentar beneficios, sino también incluso impulsar el desarrollo de esas empresas hacia otros canales de negocio que, de otra manera, no se habrían descubierto. Sin embargo, la falta de experiencia en el correcto desarrollo de estos algoritmos puede llegar a convertirse en un problema real. El sesgo algorítmico es uno de los mayores riesgos porque compromete y pone en duda la totalidad del propósito del Machine Learning. Este sesgo, a menudo pasado por alto, puede conducir a errores costosos y, de no controlarse de manera adecuada, puede llevar a proyectos y organizaciones enteras a tomar decisiones en direcciones erróneas, independientemente del paradigma en el que nuestras herramientas se hayan desarrollado. Los factores que más suelen influir en el desempeño de un algoritmo de Machine Learningson, no sólo el paradigma del algoritmo y su implementación, sino también la calidad y cuantía de los datos utilizados. Un modelo es tan bueno como los datos de los que aprende, y esto se convierte en algo imprescindible para mantener la integridad de las decisiones tomadas por las representaciones aprehendidas por los algoritmos. Entrenar un modelo con datos sesgados en una determinada dirección puede afectar seriamente al desempeño de la herramienta, condenando los resultados obtenidos por el modelo. A esto se le llama Machine bias, del cual ha quedado ampliamente demostrado su dificultad de subsanación. Este sesgo, muy diferente del sesgo estadístico o muestral al que estamos acostumbrados, no es más que la proyección de losprejuicios – que pueden ser inconscientes – de los desarrolladores en los propios algoritmos o de falta de rigor en la correcta recogida de datos para el entrenamiento de los mismos. Ninguna empresa que construya tecnología de Machine Learning está exenta de evitar la incorporación de prejuicios a sus algoritmos. Tanto los algoritmos supervisados como los no supervisados se utilizan para encontrar e identificar tendencias en grandes conjuntos de datos. Los principales pasos para el uso de un algoritmo de Machine Learning son el pre-procesamiento de datos, el ajuste del modelo, la realización de predicciones, la visualización de resultados y la evaluación de su desempeño. Los algoritmos más usados en Machine Learning son aquellos capaces de automatizar un proceso tras haber aprendido a partir de un conjunto de ejemplos conocidos. A esto se le conoce como aprendizaje supervisado, en el que el usuario facilita a un determinado algoritmo pares entrada-salida de ejemplo, siendo el algoritmo el encargado de aprender la relación para posteriormente ser capaz de dar una salida para una entrada jamás vista sin la supervisión de ningún ser humano. En los problemas de aprendizaje supervisado se enseña o entrena al algoritmo a partir de datos que ya vienen etiquetados con la respuesta correcta. Cuanto mayor es el conjunto de datos más aprenderá el algoritmo. Una vez concluido el entrenamiento, se le brindan nuevos datos, ya sin las etiquetas de las respuestas correctas, y elalgoritmo de aprendizaje utiliza la experiencia pasada que adquirió durante la etapa de entrenamiento para predecir un resultado. El otro principal paradigma corresponde al aprendizaje no supervisado, donde sólo se proporcionan los datos de entrada al modelo. Hay múltiples aplicaciones de este tipo de método, aunque pueden resultar más difíciles o complicadas de evaluar. En los problemas de aprendizaje no supervisado el algoritmo es entrenado usando un conjunto de datos que no tiene ninguna etiqueta; en este caso, nunca se le dice al algoritmo lo que representan los datos. La intención es que el algoritmo pueda descubrir por sí solo patrones que ayuden a entender el conjunto de datos. A pesar de lo indicado anteriormente, el Machine bias se puede prevenir y reparar. Para ello es necesario que aquellos que crean y usan sistemas de Machine Learningcomprendan cómo funcionan estos sistemas, monitoricen continuamente la calidad de sus resultados y estén dispuestos a emplear expertos que puedan hacer que esta tecnología funcione de manera óptima. El Machine Learning apenas ha comenzado a mostrar el verdadero valor que puede ofrecer a nuestra sociedad. Nos corresponde a todos, tanto a aquellos que construyen las herramientas como a los que las usan, asegurarnos de que esta tecnología sea de beneficio general, libre de discriminaciones o prejuicios. Por todo ello, en Telefónica nos comprometemos a usar la inteligencia artificial con integridad y transparencia, haciendo especial hincapié en la igualdad, la claridad, la privacidad y la seguridad en todos los mercados en los que opera la compañía. De esta manera, el 30 de octubre de 2018, Telefónica se convirtió en una de las primeras compañías del mundo en fijar unos principios aplicación de IA y de pautas éticas al respecto. No te pierdas ninguno de nuestros post. Suscríbete a LUCA Data Speaks. Para mantenerte al día con LUCA visita nuestra página web, y no olvides seguirnos en Twitter, LinkedIn y YouTube.
4 de diciembre de 2018
AI & Data
Aprende a construir y manejar Redes Neuronales Recurrentes con Keras
En este artículo vamos a hacer una introducción a las Redes Neuronales Recurrentes ( RNN), una clase de redes que tienen la capacidad de predecir datos pertenecientes a secuencias o series temporales. Este tipo de redes tiene múltiples aplicaciones en la actualidad: desde predecir la evolución del precio de la acción en el mercado bursátil hasta en los sistemas de conducción autónomos, permitiendo la anticipación de trayectorias de automóvil para ayudar a evitar accidentes. De manera más general, pueden funcionar en secuencias de longitudes arbitrarias, en lugar de con inputs de tamaño fijo, como ocurría con todas las redes que hemos discutido en los artículos anteriores. Por ejemplo, se pueden introducir oraciones, documentos o fragmentos de audio, por lo que este tipo de redes también resultan cruciales para el Procesamiento del Lenguaje Natural ( NLP), presentes en sistemas de traducción automática, transcripción de discurso a texto o análisis de sentimientos; por ejemplo, leer reseñas de películas y ser capaz de evaluar la sensación del usuario sobre las mismas. Figura 1: Neuronas. En anteriores artículos, realizamos una introducción al manejo de la librería TensorFlow para Deep Learning: • Deep Learning con Python: Introducción a TensorFlow (Parte I), • Deep Learning con Python: Introducción a TensorFlow (Parte II), • Deep Learning con Python: Introducción a TensorFlow (Parte III) una vez asimilados los conceptos básicos asociados al manejo de esta librería y de otras como Keras, os proponemos dos ejercicios de clasificación con algunos datasets no tan habituales en el aprendizaje de técnicas de Machine Learning. La progresión que se vaticina para las redes neuronales recurrentes es enorme, y sin duda en un futuro muy próximo verá refinado su funcionamiento y extendido su campo de aplicabilidad. En este post nos centraremos en hacer uso de un tipo de bloque recurrente llamado LSTM ( Long Short-Term Memory). Este tipo de celda fue propuesto por Sepp Hochreiter y Jürgen Schmidhuber en 1973 (podéis ver la publicación original aquí). Esta propuesta de celda fue refinada con los años por varios investigadores como Alex Graves, Haşim Sak y Wojciech Zaremba. Con las LSTM, el entrenamiento converge más rápido y detecta dependencias a largo plazo en los datos, aspecto que no estaba del todo cubierto con las RNNs. Para profundizar más en el funcionamiento de una LSTM y en las operaciones soportadas por ellas, recomendamos visitar el siguiente enlace. Vamos a mostrar cómo realizar predicción de texto y clasificación de texto haciendo uso de LSTM con la librería Keras. Esta librería es una biblioteca de aprendizaje profundo de código abierto escrita en Python. Es capaz de ejecutarse sobre TensorFlow, Microsoft Cognitive Toolkit o Theano. Diseñado para permitir una rápida experimentación con redes neuronales profundas, es fácil de usar, modular y extensible. En el siguiente Jupyter Notebook mostramos cómo generar texto haciendo uso de celdas LSTM. Para ello nos descargaremos un texto de muestra sobre el que entrenar nuestro modelo. A pesar de que el modelo es bastante simple, tarda algunas horas en entrenar. En el caso de que no dispongáis del tiempo suficiente, tenéis disponible en este enlace el checkpoint del modelo entrenado sobre el texto indicado en la introducción del cuaderno. https://gist.github.com/eblancoh/804f5e1372fe843816f39b96f2f0968b.js'%20defer%20onload='' defer onload=' La clasificación de secuencias es un problema de modelado predictivo en el que se tiene una secuencia de entradas sobre el espacio o el tiempo con la finalidad de predecir una categoría para la secuencia. Lo que hace que este problema sea difícil es que las secuencias pueden variar en longitud, estar compuestas de un vocabulario muy grande, tener símbolos de entrada y pueden requerir que el modelo aprenda el contexto a largo plazo o las dependencias entre símbolos en la secuencia de entrada. En este cuaderno veremos cómo se pueden desarrollar modelos de redes neuronales recurrentes LSTM para problemas de clasificación de secuencias en Python utilizando la biblioteca de aprendizaje profundo Keras. https://gist.github.com/eblancoh/a4b7862addf380d90410f34958d24979.js'%20defer%20onload='' defer onload=' Arriba hemos abordado distintas arquitecturas de clasificación con la estructura Embedding + LSTM, así como un preprocesado de t-SNE del vocabulario de las reseñas que se deseaban clasificar. Al igual que con el Jupyter Notebook anterior, el entrenamiento puede ser bastante extenso, por lo que también tenéis disponible los checkpoints de los modelos para acelerar las pruebas que deseéis realizar.
25 de septiembre de 2018
AI & Data
Aprendizaje Reforzado y Deep Learning en videojuegos clásicos: todo lo que hemos aprendido en un año
Hace aproximadamente un año en este blog se comenzó una serie de artículos en la que se exploraba cómo conseguir que una Inteligencia Artificial aprendiera a desenvolverse con juegos sencillos haciendo uso de algoritmos de Aprendizaje Reforzado. En este post ofrecemos una recopilación de todos los artículos y recursos mostrados sobre Aprendizaje Reforzado y Deep Learning durante los últimos meses. Comenzamos con algoritmos sencillos en entornos muy básicos con pocas variables para posteriormente migrar a juegos más complicados que requerían de arquitecturas profundas para poder entrenar a la IA de manera eficiente y adecuada. Nos encontramos con un campo muy atractivo en el que se exploraba un campo del Machine Learning no tan popular como el Aprendizaje Reforzado, más allá de los típicos algoritmos de aprendizaje supervisado y no supervisado a los que estamos acostumbrados, pues además necesitaba de la aplicación de modelos de redes neuronales profundas dirigidas a la identificación de píxeles en la pantalla. De esta manera se aunaban en un mismo ejercicio algoritmos de Machine Learning, Deep Learning y técnicas de visión artificial apoyándonos en un lenguaje de programación sencillo como Python y en librerías como Universe y OpenAI Gym. A continuación, se resumen las tres series de artículos que se han ido completando en torno a esta temática. 1ª serie "Cómo entrenar a tu IA jugando a videojuegos" En este conjunto de posts se facilita una introducción a las librerías de OpenAI Gym y Universe, a las características de los entornos de juegos clásicos soportados por estas librerías y algunos algoritmos sencillos que permitían hacer aprender a una Inteligencia Artificial desenvolverse en este tipo de entornos. En el primer artículo Cómo entrenar a tu Inteligencia Artificial jugando a videojuegos. Parte 1, preparando la "rejilla de juegos", se ofrecen las instrucciones a seguir para la correcta instalación de todo el entorno que nos permita desplegar todos los entornos de juego y comenzar a investigar con el aprendizaje reforzado. Figura 2: Funcionamiento básico OpenAI En el segundo artículo Cómo entrenar a tu Inteligencia Artificial jugando a videojuegos. Parte 2, observando el entorno introdujimos algunos conceptos básicos asociados tanto a los entornos como al Aprendizaje Reforzado e hicimos nuestro primer intento de entrenar a una IA a ganar a Cartpole con unas pocas líneas de código en Python. En Cómo entrenar a tu Inteligencia Artificial jugando a videojuegos. Parte 3, resolviendo CartPole con Random Search, el tercer post de esta serie, explorábamos nuevos algoritmos de Aprendizaje Reforzado para Cartpole, y se allanaba el camino para introducir el Q-Learning al que, como vimos en los siguientes dos artículos Cómo entrenar a tu Inteligencia Artificial jugando a videojuegos. Parte 4. Aprende Q-Learning con el juego "Taxi", parte 1 de 2 y Cómo entrenar a tu Inteligencia Artificial jugando videojuegos, Parte 5. Aprende Q-Learning con el juego "Taxi", parte 2 de 2, era necesario recurrir en el caso de que tanto el número de acciones como la complejidad del juego aumentase. 2ª serie "Deep Learning vs Atari: entrena tu IA para dominar videojuegos clásicos" Tras la serie "Cómo entrenar a tu AI jugando a videojuegos" y con la intención de mostrar todo lo presentado de una maner más cercana, hicimos el primer capítulo de un webinar de una serie de dos episodios, el cual podéis consultar en el siguiente enlace. En esta segunda serie decidimos migrar a entornos más difíciles en los que intentar enseñar a una IA a desenvolverse con soltura como Breakout y Space Invaders. Todos recordamos estos míticos juegos que, a pesar de su simplicidad, presentan el grado de complejidad suficiente para tener que mezclar algoritmos de Aprendizaje Reforzado y Redes Neuronales Profundas (Deep Q-Learning) Figura 3: diagrama del proceso de aprendizaje de un agente durante el entrenamiento Podéis encontrar todos los detalles asociados al pre-procesado de las imágenes de los juegos, entrenamiento, elección de arquitectura profunda, resultados y lecciones aprendidas en los siguientes artículos: Deep Learning vs Atari: entrena tu IA para dominar videojuegos clásicos (Parte I) Deep Learning vs Atari: entrena tu IA para dominar videojuegos clásicos (Parte II) Deep Learning vs Atari: entrena tu IA para dominar videojuegos clásicos (Parte III) Además, en el siguiente link se facilita el segundo episodio del webinar donde se presenta y resume todo el trabajo realizado con OpenAI Gym en estos dos entornos más complejos. 3ª serie: "Deep Learning con Python: introducción a Tensorflow" Como habéis podido comprobar, para poder seguir todos los posts anteriores es necesario un nivel básico de Python, en especial cuando toca definir las arquitecturas del modelo que queramos entrenar. Os recomendamos encarecidamente leer la serie de artículos "Python para todos": Introducción: Machine Learning con Python para todos los públicos. Python para todos (1): Instalación del entorno Anaconda. Python para todos (2): ¿Qué son los Jupyter Notebook?. Creamos nuestro primer notebook y practicamos algunos comandos fáciles. Python para todos (3): ¿Qué son las librerías?. Preparamos el entorno. Python para todos (4): Empezamos el experimento propiamente. Carga de datos, análisis exploratorio (dimensiones del dataset, estadísticas, visualización etc) Python para todos (5) Final: Creación de los modelos y estimación de su precisión donde podréis refrescar o aprender los conceptos y las técnicas necesarias para abordar un proyecto de Machine Learning con Python. Dado que en el caso que nos ocupa necesitamos manejar recursos de Deep Learning, decidimos hacer uso de la librería TensorFlow, que se encuentra entre las más extendidas dentro de esta disciplina, soportada Python y C++, además de Java y Go entre otros. Además permite distribuir los cálculos en CPU, GPU de forma simultánea. Figura 4: logo de TensorFlow. Fuente. En esta serie de artículos abordamos desde los fundamentos más básicos para familiarizarnos con el uso de la librería, hasta ejemplos de regresión y clasificación de datasets conocidos. También se incluye un ejemplo de clasificación con Keras, una API de alto nivel para Deep Learning haciendo uso de su back-end en TensorFlow. Los tres artículos que sirven como tutorial introductorio los tenéis disponibles a continuación: Deep Learning con Python: Introducción a TensorFlow (Parte I) Deep Learning con Python: Introducción a TensorFlow (Parte II) Deep Learning con Python: Introducción a TensorFlow (Parte III) Esperamos que, tanto si todavía disfrutáis de unos merecidos días de vacaciones como si ya os habéis incorporado a la rutina laboral, disfrutéis de este recopilatorio sobre Aprendizaje Reforzado y Deep Learning. Por nuestra parte, continuaremos “jugando” con las librerías y entornos presentados en estas series en busca de nuevas aplicaciones. Para mantenerte al día con LUCA visita nuestra página web, suscríbete a LUCA Data Speaks o síguenos en Twitter, LinkedIn y YouTube.
23 de agosto de 2018
AI & Data
Deep Learning con Python: Introducción a TensorFlow (Parte I)
En este artículo vamos a realizar una breve introducción al framework TensorFlow, librería sobre la que ya hicimos mención en anteriores posts relacionados con técnicas de Aprendizaje Reforzado ( RL). En el artículo Deep Learning vs Atari: entrena tu IA para dominar videojuegos clásicos (Parte I), podéis encontrar más detalles sobre las posibles aplicaciones de esta librería en proyectos de Deep Learning. Existen múltiples librerías de código abierto enfocadas a su uso para Deep Learning en lenguaje Python. Las más importantes son: TensorFlow, Theano, Keras, Caffe, Lasagne, DSSTNE, PrettyTensor, Torch, mxnet, DL4J, y Microsoft Cognitive Toolkit. Entre las más extendidas se encuentra TensorFlow, la que nos ocupa en este artículo, que soporta Python y C++, además de Java y Go entre otros. Además permite distribuir los cálculos en CPU, GPU de forma simultánea y escalado horizontal. Figura 1: logo de TensorFlow. Fuente. Para poder seguir satisfactoriamente este artículo y los que están por venir, necesitaréis un nivel básico de programación en Python. Os recomendamos encarecidamente leer la serie de artículos Python para todos: Introducción: Machine Learning con Python para todos los públicos. Python para todos (1): Instalación del entorno Anaconda. Python para todos (2): ¿Qué son los Jupiter Notebook?. Creamos nuestro primer notebook y practicamos algunos comandos fáciles. Python para todos (3): ¿Qué son las librerías?. Preparamos el entorno. Python para todos (4): Empezamos el experimento propiamente. Carga de datos, análisis exploratorio (dimensiones del dataset, estadísticas, visualización etc) Python para todos (5) Final: Creación de los modelos y estimación de su precisión donde podréis refrescar o aprender los conceptos y las técnicas necesarias para abordar un proyecto de Machine Learning con Python. Si queremos hacer uso de TensorFlow, es necesario tener el entorno correctamente configurado con todos los complementos y librerías necesarias. En este post quedan indicados todos los pasos requeridos para dejar listo un entorno que te permita realizar proyectos de Deep Learning con TensorFlow. Comenzaremos explorando los fundamentos que sustentan esta librería. El primer paso que daremos es habituarnos a la sintaxis básica de TensorFlow. Para ello, incluiremos algunos Jupyter Notebooks con la intención de describir al máximo el uso de las bases de este framework. Aquí tenemos el primer ejemplo. Ahora que ya sabemos definir constantes, tensores y ejecutar sesiones, daremos el siguiente paso al manejo y generación de grafos en TensorFlow. En TensorFlow, los grafos se definen como el conjunto de nodos conectados entre sí a través de aristas o arcos, que representan relaciones entre elementos de un conjunto. Cada uno de esos nodos se identifica como una operación con un tipo posible de entrada y que puede generar un determinado resultado. Generalmente, en TensorFlow, primero se define un grafo y posteriormente se ejecuta. A continuación, mostraremos unos ejemplos sencillos en Python y se analizará cómo hace uso TensorFlow de un grafo. Empezaremos creando un grafo con dos nodos constantes que llegan a un tercer nodo, que a su vez hará la suma de esos dos valores y devolverá el resultado de la misma. En TensorFlow podemos encontrarnos con dos tipos de tensores en un grafo: variables y placeholders, por lo que ahora toca definir y familiarizarnos con estos objetos. Podremos almacenar en forma de variable los valores de los pesos y los biases a lo largo de toda la sesión. Durante el proceso de optimización, TensorFlow hará un tuning de los parámetros del modelo. Las variables siempre deben ser inicializadas en nuestro código. Los placeholders se definen originalmente como elementos vacíos, de los que iremos haciendo uso durante el entrenamiento del modelo. Al crear estos objetos se debe definir su dimensión y el data type que van a soportar. Hemos aprendido a manejar todos los elementos o piezas que nos permitirán trabajar con TensorFlow como sesiones, grafos, variables y placeholders. Con estos recursos seremos capaces de construir nuestras primeras neuronas, que sentarán las bases para crear las primeras redes neuronales, a las que podremos incorporar algoritmos de regresión, clasificación o aprendizaje reforzado. En los siguientes posts avanzaremos en esa dirección. Como ya se indicó anteriormente, para hacer funcionar los modelos, primero construiremos el grafo, después iniciaremos la sesión y finalmente alimentaremos la arquitectura creada con datos de entrada correctamente procesados. Deep Learning con Python: Introducción a TensorFlow (Parte I) Deep Learning con Python: Introducción a TensorFlow (Parte II) Deep Learning con Python: Introducción a TensorFlow (Parte III)
9 de mayo de 2018
AI & Data
Deep Learning vs Atari: entrena tu IA para dominar videojuegos clásicos (Parte I)
Hace unos meses, comenzamos una serie de posts en este mismo blog donde explicamos cómo entrenar una Inteligencia Artificial ( IA ) para que lograra superar algunos juegos. Si este tema te interesa, te invitamos a registrarte al webinar "Domina los videojuegos clásicos con OpenAI y Machine Learning" donde explicaremos en directo todo sobre el OpenAI Gym, desde su instalación hasta su funcionamiento, y también mostrando ejemplos con Python y técnicas sencillas de Aprendizaje Reforzado (RL). Será una serie de dos capítulos, que se estrenará el 24 de abril a las 16:00 CET. Registrate aquí. Figura 1. Atari 2600 lanzada al mercado en septiembre de 1977 Antes de continuar con estos nuevos capítulos, te animamos a que leas los artículos: Parte 1. Cómo entrenar a tu Inteligencia Artificial jugando a videojuegos, preparando la “rejilla de juegos”. Parte 2. Cómo entrenar a tu Inteligencia Artificial jugando a videojuegos, observando el entorno. Parte 3. Cómo entrenar a tu Inteligencia Artificial jugando a videojuegos, resolviendo CartPole con Random Search. Parte 4. Cómo entrenar a tu Inteligencia Artificial jugando a videojuegos. Aprende Q-Learning con el juego “Taxi”, parte 1 de 2. Parte 5. Cómo entrenar a tu Inteligencia Artificial jugando videojuegos. Aprende Q-Learning con el juego “Taxi”, parte 2 de 2. En los cuales podrás encontrar una introducción a OpenAI y sus librerías Universe y Gym para luego continuar desarrollando algunas soluciones simples de aprendizaje para entornos sencillos y bien conocidos como CartPole o el juego Taxi. Estos artículos te ayudarán a instalar el entorno y las librerías necesarias, así como a sentar las bases necesarias para afrontar los próximos posts. En esta nueva serie continuaremos con OpenAI pero esta vez nos enfrentaremos a retos mayores, tanto en la complejidad de los juegos (entorno a resolver) como en el diseño del agente de IA (técnicas de Aprendizaje Reforzado). El objetivo final será ofrecer una introducción al entrenamiento de una Inteligencia Artificial el cual se sea capaz de superar estos entornos de videojuegos con más dificultad siguiendo la estrategia de prueba y error. La base del entrenamiento se realizará sobre el clásico videojuego Breakout de Atari, aunque las soluciones que propondremos se podrán extrapolar a otros con más o menos las mismas características. Aunque hemos aumentado la dificultad de los entorno, para la solución que vamos a ofrecer necesitaremos juegos en los que el escenario no sea demasiado complejo, sin demasiados objetos en movimiento que analizar y que la secuencia de movimientos sea sencilla, por ejemplo: izquierda, derecha y disparo. Dentro de este límite de complejidad podemos encontrar clásicos como Space Invaders o Ms. Pac-Man. Figura 2. Captura de ejecución de los entornos OpenAI Gym de los juegos Breakout, SpaceInvaders y MsPacMan. Estos juegos arcade se podían jugar en la consola Atari 2600, desarrollada en 1977, la cual resultó ser un éxito de ventas durante más de una década y, sin duda, marcaron la infancia y adolescencia de muchos de nosotros. La simplicidad de los entornos de juego desarrollados para esta consola, ha permitido que estos “mundos” se conviertan en plataformas para estudio y aplicación de la teoría de IA y técnicas de aprendizaje automático. Esta sencillez permite que cada frame de un juego se pueda definir por estado en un espacio de observaciones relativamente manejable y un conjunto de acciones reducido. Como hemos comentado antes, el videojuego que hemos elegido para realizar nuestra PoC es Breakout. Este videojuego arcade clásico fue creado en 1976 por Nolan Bushnell y Steve Bristow pero implementado inicialmente por Steve Wozniak (no os perdáis la fantástica historia detrás del diseño de Breakout que implica a Steve Jobs y Steve Wozniak) que tiene como finalidad romper todos los ladrillos que ocupan la mitad superior de la pantalla con una pelota la cual rebota, utilizando para ello una especie de pala o raqueta. La pala, controlada por el jugador, puede moverse tanto a la derecha como a la izquierda de la pantalla dentro de los límites del juego. Figura 3. Captura de ejecución del entrenamiento en el entorno Breakout en OpenAI Gym. El objetivo del jugador es devolver la pelota hacia los ladrillos para irlos destruyendo uno a uno, evitando que la pelota se pierda por la parte inferior de la pantalla, lo cual provocaría la pérdida de una vida. El juego se reanuda cuando todos los ladrillos se han golpeado o cuando el jugador ha perdido las cinco vidas de las que nos ofrece el juego al inicio de la partida. Gracias al gran número de entornos soportados ( aquí podéis ver la lista de todos ellos), volvemos a usar OpenAI y Gym el cual esta vez se encargará de suministrar las capturas de los fotogramas del juego para posteriormente poder identificar y ubicar los pixeles en la pantalla los cuales corresponderán a los diferentes elementos del juego (como por ejemplo la posición de la pelota, de la pala, etc). El entorno Gym que hemos utilizado se llama Breakout-v0 y presenta las siguientes características: Un espacio de observación (env.observation_space), que puede representarse por un estado del juego, es una matriz height x width x deep = 210 x 160 x 3, representa los valores de píxel de una imagen en el juego. La tercera dimensión se reserva a los valores de rgb definidos en cada píxel para una paleta de colores de 128 bytes. El espacio de acciones (env.action_space.n) se define para este juego como un grupo de 4 enteros [0, 1, 2, 3]. La correspondencia de estos enteros con las acciones permitidas se puede obtener por medio de env.unwrapped.get_action_meanings(), que devuelve ['NOOP', 'FIRE', 'RIGHT', 'LEFT']. Figura 4. Información sobre el entorno Breakout-v0 en OpenAI. Fuente. La interacción con de la herramienta OpenAI Gym se puede resumir en los siguientes pasos: cargar el entorno deseado desde la librería; obtener un estado aleatorio inicial; aplicar una cierta acción en el entorno derivada de una cierta política; esta acción derivará en un nuevo estado del juego; se obtendrá una recompensa tras aplicar la acción elegida, así como como un indicador de si la partida ha finalizado y el número de vidas restantes. Al intentar entrenar una IA en entornos como Breakout con estrategias como, por ejemplo, random search, nos damos cuenta que es una tarea totalmente inabarcable, en especial cuando se proporcionan al agente estados o conjuntos de estados de elevado tamaño y complejidad, como en esta caso. Por lo tanto necesitamos que nuestro agente utilice una política de acciones adecuada que nos permita aproximar satisfactoriamente la función Q(s,a) que maximice la recompensa a obtenida tras la aplicación una acción a dado un estado s. Pero, ¿cómo podemos lidiar con la complejidad asociada a un conjunto de estados del entorno y aproximar esa función? Usando Deep Neural Networks dotadas de algoritmos de Q-Learning, más conocidas como Deep Q-Networks ( DQN). Para este tipo de entrenamiento, las redes neuronales a usar son las llamadas Convolutional Neural Networks, las cuales han demostrado a lo largo de la historia del Deep Learning ser arquitecturas con un excelente comportamiento a la hora de reconocer y aprender patrones basados en imágenes. Este tipo de redes toman los valores de los píxeles de los fotogramas que se le entregan como input. Las representaciones de las entradas se irán abstrayendo en mayor medida según se profundice en la arquitectura, y terminarán reducidas a una capa densa final con un número de salidas igual al espacio de acciones del entorno (4 en el caso de Breakout-v0). Figura 5. Detalle de las capas de la Red Neuronal Convolucional utilizada en nuestro proyecto Breakout. Optimizar una red neuronal resulta tarea complicada, altamente dependiente de la calidad y cantidad de los datos con los que se entrene el modelo. La dificultad de optimizar la red también vendrá dada por la arquitectura de la misma, entendiendo que a mayor número de capas y dimensionalidad, se tendrá que abordar la optimización de un mayor número de pesos y biases. Como recordatorio, la función Q(s,a) se define como: Si conociéramos perfectamente la función Q(s, a), sería inmediato conocer la acción a realizar en base a una política definida: En cierto modo, la red neuronal intentará predecir su propio output a través del uso recurrente de esta fórmula dirigida a modificar la función Q(s, a). Sin embargo, ¿qué nos garantiza, mediante la toma de muestras dada una política de toma de decisiones, que logremos hacer converger nuestra función Q hacia la verdadera? Realmente ninguna, nada nos salva de que la función nunca converja hacia la solución óptima. Existen diferentes técnicas que permitirán que esa función se aproxime de manera correcta, cuya explicación dejaremos para la segunda parte de esta serie. Durante el entrenamiento de este tipo de arquitecturas, la estrategia más conveniente es dar a nuestro algoritmo un conjunto de estados iniciales pre-procesados a partir de los cuales se obtendrá una acción beneficiosa para el agente, que recogerá la recompensa fruto de esa acción y el siguiente estado alcanzado en el entorno, con el que volverá a alimentar el modelo y así sucesivamente. Ahora que ya tenemos una base de redes neuronales profundas y Q-learning, en el siguiente post ofreceremos el resultado del entrenamiento propio para Breakout y Space Invaders. Además, daremos más detalles sobre la implementación de la arquitectura del modelo, las estrategias usadas para hacer converger la solución y las tareas de pre-procesado de imágenes necesarias para poder entrenar la red de manera más eficiente. Nuestro modelo será una DQN construida que haga uso de la librería TensorFlow. Ahora mismo estamos inmersos en el entrenamiento del modelo. Aquí dejamos un avance de las capacidades actuales de nuestro agente: Como puedes ver, ¡nuestra IA ya es capaz de obtener 101 puntos en un episodio! Esto lo ha podido conseguir tras haber sido entrenada durante algo más de 1900 episodios y haber procesado casi 2.5e7 estados durante una semana de entrenamientos. Dejaremos el modelo entrenando más tiempo para poder dejaros en el próximo artículo, un vídeo en el que se muestre cómo nuestro agente es capaz de golpear todos los ladrillos antes de perder las 5 vidas de las que disponemos y además os daremos otro en el que se muestre un modelo entrenado en Space Invaders. ¡Te esperamos en la siguiente entrega de esta nueva serie!
18 de abril de 2018