Uso del Machine Learning para predecir el tiempo: Parte 3

    Este es el artículo final sobre el uso del Machine Learning en Python para realizar predicciones de la temperatura media en función de los datos meteorológicos meteorológicos recuperados de Weather Underground, como se describe en la parte uno de esta serie.

    El tema de este artículo final será construir un regresor de red neuronal utilizando la biblioteca TensorFlow de código abierto de Google . Para obtener una introducción general a TensorFlow, así como una discusión sobre los métodos de instalación, consulte el excelente tutorial sobre redes neuronales de TensorFlow publicado por Mihajlo Pavloski.

    Los temas que cubriré en este artículo incluyen:

    • Comprensión de la teoría de las redes neuronales artificiales
    • API de estimador de alto nivel de TensorFlow
    • Creación de un DNNRegressor para predecir el clima

    Comprensión de la teoría de las redes neuronales artificiales

    En el último artículo (parte 2) describí el proceso de construcción de un modelo de regresión lineal, una venerable técnica de Machine Learning que subyace a muchas otras, para predecir la temperatura media diaria en Lincoln, Nebraska. Los modelos de regresión lineal son extremadamente poderosos y se han utilizado para hacer predicciones numéricas, así como categóricas, desde mucho antes de que se acuñara el término “Machine Learning”. Sin embargo, la técnica tiene algunas críticas, principalmente en torno a su supuesto estriado de una relación lineal entre la variable dependiente y la variable independiente.

    Existe un número incontable de otros algoritmos en la industria de la ciencia de datos y el Machine Learning que superan este supuesto de linealidad. Una de las áreas de enfoque más populares en los últimos años ha sido la aplicación de redes neuronales a una amplia gama de problemas de Machine Learning. Las redes neuronales tienen una forma poderosa de utilizar técnicas de aprendizaje basadas en operaciones lineales y no lineales.

    Las redes neuronales están inspiradas en neuronas biológicas en el cerebro que trabajan en una red compleja de interacciones para transmitir, recopilar y aprender información basada en un historial de la información que ya se ha recopilado. Las redes neuronales computacionales que nos interesan son similares a las neuronas del cerebro en que son una colección de neuronas (nodes) que reciben señales de entrada (cantidades numéricas), procesan la entrada y transmiten las señales procesadas a otros agentes aguas abajo en la red. El procesamiento de señales como cantidades numéricas que pasan a través de la red neuronal es una característica muy poderosa que no se limita a las relaciones lineales.

    En esta serie, me he centrado en un tipo específico de Machine Learning llamado aprendizaje supervisado , que simplemente significa que los modelos que se entrenan se construyen utilizando datos que tienen resultados objetivo conocidos que el modelo está tratando de aprender a predecir. Además, el tipo de predicciones que se realizan son valores reales numéricos, lo que significa que estamos tratando con algoritmos de predicción regresores.

    Gráficamente, en la siguiente imagen se muestra una red neuronal similar a la que se describe en este artículo.

    La red neuronal que se muestra arriba contiene una capa de entrada en el extremo izquierdo que representa dos entidades, x1 y x2, que alimentan la red neuronal. Esas dos características se alimentan a la red neuronal, que se procesan y transmiten a través de dos capas de neuronas, que se conocen como capas ocultas. Esta representación muestra dos capas ocultas y cada capa contiene tres neuronas (nodes). La señal luego sale de la red neuronal y se agrega a la capa de salida como un único valor numérico predicho.

    Permítanme tomarme un momento para explicar el significado detrás de las flechas que significan que los datos se procesan de un node a otro a través de las capas. Cada flecha representa una transformación matemática de un valor, comenzando en la base de la flecha, que luego se multiplica por un peso específico para esa ruta. Cada node dentro de una capa recibirá un valor de esta manera. Luego se suman todos los valores que convergen en el node. Es este agregado de multiplicar por pesos y sumar los productos lo que define las operaciones lineales de una red neuronal que mencioné anteriormente.

    Después de realizar la suma en cada node, se aplica una función especial, no lineal, a la suma, que se muestra en la imagen de arriba como Fn (…) . Esta función especial que introduce características no lineales en una red neuronal se denomina función de activación . Es esta característica no lineal provocada por las funciones de activación que dan su poder a las redes neuronales multicapa. Si no fuera por la no linealidad agregada al proceso, entonces todas las capas se combinarían algebraicamente en una operación constante que consiste en multiplicar las entradas por algún valor de coeficiente plano (es decir, un modelo lineal).

    Muy bien, todo está bien, pero espero que te estés preguntando en el fondo de tu mente … está bien, Adam, pero ¿cómo se traduce esto en un algoritmo de aprendizaje? Bueno, la respuesta más directa a eso es evaluar las predicciones que se están haciendo, la salida del modelo “y”, a los valores esperados reales (los objetivos) y hacer una serie de ajustes a los pesos de una manera que mejore la general precisión de la predicción.

    En el mundo de los algoritmos de Machine Learning regresor, uno evalúa la precisión utilizando una función de costo (también conocida como “pérdida” u “objetivo”), es decir, la suma de errores cuadrados (SSE). Tenga en cuenta que generalicé esa afirmación a todo el continuo del Machine Learning, no solo a las redes neuronales. En el artículo anterior, el algoritmo Ordinary Least Squares logró precisamente eso, encontró las combinaciones de coeficientes que minimizaban la suma de los errores cuadrados (es decir, mínimos cuadrados).

    Nuestro regresor de red neuronal hará exactamente lo mismo. Iterará sobre los datos de entrenamiento alimentando valores de características, calculará la función de costo (usando SSE) y hará ajustes a los pesos de una manera que minimice la función de costo. Este proceso de empujar características iterativamente a través del algoritmo y evaluar cómo ajustar las ponderaciones en función de la función de costo es, en esencia, lo que se conoce como optimización del modelo.

    Los algoritmos de optimización de modelos son muy importantes en la construcción de redes neuronales robustas. A medida que se alimentan ejemplos a través de la arquitectura de la red (es decir, el ancho y la profundidad) y luego se evalúan contra la función de costo, se ajustan los pesos. Se dice que los modelos están “aprendiendo” cuando la función del optimizador identifica que se realizó un ajuste de peso de una manera que no mejora (baja) la función de costo, que se registra con el optimizador para que no ajuste los pesos en ese dirección de nuevo.

    API de estimador de alto nivel de TensorFlow

    La biblioteca TensorFlow de Google consta de algunas API, siendo la más popular la API Core, que le brinda al usuario un conjunto de herramientas de bajo nivel para definir y entrenar esencialmente cualquier algoritmo de Machine Learning mediante operaciones simbólicas. Esto se conoce como TensorFlow Core. Si bien TensorFlow Core es una API increíble con una amplia capacidad de aplicación, me centraré en una API más nueva y de nivel superior que desarrolló el equipo de TensorFlow y que se conoce colectivamente como la API Estimator.

    El equipo de TensorFlow desarrolló la API de Estimator para que la biblioteca sea más accesible para el desarrollador cotidiano. Esta API de alto nivel proporciona una interfaz común para train(...)modelos, evaluate(...)modelos y predict(...)resultados de casos desconocidos similares a (e influenciados por) la popular biblioteca Sci-Kit Learn, que se logra mediante la implementación de una interfaz común para varios algoritmos. Además, integradas en la API de alto nivel hay una gran cantidad de mejores prácticas, abstracciones y capacidad de escalabilidad de Machine Learning.

    Toda esta bondad del Machine Learning genera un conjunto de herramientas implementadas en la clase básica de Estimator, así como varios tipos de modelos predefinidos que reducen la barrera de entrada para usar TensorFlow para que se pueda aplicar a una gran cantidad de problemas (u oportunidades) cotidianos ). Al abstraer gran parte de los aspectos mundanos y manuales de cosas como escribir ciclos de entrenamiento o lidiar con sesiones, el desarrollador puede enfocarse en cosas más importantes como probar rápidamente múltiples modelos y arquitecturas de modelos para encontrar el que mejor se adapte a sus necesidades.

    En este artículo describiré cómo utilizar uno de los estimadores de redes neuronales profundas muy potentes, el DNNRegressor.

    Creación de un DNNRegressor para predecir el clima

    Permítanme comenzar importando varias bibliotecas diferentes que usaré para construir el modelo:

    import pandas as pd
    import numpy as np
    import tensorflow as tf
    from sklearn.metrics import explained_variance_score, 
        mean_absolute_error, 
        median_absolute_error
    from sklearn.model_selection import train_test_split
    

    Ahora pongamos nuestras manos en los datos y volvamos a analizarlos para familiarizarnos con ellos. He colocado todo el código y los datos en mi repositorio de GitHub aquí para que los lectores puedan seguirlo.

    # read in the csv data into a pandas data frame and set the date as the index
    df = pd.read_csv('end-part2_df.csv').set_index('date')
    
    # execute the describe() function and transpose the output so that it doesn't overflow the width of the screen
    df.describe().T
    

    .dataframe tbody tr th: only-of-type {
    vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }
    
    .dataframe thead th {
        text-align: right;
    }

    COUNT 25% 50% 75% Media Desv min max
    meantempm

    MAXTEMPM

    mintempm

    meantempm_1

    meantempm_2

    meantempm_3

    meandewptm_1

    meandewptm_2

    meandewptm_3

    meanpressurem_1

    meanpressurem_2

    meanpressurem_3

    maxhumidity_1

    maxhumidity_2

    maxhumidity_3

    minhumidity_1

    minhumidity_2

    minhumidity_3

    maxtempm_1

    maxtempm_2

    maxtempm_3

    mintempm_1

    mintempm_2

    mintempm_3

    maxdewptm_1

    maxdewptm_2

    maxdewptm_3

    mindewptm_1

    mindewptm_2

    mindewptm_3

    maxpressurem_1

    maxpressurem_2

    maxpressurem_3

    minpressurem_1

    minpressurem_2

    minpressurem_3

    precipm_1

    precipm_2

    precipm_3

    997.013.12938810.971591-17.05.015.022.0032.00
    997.019.50952911.577275-12.011.022.029.0038.00
    997.06.43831510.957267-27.0-2.07.016.0026.00
    997.013.10932810.984613-17.05.015.022.0032.00
    997.013.08826511.001106-17.05.014.022.0032.00
    997.013.06619911.017312-17.05.014.022.0032.00
    997.06.44032110.596265-22.0-2.07.016.0024.00
    997.06.42026110.606550-22.0-2.07.016.0024.00
    997.06.39318010.619083-22.0-2.07.016.0024.00
    997.01016.1394187.582453989.01011.01016.01021.001040.00
    997.01016.1424277.584185989.01011.01016.01021.001040.00
    997.01016.1514547.586988989.01011.01016.01021.001040.00
    997.088.1073229.28062747.083.090.093.00100.00
    997.088.1063199.28015247.083.090.093.00100.00
    997.088.0932809.27677547.083.090.093.00100.00
    997.046.02507516.1085179.035.045.056.0092.00
    997.046.02106316.1055309.035.045.056.0092.00
    997.045.98495516.0470819.035.045.056.0092.00
    997.019.48946811.588542-12.011.022.029.0038.00
    997.019.47141411.603318-12.011.022.029.0038.00
    997.019.45536611.616412-12.011.022.029.0038.00
    997.06.41725210.974433-27.0-2.07.016.0026.00
    997.06.39418310.988954-27.0-2.07.016.0026.00
    997.06.36710111.003451-27.0-2.07.016.0026.00
    997.09.37813410.160778-18.01.011.018.0026.00
    997.09.35907710.171790-18.01.011.018.0026.00
    997.09.33600810.180521-18.01.011.018.0026.00
    997.03.25175511.225411-28.0-6.04.013.0022.00
    997.03.22968911.235718-28.0-6.04.013.0022.00
    997.03.19859611.251536-28.0-6.04.013.0022.00
    997.01019.9137417.755590993.01015.01019.01024.001055.00
    997.01019.9177537.757705993.01015.01019.01024.001055.00
    997.01019.9277837.757805993.01015.01019.01024.001055.00
    997.01012.3179547.885743956.01008.01012.01017.001035.00
    997.01012.3199607.886681956.01008.01012.01017.001035.00
    997.01012.3269817.889511956.01008.01012.01017.001035.00
    997.02.5931808.4280580.00.00.00.2595.76
    997.02.5931808.4280580.00.00.00.2595.76
    997.02.5730498.4102230.00.00.00.2595.76

     

    # execute the info() function
    df.info()
    
    <class 'pandas.core.frame.DataFrame'>
    Index: 997 entries, 2015-01-04 to 2017-09-27
    Data columns (total 39 columns):
    meantempm          997 non-null int64
    maxtempm           997 non-null int64
    mintempm           997 non-null int64
    meantempm_1        997 non-null float64
    meantempm_2        997 non-null float64
    meantempm_3        997 non-null float64
    meandewptm_1       997 non-null float64
    meandewptm_2       997 non-null float64
    meandewptm_3       997 non-null float64
    meanpressurem_1    997 non-null float64
    meanpressurem_2    997 non-null float64
    meanpressurem_3    997 non-null float64
    maxhumidity_1      997 non-null float64
    maxhumidity_2      997 non-null float64
    maxhumidity_3      997 non-null float64
    minhumidity_1      997 non-null float64
    minhumidity_2      997 non-null float64
    minhumidity_3      997 non-null float64
    maxtempm_1         997 non-null float64
    maxtempm_2         997 non-null float64
    maxtempm_3         997 non-null float64
    mintempm_1         997 non-null float64
    mintempm_2         997 non-null float64
    mintempm_3         997 non-null float64
    maxdewptm_1        997 non-null float64
    maxdewptm_2        997 non-null float64
    maxdewptm_3        997 non-null float64
    mindewptm_1        997 non-null float64
    mindewptm_2        997 non-null float64
    mindewptm_3        997 non-null float64
    maxpressurem_1     997 non-null float64
    maxpressurem_2     997 non-null float64
    maxpressurem_3     997 non-null float64
    minpressurem_1     997 non-null float64
    minpressurem_2     997 non-null float64
    minpressurem_3     997 non-null float64
    precipm_1          997 non-null float64
    precipm_2          997 non-null float64
    precipm_3          997 non-null float64
    dtypes: float64(36), int64(3)
    memory usage: 311.6+ KB
    

    Tenga en cuenta que tenemos poco menos de 1000 registros de datos meteorológicos y que todas las características son de naturaleza numérica. Además, debido a nuestro arduo trabajo en el primer artículo, todos los registros están completos en el sentido de que no les falta ningún valor (sin valores no nulos).

    Ahora eliminaré las columnas “mintempm” y “maxtempm”, ya que no tienen ningún significado para ayudarnos a predecir las temperaturas medias medias. Estamos tratando de predecir el futuro, por lo que obviamente no podemos tener datos sobre el futuro. También separaré las características ( X) de los destinos ( y).

    # First drop the maxtempm and mintempm from the dataframe
    df = df.drop(['mintempm', 'maxtempm'], axis=1)
    
    # X will be a pandas dataframe of all columns except meantempm
    X = df[[col for col in df.columns if col != 'meantempm']]
    
    # y will be a pandas series of the meantempm
    y = df['meantempm']
    

    Al igual que con todas las aplicaciones de Machine Learning supervisadas, dividiré mi conjunto de datos en conjuntos de entrenamiento y prueba. Sin embargo, para explicar mejor el proceso iterativo de entrenamiento de esta red neuronal, utilizaré un conjunto de datos adicional al que me referiré como un “conjunto de validación”. Para el conjunto de entrenamiento, utilizaré el 80 por ciento de los datos y para el conjunto de pruebas y validación, cada uno será el 10% de los datos restantes.

    Para dividir estos datos, volveré a usar Sci-Kit Learn train_test_split(...).

    # split data into training set and a temporary set using sklearn.model_selection.traing_test_split
    X_train, X_tmp, y_train, y_tmp = train_test_split(X, y, test_size=0.2, random_state=23)
    
    # take the remaining 20% of data in X_tmp, y_tmp and split them evenly
    X_test, X_val, y_test, y_val = train_test_split(X_tmp, y_tmp, test_size=0.5, random_state=23)
    
    X_train.shape, X_test.shape, X_val.shape
    print("Training instances   {}, Training features   {}".format(X_train.shape[0], X_train.shape[1]))
    print("Validation instances {}, Validation features {}".format(X_val.shape[0], X_val.shape[1]))
    print("Testing instances    {}, Testing features    {}".format(X_test.shape[0], X_test.shape[1]))
    
    Training instances   797, Training features   36
    Validation instances 100, Validation features 36
    Testing instances    100, Testing features    36
    

    El primer paso que se debe tomar al construir un modelo de red neuronal es crear una instancia de la tf.estimator.DNNRegressor(...)clase. El constructor de la clase tiene varios parámetros, pero me centraré en lo siguiente:

    • feature_columns: Una estructura similar a una lista que contiene una definición del nombre y los tipos de datos de las características que se introducen en el modelo
    • hidden_units: Una estructura similar a una lista que contiene una definición del ancho y la profundidad del número de la red neuronal
    • optimizer: Una instancia de tf.Optimizersubclase, que optimiza los pesos del modelo durante el entrenamiento; su valor predeterminado es el optimizador AdaGrad.
    • activation_fn: Una función de activación utilizada para introducir la no linealidad en la red en cada capa; el valor predeterminado es ReLU
    • model_dir: Un directorio que se creará que contendrá metadatos y otros puntos de control guardados para el modelo

    Comenzaré definiendo una lista de columnas de características numéricas. Para hacer esto, utilizo la tf.feature_column.numeric_column()función que devuelve una FeatureColumninstancia de características numéricas de valor continuo.

    feature_cols = [tf.feature_column.numeric_column(col) for col in X.columns]
    

    Con las columnas de características definidas, ahora puedo crear una instancia de la DNNRegressorclase y almacenarla en la variable regresor. Especifico que quiero una red neuronal que tenga dos capas de profundidad donde ambas capas tengan un ancho de 50 nodes. También indico que quiero que los datos de mi modelo se almacenen en un directorio llamado tf_wx_model.

    regressor = tf.estimator.DNNRegressor(feature_columns=feature_cols,
                                          hidden_units=[50, 50],
                                          model_dir="tf_wx_model")
    
    INFO:tensorflow:Using default config.
    INFO:tensorflow:Using config: {'_tf_random_seed': 1, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_model_dir': 'tf_wx_model', '_log_step_count_steps': 100, '_keep_checkpoint_every_n_hours': 10000, '_save_summary_steps': 100, '_keep_checkpoint_max': 5, '_session_config': None}
    

    Lo siguiente que quiero hacer es definir una función reutilizable que se denomina genéricamente una “función de entrada”, a la que llamaré wx_input_fn(...). Esta función se utilizará para alimentar datos a mi red neuronal durante las fases de entrenamiento y prueba. Hay muchas formas diferentes de construir funciones de entrada, pero describiré cómo definir y usar una basada en el tf.estimator.inputs.pandas_input_fn(...)dado que mis datos están en estructuras de datos de pandas.

    def wx_input_fn(X, y=None, num_epochs=None, shuffle=True, batch_size=400):
        return tf.estimator.inputs.pandas_input_fn(x=X,
                                                   y=y,
                                                   num_epochs=num_epochs,
                                                   shuffle=shuffle,
                                                   batch_size=batch_size)
    

    Tenga en cuenta que esta wx_input_fn(...)función incluye un parámetro obligatorio y cuatro opcionales que luego se transfieren a una función de entrada de TensorFlow específicamente para los datos de pandas, que se devuelven. Esta es una característica muy poderosa de la API de TensorFlow (y Python y otros lenguajes que tratan las funciones como ciudadanos de primera clase).

    Los parámetros de la función se definen de la siguiente manera:

    • X: La entrada de cuenta para ser alimentado en uno de los tres DNNRegressormétodos de interfaz ( train, evaluatey predict)
    • y: Los valores objetivo de X, que son opcionales y no se proporcionarán a la predictllamada
    • num_epochs: Un parámetro opcional. Una época ocurre cuando el algoritmo se ejecuta en todo el conjunto de datos una vez.
    • shuffle: Un parámetro opcional, especifica si se debe seleccionar aleatoriamente un lote (subconjunto) del conjunto de datos cada vez que se ejecuta el algoritmo.
    • batch_size: El número de muestras que se incluirán cada vez que se ejecute el algoritmo

    Con nuestra función de entrada definida, ahora podemos entrenar nuestra red neuronal en nuestro conjunto de datos de entrenamiento. Para los lectores que están familiarizados con la API de alto nivel de TensorFlow, probablemente notarán que estoy siendo un poco poco convencional acerca de cómo entreno mi modelo. Es decir, al menos desde la perspectiva de los tutoriales actuales en el sitio web de TensorFlow y otros tutoriales en la web.

    Normalmente, verá algo como lo siguiente cuando entrene uno de estos modelos predefinidos de API de alto nivel.

    regressor.train(input_fn=input_fn(training_data, num_epochs=None, shuffle=True), steps=some_large_number)
    
    .....
    lots of log info
    ....
    

    Luego, el autor se lanzará directamente a demostrar la evaluate(...)función y apenas dará pistas para describir lo que hace o por qué existe esta línea de código.

    regressor.evaluate(input_fn=input_fn(eval_data, num_epochs=1, shuffle=False), steps=1)
    
    .....
    less log info
    ....
    

    Y después de esto, saltarán directamente a ejecutar la predict(...)función asumiendo que todo es perfecto con el modelo entrenado.

    predictions = regressor.predict(input_fn=input_fn(pred_data, num_epochs=1, shuffle=False), steps=1)
    

    Para el principiante de ML que lee este tipo de tutorial, me estremezco. Hay mucho más pensamiento en esas tres líneas de código que merece más atención. Creo que esta es la única desventaja de tener una API de alto nivel: es muy fácil armar un modelo sin comprender los puntos clave. Espero proporcionar una explicación razonable de cómo entrenar y evaluar esta red neuronal de una manera que minimice el riesgo de un ajuste insuficiente o excesivo drástico de este modelo a los datos de entrenamiento.

    Entonces, sin más demora, permítame definir un ciclo de entrenamiento simple para entrenar el modelo en los datos de entrenamiento y evaluarlo periódicamente en los datos de evaluación.

    evaluations = []
    STEPS = 400
    for i in range(100):
        regressor.train(input_fn=wx_input_fn(X_train, y=y_train), steps=STEPS)
        evaluations.append(regressor.evaluate(input_fn=wx_input_fn(X_val,
                                                                   y_val,
                                                                   num_epochs=1,
                                                                   shuffle=False)))
    
    INFO:tensorflow:Create CheckpointSaverHook.
    INFO:tensorflow:Saving checkpoints for 1 into tf_wx_model/model.ckpt.
    INFO:tensorflow:step = 1, loss = 1.11335e+07
    INFO:tensorflow:global_step/sec: 75.7886
    INFO:tensorflow:step = 101, loss = 36981.3 (1.321 sec)
    INFO:tensorflow:global_step/sec: 85.0322
    ... A WHOLE LOT OF LOG OUTPUT ...
    INFO:tensorflow:step = 39901, loss = 5205.02 (1.233 sec)
    INFO:tensorflow:Saving checkpoints for 40000 into tf_wx_model/model.ckpt.
    INFO:tensorflow:Loss for final step: 4557.79.
    INFO:tensorflow:Starting evaluation at 2017-12-05-13:48:43
    INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000
    INFO:tensorflow:Evaluation [1/1]
    INFO:tensorflow:Finished evaluation at 2017-12-05-13:48:43
    INFO:tensorflow:Saving dict for global step 40000: average_loss = 10.2416, global_step = 40000, loss = 1024.16
    INFO:tensorflow:Starting evaluation at 2017-12-05-13:48:43
    INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000
    INFO:tensorflow:Finished evaluation at 2017-12-05-13:48:43
    INFO:tensorflow:Saving dict for global step 40000: average_loss = 10.2416, global_step = 40000, loss = 1024.16
    

    El ciclo anterior se repite 100 veces. En el cuerpo del bucle, llamo al train(...)método del objeto regresor, pasándole mi reutilizable wx_input_fn(...)que a su vez pasa mi conjunto de características de entrenamiento y objetivos. A propósito, dejé los parámetros predeterminados num_epochsiguales a None, que básicamente dice “No me importa cuántas veces pases por encima del conjunto de entrenamiento, solo sigue entrenando el algoritmo con cada valor predeterminado batch_sizede 400″ (aproximadamente la mitad del tamaño del conjunto de entrenamiento). También dejé el shuffleparámetro igual a su valor predeterminado de Truepara que, durante el entrenamiento, los datos se seleccionen al azar para evitar cualquier relación secuencial en los datos. El parámetro final del train(...)método es el stepsque configuré en 400, lo que significa que el conjunto de entrenamiento se procesará 400 veces por ciclo.

    Esto me da un buen momento para explicar de una manera numérica más concreta cuál es el significado de una época. Recuerde de los puntos anteriores que ocurre una época cuando todos los registros de un conjunto de entrenamiento pasan a través de la red neuronal para entrenar exactamente una vez. Entonces, si tenemos alrededor de 800 (797 para ser exactos) registros en nuestro conjunto de entrenamiento y cada lote selecciona 400, entonces por cada dos lotes hemos logrado una época. Por lo tanto, si iteramos sobre el conjunto de entrenamiento para 100 iteraciones de 400 pasos cada una con un tamaño de lote de 400 (media época por lote) obtenemos:

    (100 x 400 / 2) = 20,000 epochs
    

    Ahora es posible que se pregunte por qué ejecuté un evaluate(...)método para cada iteración del bucle y capturé su salida en una lista. Primero déjame explicarte qué sucede cada vez que se activa el train(...)método. Selecciona un lote aleatorio de registros de entrenamiento y los empuja a través de la red hasta que se hace una predicción y para cada registro se calcula la función de pérdida. Luego, basándose en la pérdida calculada, los pesos se ajustan de acuerdo con la lógica del optimizador, que hace un buen trabajo al hacer ajustes en la dirección que reduce la pérdida general para la siguiente iteración. Estos valores de pérdida, en general, siempre que la tasa de aprendizaje sea lo suficientemente pequeña, disminuyen con el tiempo con cada iteración o paso.

    Sin embargo, después de una cierta cantidad de estas iteraciones de aprendizaje, las ponderaciones comienzan a verse influenciadas no solo por las tendencias generales en los datos, sino también por el ruido no informativo heredado en prácticamente todos los datos reales. En este punto, la red está demasiado influenciada por la idiosincrasia de los datos de entrenamiento y se vuelve incapaz de generalizar las predicciones sobre la población general de datos (es decir, datos que aún no ha visto).

    Esto se relaciona con el problema que mencioné anteriormente, donde muchos otros tutoriales sobre la API de TensorFlow de alto nivel se han quedado cortos. Es muy importante interrumpir periódicamente durante el entrenamiento y evaluar cómo el modelo se está generalizando a un conjunto de datos de evaluación o validación. Tomemos un momento para ver qué evaluate(...)devuelve la función al observar la salida de evaluación de la primera iteración del ciclo.

    evaluations[0]
    
    {'average_loss': 31.116383, 'global_step': 400, 'loss': 3111.6382}
    

    Como puede ver, genera la pérdida promedio (Error cuadrático medio) y la pérdida total (Suma de errores cuadrados) para el paso del entrenamiento, que para este es el paso 400. Lo que normalmente verá en una red altamente capacitada es una tendencia en la que tanto las pérdidas de capacitación como las de evaluación disminuyen más o menos constantemente en paralelo. Sin embargo, en un modelo sobreajustado en algún momento, en realidad en el punto donde comienza a ocurrir el sobreajuste, el conjunto de entrenamiento de validación dejará de ver reducciones en la salida de su evaluate(...)método. Aquí es donde desea detener el entrenamiento adicional del modelo, preferiblemente justo antes de que ocurra ese cambio.

    Ahora que tenemos una colección de evaluaciones para cada una de las iteraciones, permítanos trazarlas como una función de los pasos de entrenamiento para asegurarnos de que no hemos sobreentrenado nuestro modelo. Para hacerlo, usaré un diagrama de dispersión simple del pyplotmódulo de matplotlib .

    import matplotlib.pyplot as plt
    %matplotlib inline
    
    # manually set the parameters of the figure to and appropriate size
    plt.rcParams['figure.figsize'] = [14, 10]
    
    loss_values = [ev['loss'] for ev in evaluations]
    training_steps = [ev['global_step'] for ev in evaluations]
    
    plt.scatter(x=training_steps, y=loss_values)
    plt.xlabel('Training steps (Epochs = steps / 2)')
    plt.ylabel('Loss (SSE)')
    plt.show()
    

    ¡Frio! En el gráfico anterior, parece que después de todas esas iteraciones no he sobreajustado el modelo porque las pérdidas de evaluación nunca muestran un cambio significativo en la dirección hacia un valor creciente. Ahora puedo pasar de manera segura a hacer predicciones basadas en mi conjunto de datos de prueba restante y evaluar cómo funciona el modelo para predecir las temperaturas climáticas medias.

    Al igual que los otros dos métodos regresores que he demostrado, el predict(...)método requiere un método input_fnque pasaré usando el reutilizable wx_input_fn(...), entregándole el conjunto de datos de prueba, especificando num_epochsque sea uno y shufflefalso para que alimente secuencialmente todos los datos para probar en contra.

    A continuación, hago un poco de formateo del iterable de dictados que se devuelven desde el predict(...)método para tener una gran variedad de predicciones. Luego utilizo el conjunto de predicciones con los métodos sklearn explained_variance_score(...), mean_absolute_error(...)y median_absolute_error(...)para medir cuán bien las predicciones les fue en relación con los objetivos conocidos y_test. Esto le dice al desarrollador cuáles son las capacidades predictivas del modelo.

    pred = regressor.predict(input_fn=wx_input_fn(X_test,
                                                  num_epochs=1,
                                                  shuffle=False))
    predictions = np.array([p['predictions'][0] for p in pred])
    
    print("The Explained Variance: %.2f" % explained_variance_score(
                                                y_test, predictions))  
    print("The Mean Absolute Error: %.2f degrees Celcius" % mean_absolute_error(
                                                y_test, predictions))  
    print("The Median Absolute Error: %.2f degrees Celcius" % median_absolute_error(
                                                y_test, predictions))
    
    INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000
    The Explained Variance: 0.88
    The Mean Absolute Error: 3.11 degrees Celcius
    The Median Absolute Error: 2.51 degrees Celcius
    

    He utilizado las mismas métricas que el artículo anterior que cubre la técnica de regresión lineal para que no solo podamos evaluar este modelo, sino que también podamos compararlos. Como puede ver, los dos modelos funcionaron de manera bastante similar, siendo ligeramente mejor el modelo de regresión lineal más simple. Sin embargo, un practicante astuto sin duda ejecutaría varios experimentos variando los hiperparámetros (tasa de aprendizaje, ancho y profundidad) de esta red neuronal para ajustarla un poco, pero en general esto probablemente esté bastante cerca del modelo óptimo.

    Esto trae un punto que vale la pena mencionar, rara vez es el caso, y definitivamente no es recomendable, simplemente confiar en un modelo o el tema candente más reciente en la comunidad de Machine Learning. No hay dos conjuntos de datos idénticos y ningún modelo es el rey. La única forma de determinar cuál es el mejor modelo es probarlos. Luego, una vez que haya identificado el mejor modelo, hay otras compensaciones a tener en cuenta, como la interpretabilidad.

    Recursos

    ¿Quiere aprender las herramientas, las técnicas de Machine Learning y el análisis de datos que se utilizan en este tutorial? Aquí hay algunos recursos excelentes para comenzar:

    Conclusión

    En este artículo se demostró cómo usar la API de alto nivel de TensorFlow para la subclase de Estimator predefinida DNNRegressor. A lo largo del camino, he descrito, en un sentido general, la teoría de las redes neuronales, cómo se entrenan y la importancia de ser consciente de los peligros de sobreajustar un modelo en el proceso.

    Para demostrar este proceso de construcción de redes neuronales, he construido un modelo que es capaz de predecir la temperatura media para el día siguiente en función de las características numéricas recopiladas en el primer artículo de esta serie. Dicho esto, me gustaría tomarme un momento para aclarar mis intenciones para esta serie. Mi objetivo principal no ha sido construir modelos de pronóstico de última generación ni en el artículo de Regresión lineal ni en el actual sobre redes neuronales, pero mis objetivos han sido lograr lo siguiente:

    • Demonstrate the general process for undertaking an analytics (machine learning, data science, whatever…) project from data collection, data processing, exploratory data analysis, model selection, model building, and model evaluation.
    • Demonstrate how to select meaningful features that do not violate key assumptions of the Linear Regression technique using two popular Python libraries, StatsModels and Scikit Learn.
    • Demonstrate how to use the high level TensorFlow API and give some intuition into what is happening under all those layers of abstraction.
    • Discuss the issues associated with over fitting a model.
    • Explain the importance of experimenting with more than one model type to best solve a problem.

    Gracias por leer. Espero que hayan disfrutado de esta serie tanto como yo y, como siempre, agradezco los comentarios y críticas.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *