sábado, 7 de diciembre de 2019

Artificial Neural Nets con Keras Framework. Functional API


En artículos anteriores mostramos como programar de forma sencilla redes neuronales (MLP, CNN y RNN) basándose en el API secuencial de Keras sobre un backend Tensorflow. 

Sin embargo dicho API se queda corto a la hora de programar redes más complejas como Ensemble NN, Autoencoders, GAN o Reinforce Learning donde múltiples inputs/outputs y Neural Nets tienen que combinarse entre si para la consecución el objetivo de aprendizaje.

Recordemos que Keras es Keras es una API de redes neuronales de alto nivel, escrita en Python y capaz de funcionar sobre TensorFlow, CNTK o Theano. Fue desarrollado con el objetivo de permitir una rápida experimentación. 

Retomado el caso de experimentación anterior, que podreis encontrar en el artículo Artificial Neural Nets con Keras Framework compararemos la diferencia en la estructura del código utilizando el API Sequential y el Functional del framework para después ver su aplicación en el ensamblado de modelos neuronales.

Vamos a ver las diferencias de implementación de una red simple MLP en ambos modelos para analizar las diferencias.

Codificación de una Red MLP basado en el API secuencial.

model = Sequential()
model.add(Dense(best_model['conf'][0], input_shape=(input_dimension,)))
model.add(Activation('relu'))
model.add(Dropout(best_model['conf'][4]))
model.add(Dense(best_model['conf'][1]))
model.add(Activation('relu'))
model.add(Dropout(best_model['conf'][4]))
model.add(Dense(output_dimension))
model.add(Activation('softmax'))

Codificación de una Red MLP basado en el API Funcional.

input_1 = Input(shape=(input_dimension,))
x = input_1
x = Dense(best_model['conf'][0])(x)
x = Activation('relu')(x)
x = Dropout(best_model['conf'][4])(x)
x = Dense(best_model['conf'][1])(x)
x = Activation('relu')(x)
x = Dropout(best_model['conf'][4])(x)
output = Dense(output_dimension, activation = 'softmax' )(x)
model = Model([input_1], output)
Como podemos comprobar el modelo secuencial se comporta como un a lista a la que vamos añadiendo elementos, capas al modelo, que se ejecutarán de forma lineal, mientras que en el enfoque funcional actúan como una formulación matemática o programática  y = F(x).

Pero si ambos APIs obtienen al final el mismo resultado, ¿para que necesitamos el API funcional?

La respuesta la encontramos en los modelos complejos que no tienen solución en la implementación secuencial del Framework. 

Para ello vamos a utilizar el siguiente ejemplo.


¿Como implementaríamos la siguiente ANN sin el API Functional?



La respuesta es sencilla, no se puede, ya que contradice el principio de linealidad de la agregación en las listas.


Implementación de un Modelo Ensamblado con Functional API.

Los modelos ensamblados se basan en aprovechar la combinación de modelos sub-óptimos para conseguir modelos optimizados minimizando el consumo de recursos y maximizando la capacidad aprendizaje de cada modelo esperando que cada modelo aprenda sobre elementos no cubiertos del otro complementándose entre sí. 

Para ello lo primero que necesitamos es importar las clases que van a sustituir a Sequential Class.  

from keras.models import Model 
from keras.layers import Dense, Activation, Dropout, Input, Conv2D, MaxPooling2D, Flatten 
from keras.layers.merge import concatenate

Tras esto compondremos nuestro modelo tras generar las adaptaciones en los datos necesarias para compatibilizar los dataframes con las input de redes MPL y CNN. 
Esta información como el código ejecutable con todos los parámetros está disponible en github en formato .ipynb.

Primero crearemos el modelo MLP:
input_1 = Input(shape=(input_dimension,)) 
x = input_1 
x = Dense(best_model['conf'][0][0])(x) 
x = Activation('relu')(x) 
x = Dropout(best_model['conf'][4])(x) 
x = Dense(best_model['conf'][0][1])(x) 
x = Activation('relu')(x) 
x = Dropout(best_model['conf'][4]) (x)
Tras esto creamos el modelo CNN: 
input_2 = Input(shape=cinput_dimension) 
y = input_2 
y = Conv2D(filters = best_model['conf'][1][0], kernel_size = best_model['conf'][1][1],activation = 'relu', data_format = 'channels_last',padding='same')(y) 
y = MaxPooling2D(best_model['conf'][1][2])(y) 
y = Flatten()(y)y = Dense(cdense)(y) 
y = Activation('relu')(y)
Y finalmente concatenamos las salidas en un modelo único:
y = concatenate([x,y])                         
output = Dense(output_dimension, activation='softmax')(y) 
model = Model([input_1, input_2], output)
Una vez que tenemos esto solo nos faltaría seleccionar el optimizador y la función de perdida y ejecutar el entrenamiento del modelo. en nuestro caso utilizamos un optimizador Adam, crossentropy como función de pérdida y como medida de validación usaremos la "accuracy" o porcentaje de aciertos.

# loss function for one-hot vector
# use of adam optimizer
# accuracy is a good metric for classification tasks
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# train the network
model_history = model.fit(x = [x_train, c_train], y = y_train, batch_size = best_model['conf'][2], epochs = best_model['bepoch'])
Finalmente comprobaremos los resultados obtenidos con los datos de entrenamiento y de test (no usados durante el entrenamiento del modelo) y comprobaremos su adecuación al problema.
print ('Train : Loss(%s) - Accuracy: (%s)' %(model_history.history['loss'][-1], model_history.history['acc'][-1]))
Train : Loss(0.48775133260613274) - Accuracy: (0.787648013750955)

print ('Train : Loss(%s) - Accuracy: (%s)' %(model_history.history['loss'][-1], model_history.history['acc'][-1]))
 Test : Loss(0.5097890445164271) - Accuracy: (0.8085714343615941)


Siguientes pasos

Una vez que ya conocemos el uso del API Funcional de Keras con Tensorflow lo que os queda es analizar cada una de los tipos de redes existente y decidir cuál es la necesaria para resolver cada problema concreto y lanzarse a disfrutar de la experimentación.

  • Autoencoders: se usan para validar la adecuación de un conjunto de datos contra un patrón de comportamiento. Aplica a problemas de detección de anomalías en procesos, sensorización o fraude.
  • Generative Adversarial Networks: Se basa en aprender a "falsificar" datos. Esto es, la generación de Fake Data. Si bien puede sonar raro, su aplicación es muy potente ya que permite generar conjuntos de datos validados para entornos de simulación para test de carga y stress de sistemas y aplicaciones tan conocidas como el coloreados de imágenes o el "DeepFake".
  • Reinforce Deep Learning: Si bien está en sus estadíos iniciales las Redes DQNN permiten la resolución de problemas, principalmente en el espacio continuo, imposibles o muy complicados de modelar por sistemas de Q-Learning tradicionales.  

Como siempre, todo el código completo de los experimentos se pueden encontrar en github.

miércoles, 21 de agosto de 2019

Artificial Neural Nets con Keras Framework



En el siguiente articulo mostraremos como programar de forma sencilla redes neuronales (MLP, CNN y RNN) basándose en el API secuencial de Keras sobre un backend Tensorflow.

Keras es una API de redes neuronales de alto nivel, escrita en Python y capaz de funcionar sobre TensorFlow, CNTK o Theano. Fue desarrollado con el objetivo de permitir una rápida experimentación.

Todo el código completo se puede encontrar en github - https://github.com/e2its/Neural-Nets-practica

Requisitos

  • Python 3.6
  • Jupyter notebooks
  • Pandas 0.25
  • numpy 1.16.4
  • scikit-learn 0.21.2
  • keras 2.2.4
  • tensorflow 1.14

Descripción

Para los experimentos usaremos un conjunto de datos orientados al reconocimiento de la actividad. Estos se basan en la información aportada por 3 sensores de forma que podamos realizar un análisis de patrones (5 series temporales consecutivas en intervalos de 250 ms) para detectar la actividad física que se esta realizando (sitting, bending, cycling, lying, walking, standing) mediante tres tipos de redes neuronales MLP (Multi-Layer Perceptron), CNN (Convolutional Neural Nets) y RNN (Recurrent Neural Nets).

En cada archivo, cada fila corresponde a 5 mediciones (en orden temporal con una diferencial de 250 ms) conteniendo la siguiente información: 


No hay texto alternativo para esta imagen
Add caption
(0) avg_rss12, var_rss12, avg_rss13, var_rss13, avg_rss23, var_rss23

(+250ms) avg_rss12_1, var_rss12_1, avg_rss13_1, var_rss13_1, avg_rss23_1, var_rss23_1

(+500ms) avg_rss12_2, var_rss12_2, avg_rss13_2, var_rss13_2, avg_rss23_2, var_rss23_2

(+750ms) avg_rss12_3, var_rss12_3, avg_rss13_3, var_rss13_3, avg_rss23_3, var_rss23_3

(+1000ms) avg_rss12_4, var_rss12_4, avg_rss13_4, var_rss13_4, avg_rss23_4, var_rss23_4

donde avg y var son la media y los valores de varianza respectivamente.



La descripción y los datasets originales pueden ser encontrados en el siguiente enlace Activity Recognition system based on Multisensor data fusion (AReM) Data Set


Lo primero que tenemos que hacer es cargar los datasets de entrenamiento y test desde github.


No hay texto alternativo para esta imagen
Tras esto codificaremos los datos de actividad, nuestras Labels (ATYPE), como valores numéricos aptos para los modelos de Redes Neuronales.


No hay texto alternativo para esta imagen
Normalizamos los datos para que los modelos trabajen de forma más óptima con media a 0 y desviación estándar a 1.


No hay texto alternativo para esta imagen

Y finalmente convertimos nuestros datasets a arrays Numpy ('x_' para características e 'y_' para labels) :


No hay texto alternativo para esta imagen

En este punto ya estamos listos para operar con las distintas redes a través del API Secuencial de Keras.


Multilayer Perceptron (MLP)


Una red multilayer perceptron (MLP) es una clase de red neuronal artificial que consta de al menos tres capas de nodos: una capa de entrada, una capa oculta y una capa de salida.
A excepción de los nodos de entrada, cada nodo es una neurona que utiliza una función de activación no lineal. MLP utiliza una técnica de aprendizaje supervisado llamada backpropagation para el entrenamiento.

Para implementar una red MLP lo primero que tenemos que hacer es adaptar los arrays Numpy a las estructuras de datos que espera el modelo. En nuestro caso no hay que hacer ningún cambio ya que el modelo espera exactamente la estructura base definida anteriormente.

(41888,30)

Para localizar el mejor modelo implementamos un análisis de varias configuraciones basadas en iteraciones sobre distintos valores de los parámetros clave del modelo.

No hay texto alternativo para esta imagen

Y ejecutamos los modelos.

No hay texto alternativo para esta imagen

Tras esto seleccionamos el mejor modelo

No hay texto alternativo para esta imagen

siendo la estructura de dicho modelo:

No hay texto alternativo para esta imagen

y los resultados obtenidos tras la re-ejecución del modelo:

No hay texto alternativo para esta imagen

Convolutional Neural Nets (CNN)


Una red neuronal convolucional es un tipo de red neuronal artificial donde las neuronas corresponden a campos receptivos de una manera muy similar a las neuronas en la corteza visual primaria (V1) de un cerebro biológico.Este tipo de red es una variación de un perceptron multicapa, sin embargo, debido a que su aplicación es realizada en matrices bidimensionales, son muy efectivas para tareas de visión artificial, como en la clasificación y segmentación de imágenes
Como redes de clasificación, al principio se encuentra la fase de extracción de características, compuesta de neuronas convolucionales y de reducción de muestreo. Al final de la red se encuentran neuronas de perceptron sencillas para realizar la clasificación final sobre las características extraídas. La fase de extracción de características se asemeja al proceso estimulante en las células de la corteza visual. Esta fase se compone de capas alternas de neuronas convolucionales y neuronas de reducción de muestreo.

Las redes convolucionales asemejan el proceso de reconocimiento visual cerebral, pero ¿es aplicable a nuestros experimento?. Para ello solo tenemos que transformar nuestro conjunto de datos en una estructura bidimensional con sentido, esto es, convertirla en una estructura similar a un array de imágenes. Esto lo conseguimos modificando la dimensionalidad de nuestro dataset de la siguiente forma.

No hay texto alternativo para esta imagen

consiguiendo una estructura de este tipo

(41888, 6, 5, 1)

Implementando la misma secuencia de pasos que para las redes MLP pero adaptados a una modelo de extracción de características (convolución) y reducción (pooling).


No hay texto alternativo para esta imagen
No hay texto alternativo para esta imagen

Seleccionamos el mejor modelo

No hay texto alternativo para esta imagen
siendo la estructura de dicho modelo


No hay texto alternativo para esta imagen

y los resultados obtenidos tras la re-ejecución del modelo:

No hay texto alternativo para esta imagen

Recurrent Neural Nets (RNN)


Red neuronal que integra bucles de realimentación, permitiendo a través de ellos que la información persista durante algunos pasos ó épocas de entrenamiento, (epochs) a través conexiones desde las salidas de las capas, que “incrustan” (embedding) sus resultados en los datos de entrada. Las conexiones entre nodos forman un gráfico dirigido a lo largo de una secuencia temporal. Funciona como una red con múltiples copias de sí misma, cada una con un mensaje a su sucesor. Cuanto más larga sea la secuencia temporal a analizar, mayor será el número de capas debe desenrollar, puede aparecer el problema de desvanecimiento del gradiente (Vanishin Gradient). Esto se soluciona incorporando capas de tipo LSTM o GRU que permiten el backpropagation through time conectando eventos que aparezcan muy alejados en los datos de entrada, sin que su peso se diluye entre las capas.

Al igual que ocurría con las redes neuronales convolucionales, para trabajar con las redes RNN, también tenemos que adaptar la estructura y dimensionalidad de los datos a la requerida para este tipo de modelos.

(41888, 6, 5)


No hay texto alternativo para esta imagen

Implementando la misma secuencia de pasos que para las redes MLP pero adaptados a una modelo de recurrente basado en unidades LSTM.

No hay texto alternativo para esta imagen

No hay texto alternativo para esta imagen

Seleccionamos el mejor modelo

No hay texto alternativo para esta imagen

siendo la estructura de dicho modelo

No hay texto alternativo para esta imagen


y los resultados obtenidos tras la re-ejecución del modelo:

No hay texto alternativo para esta imagen

Como hemos presentado en este articulo, la implementación de Redes Neuronales con el API secuencial de Keras es tan sencilla como útil, sin embargo, esta limitada. En próximos artículos introduciremos nuevos tipos de redes Neuronales más complejas junto con el API funcional de Keras, de mayor versatilidad y potencia.

Todo el código completo de los experimentos se pueden encontrar en github.

martes, 11 de abril de 2017

Clasificador MultiClase - H2O.ai (General Linear Model)


Como vimos en los anteriores artículos de Blog  estamos realizando un  experimento de clasificación lo suficientemente complejo para permitirnos probar las distintas tecnologías existentes en el mercado y las diferencias relativas a los siguientes 4 puntos:

1) Performance
2) Consumo de recursos
3) Dificultad para implementar la algorítmica asociada
4) Tiempo de aprendizaje

Para la realización del experimento estamos utilizando un conjunto de datos de caracteres alfanuméricos en formato imagen (28x28) provistos por Google para la realización del curso "Deep Learning by Google" a través de la plataforma Udacity.

Es un conjunto de caracteres NOMINST en distintas formas y formatos simulando lo que sería un reconocimiento de escritura natural sobre 10 clases de caracteres y etiquetados por una clase de 0 a 9. de esta forma la correspondencia será:
A - 0
B - 1
C - 2
 D - 3 
E - 4
F - 5
G - 6
H - 7
I  - 8
J - 9


Para estos experimentos trabajamos con un conjunto de 200.000 datos de entrenamiento y  dos conjuntos de validación y test de 10.000 sucesos. 

Todos los conjuntos han sido previamente mezclados aleatoriamente para el experimento y almacenados en fichero con formato cpickle para reproducir las mismas circunstancias en todos los experimentos.

H2O.ai 3.10

H2O es una plataforma de análisis predictivo y aprendizaje automático de código abierto, trabaja en memoria, de forma distribuida, siendo rápida y escalable, lo que permite construir modelos de aprendizaje automático para grandes conjuntos de datos de un forma rápida y comprensible y proporcionando una producción fácil de los modelos en un entorno empresarial.


El código básico de H2O está escrito en Java. El analizador de datos de H2O tiene inteligencia incorporada para adivinar el esquema del dataset entrante y admite la ingesta de datos de múltiples fuentes en varios formatos.

Los algoritmos que aporta se reducen en las siguientes familias:


AL igual que Spark ™ implementa el concepto de hyper-parámetros que nos puede servir para optimizar los distintas configuraciones del modelo de aprendizaje, dentro de unos rangos, buscando de una forma cómoda la mejor configuración de estos reduciendo bastante el tiempo de codificación.


En nuestro caso utilizaremos el algoritmo GLM para nuestro experimento.

Inicialización

Lo primero que vamos a hacer es cargar las librerías y módulos necesarios.



# These are all the modules we'll be using later. Make sure you can import them # before proceeding further. 

from __future__ import print_function
import matplotlib.pyplot as plt

import numpy as np
import os
import pickle as pickle
import time
import sys
import h2o

# Config the matplotlib backend as plotting inline in IPython
%matplotlib inline

Y Cargamos la imagenes empaquetadas en un fichero cPickle.
Nos basamos en un conjunto de imagenes 28X28 NOMINST (provisto por Google) de distintos tipos de fuentes para los caracteres y etiquetas asociadas:



[A,B,C,D,E,F,G,H,I,J] = [0,1,2,3,4,5,6,7,8,9]

os.chdir('d:/Data/Gdeeplearning-Udacity')
pickle_file = 'notMNIST.pickle'

with open(pickle_file, 'rb') as f:
  save = pickle.load(f)
  train_dataset = save['train_dataset']
  train_labels = save['train_labels']
  valid_dataset = save['valid_dataset']
  valid_labels = save['valid_labels']
  test_dataset = save['test_dataset']
  test_labels = save['test_labels']
  del save  # hint to help gc free up memory
  print('Training set', train_dataset.shape, train_labels.shape)
  print('Validation set', valid_dataset.shape, valid_labels.shape)
  print('Test set', test_dataset.shape, test_labels.shape)

Como vemos nuestras imágenes y etiquetas han sido perfectamente cargadas y estamos listos para trabajar con ellas.


Training set (200000, 28, 28) (200000,)
Validation set (10000, 28, 28) (10000,)
Test set (10000, 28, 28) (10000,) 

GLM on H2O.ai

Lo primero que vamos a realizar es la inicialización del entorno H2O.


h2o.init()

Obteniendo la siguiente salida:

Checking whether there is an H2O instance running at http://localhost:54321..... not found.
Attempting to start a local H2O server...
; OpenJDK 64-Bit Server VM (Zulu 8.17.0.3-win64) (build 25.102-b14, mixed mode)
  Starting server from D:\Anaconda3\h2o_jar\h2o.jar
  Ice root: C:\Users\Public\Documents\Wondershare\CreatorTemp\tmp73wyrjxi
  JVM stdout: C:\Users\Public\Documents\Wondershare\CreatorTemp\tmp73wyrjxi\h2o_e2its_started_from_python.out
  JVM stderr: C:\Users\Public\Documents\Wondershare\CreatorTemp\tmp73wyrjxi\h2o_e2its_started_from_python.err
  Server is running at http://127.0.0.1:54321
Connecting to H2O server at http://127.0.0.1:54321... successful.
Warning: Your H2O cluster version is too old (3 months and 9 days)! Please download and install the latest version from http://h2o.ai/download/
H2O cluster uptime: 06 secs
H2O cluster version: 3.10.0.9
H2O cluster version age: 3 months and 9 days !!!
H2O cluster name: H2O_from_python_e2its_gbwxm1
H2O cluster total nodes: 1
H2O cluster free memory: 3.530 Gb
H2O cluster total cores: 8
H2O cluster allowed cores: 8
H2O cluster status: accepting new members, healthy
H2O connection url: http://127.0.0.1:54321
H2O connection proxy: None
Python version: 3.5.2 final


Ahora pasamos a inicializar las Variables y convertilas en H2OFrames para poder trabajar con el modelo.

def reformat(dataset, labels):
  dataset = dataset.reshape((-1, image_size * image_size)).astype(np.float32)
  # Map 0 to [1.0, 0.0, 0.0 ...], 1 to [0.0, 1.0, 0.0 ...]
  labels = labels.reshape((-1, 1)).astype(str)
  #labels = (np.arange(num_labels) == labels[:,None]).astype(np.float32)
  return dataset, labels

tf_train_dataset, tf_train_labels = reformat(train_dataset, train_labels)
tf_valid_dataset, tf_valid_labels = reformat(valid_dataset, valid_labels)
tf_test_dataset, tf_test_labels = reformat(test_dataset, test_labels)


train_dataset_h2o = h2o.H2OFrame(python_obj=tf_train_dataset)
   .cbind(h2o.H2OFrame(python_obj=tf_train_labels).asfactor())

Parse progress: |█████████████████████████████████████████████████████████| 100%
Parse progress: |█████████████████████████████████████████████████████████| 100%

valid_dataset_h2o = h2o.H2OFrame(python_obj=tf_valid_dataset)
   .cbind(h2o.H2OFrame(python_obj=tf_valid_labels).asfactor())
test_dataset_h2o = h2o.H2OFrame(python_obj=tf_test_dataset)
   .cbind(h2o.H2OFrame(python_obj=tf_test_labels).asfactor())

Parse progress: |█████████████████████████████████████████████████████████| 100%
Parse progress: |█████████████████████████████████████████████████████████| 100%
Parse progress: |█████████████████████████████████████████████████████████| 100%
Parse progress: |█████████████████████████████████████████████████████████| 100%

Y liberamos Memoria

#deleting non used memory
train_dataset = []
train_labels = []
test_dataset = []
test_labels = []
valid_dataset = []
valid_labels = []


Experimentos


Probaremos ahora a definir y entrenar el modelo.

image_size = 28
num_labels = 10
from h2o.estimators.glm import H2OGeneralizedLinearEstimator
glm_multi_v1 = H2OGeneralizedLinearEstimator(model_id='glm_v1',family='multinomial',solver='AUTO',\
                                            nfolds=4,alpha=0.0 ,lambda_=0.001,\
                                            score_each_iteration = True,early_stopping=True, seed = 1234)

predictors_X = train_dataset_h2o.col_names[:-1] 
results_y = train_dataset_h2o.col_names[-1]  
start=time.time()
glm_multi_v1.train(x=predictors_X, y=results_y, \
     training_frame=train_dataset_h2o, \
     validation_frame=valid_dataset_h2o)
stop = time.time()
print('time %s' % (stop-start))

glm Model Build progress: |███████████████████████████████████████████████| 100%
time 853.3969919681549

Una de las ventajas del entorno H2O es que interactivamente podemos obtener información del modelo llamando a la variable que almacena el modelo.

glm_multi_v1


Model Details
=============
H2OGeneralizedLinearEstimator :  Generalized Linear Modeling
Model Key:  glm_v1
GLM Model: summary

family link regularization number_of_predictors_total number_of_active_predictors number_of_iterations training_frame
multinomial multinomial Ridge ( lambda = 0.001 ) 7850 7840 50 py_1_sid_a4c1
ModelMetricsMultinomialGLM: glm
** Reported on train data. **

MSE: 0.16822519190326915
RMSE: 0.4101526446376631

ModelMetricsMultinomialGLM: glm
** Reported on validation data. **

MSE: 0.17262832959326135
RMSE: 0.41548565509926016

ModelMetricsMultinomialGLM: glm
** Reported on cross-validation data. **

MSE: 0.1718545212776373
RMSE: 0.41455339979022887
Cross-Validation Metrics Summary: 
mean sd cv_1_valid cv_2_valid cv_3_valid cv_4_valid
accuracy 0.8328426 0.0012030 0.8338638 0.8344597 0.8330132 0.8300337
err 0.1671574 0.0012030 0.1661362 0.1655403 0.1669868 0.1699663
err_count 8357.75 52.34113 8299.0 8304.0 8346.0 8482.0
logloss 0.6341491 0.0043168 0.6347954 0.6262802 0.6322534 0.6432675
max_per_class_error 0.2134856 0.0013493 0.2154245 0.2122186 0.2110312 0.2152681
mean_per_class_accuracy 0.8328531 0.0012293 0.8338676 0.8345117 0.8330554 0.8299777
mean_per_class_error 0.1671469 0.0012293 0.1661324 0.1654883 0.1669446 0.1700222
mse 0.1718562 0.0007704 0.1714213 0.1706311 0.17177 0.1736027
null_deviance 230261.66 318.8281 230044.92 231011.97 230169.4 229820.34
r2 0.9791685 0.0001137 0.9792296 0.979303 0.9792477 0.9788939
residual_deviance 63413.84 354.82706 63419.875 62832.19 63200.05 64203.246
rmse 0.4145534 0.0009282 0.4140305 0.4130752 0.4144514 0.4166565
Scoring History: 
timestamp duration iteration negative_log_likelihood objective
2017-02-12 21:29:41 0.000 sec 0 460517.0185988 2.3025851
2017-02-12 21:29:43 2.443 sec 1 230975.2526532 1.1548763
2017-02-12 21:29:46 5.043 sec 2 182336.1420963 0.9116807
2017-02-12 21:29:49 8.408 sec 3 162076.0761888 0.8103804
2017-02-12 21:29:52 11.771 sec 4 149946.0303992 0.7497302
--- --- --- --- --- ---
2017-02-12 21:32:11 2 min 30.693 sec 46 123698.3410792 0.6184917
2017-02-12 21:32:15 2 min 34.064 sec 47 123610.0715447 0.6180504
2017-02-12 21:32:18 2 min 37.420 sec 48 123565.0645532 0.6178253
2017-02-12 21:32:21 2 min 40.786 sec 49 123486.6772247 0.6174334
2017-02-12 21:32:25 2 min 44.153 sec 50 123384.3931645 0.6169220
See the whole table with table.as_data_frame()

Generamos las predicciones para el conjunto de datos de Test y obtenemos el performance:

glm_multi_v1.predict(test_dataset_h2o[:-1])
accuraccy = (glm_multi_v1.predict(train_dataset_h2o[:-1])[0] == train_dataset_h2o[-1])
print('Train Accuracy: %s'% (accuraccy.sum()/ accuraccy.nrows))
accuraccy = (glm_multi_v1.predict(valid_dataset_h2o[:-1])[0] == valid_dataset_h2o[-1])
print('Valid Accuracy: %s'% (accuraccy.sum()/ accuraccy.nrows))
accuraccy = (glm_multi_v1.predict(test_dataset_h2o[:-1])[0] == test_dataset_h2o[-1])
print('Test Accuracy: %s'% (accuraccy.sum()/ accuraccy.nrows))

glm prediction progress: |████████████████████████████████████████████████| 100%
Train Accuracy: 0.836965
glm prediction progress: |████████████████████████████████████████████████| 100%
Valid Accuracy: 0.8332
glm prediction progress: |████████████████████████████████████████████████| 100%
Test Accuracy: 0.8986

Resultados:

Time = 853.3969919681549
Training dataset Accuracy = 0.836965
Valid dataset Accuracy = 0.8332
Test dataset Accuracy = 0.8986


Los resultados obtenidos son muy similares a los obtenidos con los modelos anteriores pero con un consumo de recursos y tiempo de aprendizaje muy equilibrado.

Desde el punto de vista de rendimiento


El algoritmo, desde el punto de vista de recursos presenta comportamientos mucho mejores con respecto a las pruebas realizadas anteriormente. El consumo de recursos de memoria es muy bueno (2,6 Gb)  y consigue aprovechar el 100% de rendimiento en CPU de la máquina.

 

Conclusión

Si bien la tecnología aportada es menos potente a priori que Spark ™, en base al catálogo de características, los resultados para este problema son similares (incluso superiores) pero con unos tiempos de aprendizaje y consumo de recursos mucho mas eficientes.  Dado su alto grado de interactividad lo hace indicado para todo tipo de problemas y, en base a su alta capacidad de escalabilidad (sobre cluster propio o sobre un cluster Spark ™)  junto con su especialización puede ser muy valido para sistemas en producción donde la homogeneidad tecnológica y la escalabilidad sean el factor diferencial.  A nivel curva de aprendizaje y complejidad es similar a Spark ™ y, por tanto, no es especialmente intuitivo si bien la gran cantidad de información que aporta de forma sencilla mejora sustancialmente dicha percepción. Cabe destacar que todos estos factores le han hecho ser parte del famoso Cuadrante mágico de Gartner 2017 en el apartado de herramientas asociadas a la Ciencia de Datos.