<jupyter_start><jupyter_text>Construyendo una red neuronal con Keras  ¿Qué librerías necesitamos?<jupyter_code>import numpy
import keras
from keras.datasets import mnist
from keras.layers import Activation, Dense, Dropout
from keras.models import Sequential
from keras import optimizers, regularizers<jupyter_output><empty_output><jupyter_text>Otras librerías<jupyter_code>%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot<jupyter_output><empty_output><jupyter_text>Cargando los datos del MNIST- El conjunto de datos a utilizar es el **[MNIST](http://yann.lecun.com/exdb/mnist/)**.- Es un conjunto estándar para hacer *reconocimiento de imágenes*.- Buscamos entrenar un clasificador que reconozca que dígito es mostrado en la imagen.- El MNIST está compuesto por imágenes de 28x28 píxeles representadas como matrices.- La salida son 10 clases (dígitos del 0 al 9).- Se preprocesará para *convertir las matrices en vectores* y transformar las etiquetas en representaciones *one-of-k*.<jupyter_code>batch_size = 128  # For mini-batch gradient descent
num_classes = 10
epochs = 5
input_size = 28*28
train_examples = 60000
test_examples = 10000

# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# reshape the dataset to convert the examples from 2D matrixes to 1D arrays.
x_train = x_train.reshape(train_examples, input_size)
x_test = x_test.reshape(test_examples, input_size)

# normalize the input
x_train = x_train / 255
x_test = x_test / 255

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)<jupyter_output><empty_output><jupyter_text>Contruyendo la red neuronal- Comenzaremos por construir un *perceptrón multicapa* que es la red neuronal más común.- El modelo más simple en Keras es una concatenación de capas (layers).    - Se llama modelo secuencial.- La capa más básica es la *densa* (dense o fully connected).    - Internamente tiene dos variables: una matriz de pesos y un vector de biases. Keras nos abstrae de todo eso.<jupyter_code>model = Sequential()

# Input to hidden layer
model.add(Dense(512, input_shape=(input_size,)))
model.add(Activation('relu'))

# Hidden to output layer
model.add(Dense(10))
model.add(Activation('softmax'))<jupyter_output><empty_output><jupyter_text>Para imprimir una descripción del modelo existe un comando:<jupyter_code>print(model.summary())<jupyter_output>_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 512)               401920    
_________________________________________________________________
activation_1 (Activation)    (None, 512)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 10)                5130      
_________________________________________________________________
activation_2 (Activation)    (None, 10)                0         
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_________________________________________________________________
None<jupyter_text>Hiperparámetros  Funciones de Activación- Una red neuronal con activación lineal no tiene mucho más poder de representación que un algoritmo lineal.- Para expresar no linearidad en la red neuronal se necesitan funciones no lineales de activación.- Una función de activación común es la *sigmoide* (o logística).- Keras soporta varias funciones de activación: rectified linea unit (ReLU), tangenge hiperbólica, sigmoide "dura", etc.    - Hoy en día, por sus propiedades, ReLU suele ser la más utilizada [1].- La función de activación *softmax* es utilizada al final de la red y sirve para clasificación.![Funciones de activación](images/activation_functions.png "Funciones de activación")Fuente: https://ujjwalkarn.me/2016/08/09/quick-intro-neural-networks/<jupyter_code>model = Sequential([
    Dense(64, input_shape=(784,), activation='relu'),
    Dense(32, activation='relu'),
    Dense(10, activation='softmax')
])

print(model.summary())<jupyter_output>_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_3 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_4 (Dense)              (None, 32)                2080      
_________________________________________________________________
dense_5 (Dense)              (None, 10)                330       
=================================================================
Total params: 52,650
Trainable params: 52,650
Non-trainable params: 0
_________________________________________________________________
None<jupyter_text>Regularización de la red Regularización de los pesos- La red puede regularizarse penalizando los pesos.- Los pesos se regularizan mediante alguna norma:    - L1 es la suma del valor absoluto: ${\displaystyle \lambda \sum_{i=1}^{k} |w_i|}$    - L2 es la suma del valor cuadrado, es la más común: ${\displaystyle \lambda \sum_{i=1}^{k} w_i^2}$    - Elastic net es una combinación de ambas: ${\displaystyle \lambda_1 \sum_{i=1}^{k} |w_i| + \lambda_2 \sum_{i=1}^{k} w_i^2}$- Para un análisis detallado de la diferencia entre L1 y L2 revisar [\[2\]](http://www.chioka.in/differences-between-l1-and-l2-as-loss-function-and-regularization/)<jupyter_code>model = Sequential([
    Dense(64, input_shape=(784,), activation='relu', kernel_regularizer=regularizers.l2(0.01)),
    Dense(32, activation='relu', kernel_regularizer=regularizers.l2(0.01)),
    Dense(10, activation='softmax', kernel_regularizer=regularizers.l2(0.01))
])<jupyter_output><empty_output><jupyter_text>Dropout- Otra forma muy usada a la hora de regularizar es el **dropout** [3].- Es extremadamente efectivo y simple.- Es complementario a L1/L2/ElasticNet.- Durante el entrenamiento se implementa apagando un neurón con alguna probabilidad **_p_** (un hiperparámetro).![Dropout](images/dropout.jpeg "Dropout")Fuente: Trabajo de Srivastava et al. [3]  Dropout en Keras- Se aplica agregando capas al modelo.- Se llaman capas `Dropout` y se agrega a cada capa que se quiere regularizar.<jupyter_code>model = Sequential([
    Dense(64, input_shape=(784,), activation='relu', kernel_regularizer=regularizers.l2(0.01)),
    Dropout(0.5),
    Dense(10, activation='softmax')
])<jupyter_output><empty_output><jupyter_text>Preparando el modelo para entrenarlo- Para minimizar una red neuronal necesitamos *calcular sus gradientes*.    - Esto se hace con el algoritmo de *retropropagación*.- Keras tiene la capacidad de hacerlo automáticamente.    - Esto se conoce como _diferenciación automática_ y es algo común en los frameworks de deep learning.- El modelo de Keras necesita *compilarse*.    - Recordar que lo que armamos en Keras (o TensorFlow) es un grafo de computación.- Al compilar el modelo los parámetros más importantes son la función de costo y el algoritmo de optimización.  Funciones de costo y algoritmos de optimización- La función de costo puede cambiar de acuerdo al tipo de problema (clasificación binaria/multiclase o regresión).    - La funciones más comunes son la media del error cuadrático (_mean squared error_) para regresión y la entropía cruzada (_crossentropy_) para clasificación.- El algoritmo de optimización es el que entrena la red. Existen varios, que en si son variaciones del algoritmo de _descenso por la gradiente_.                                Fuente: http://ruder.io/optimizing-gradient-descent/  Compilando el modelo Keras y visualizando la arquitectura- Con el método `.compile()` podemos compilar el modelo de Keras.- Además de la función de costo y el algoritmo de optimización se le pueden pasar métricas para llevar registro además del error de los datos (e.g. la exactitud o la precisión).- Una vez compilado el modelo la modificación requerirá rehacerlo desde cero (salvo que se guarden y carguen los pesos).<jupyter_code>model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.Adagrad(lr=0.001, decay=0.0001), 
                  # También podría ser el string "Adagrad" con los parámetros por defecto
              metrics=['accuracy'])  # La métrica sirve para llevar algún registro además del costo<jupyter_output><empty_output><jupyter_text>Visualizar la arquitectura (opcional)Opcionalmente, si instalamos las librerías extras pedidas en el setup y utilizando `vis_util`, podemos visualizar el grafo de la red.<jupyter_code>SVG(model_to_dot(model).create(prog='dot', format='svg'))<jupyter_output><empty_output><jupyter_text>Entrenamiento- Una vez compilado el modelo, está listo para ser entrenado.- Keras tiene una interfaz similar a Scikit-Learn, con lo método `fit` y `predict`.- Para entrenar se necesitan 3 parámetros:    - El conjunto de datos de entrenamiento (datos y etiquetas).    - El tamaño del batch para hacer _mini-batch gradient descent_.    - La cantidad de épocas que entrenar.    - Eventualmente le podemos pasar datos para hacer validación del modelo.    - El parámetro `verbose` nos imprime información útil respecto al desempeño del modelo.<jupyter_code>model.fit(x_train, y_train, 
          batch_size=batch_size, epochs=epochs, 
          validation_data=(x_test, y_test), verbose=1);<jupyter_output>Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 2s 31us/step - loss: 2.1453 - acc: 0.5814 - val_loss: 1.4992 - val_acc: 0.8260
Epoch 2/5
60000/60000 [==============================] - 1s 24us/step - loss: 1.5486 - acc: 0.7127 - val_loss: 1.2186 - val_acc: 0.8544
Epoch 3/5
60000/60000 [==============================] - 1s 23us/step - loss: 1.3586 - acc: 0.7515 - val_loss: 1.0857 - val_acc: 0.8685
Epoch 4/5
60000/60000 [==============================] - 1s 21us/step - loss: 1.2545 - acc: 0.7688 - val_loss: 1.0075 - val_acc: 0.8757
Epoch 5/5
60000/60000 [==============================] - 1s 23us/step - loss: 1.1917 - acc: 0.7787 - val_loss: 0.9549 - val_acc: 0.8806<jupyter_text>Keras Avanzado- Además del modelo secuencial, Keras tiene una API funcional.- Con esta API se pueden hacer redes más complejas. E.g. con múltiples entradas y salidas.<jupyter_code>from keras.layers import Input, Dense
from keras.models import Model

# This returns a tensor
inputs = Input(shape=(784,))

# a layer instance is callable on a tensor, and returns a tensor
x = Dense(64, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)

# This creates a model that includes
# the Input layer and three Dense layers
model = Model(inputs=inputs, outputs=predictions)

model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, 
          batch_size=batch_size, epochs=epochs, 
          validation_data=(x_test, y_test), verbose=1);<jupyter_output><empty_output><jupyter_text>Modelos de múltiple entrada y/o salida- El modelo secuencial sólo permite una entrada y una salida.- La API funcional permite cosas más complejas.- Si, por ejemplo, quisiéramos aprender embeddings distintos para dominios distintos de texto.<jupyter_code>import keras.backend as K

from keras.layers import Input, Dense, Embedding, Lambda, concatenate
from keras.models import Model

users = Input(shape=(1,), dtype='int32', name='users')
items = Input(shape=(1,), dtype='int32', name='items')

users_embeddings = Embedding(input_dim=500,
                             output_dim=30,
                             input_length=1)(users)
users_embeddings = Lambda(lambda xin: K.squeeze(xin, axis=1))(users_embeddings)

items_embeddings = Embedding(input_dim=5000,
                             output_dim=30,
                             input_length=1)(items)
items_embeddings = Lambda(lambda xin: K.squeeze(xin, axis=1))(items_embeddings)

users_items = concatenate([users_embeddings, items_embeddings])

hidden_layer = Dense(30, activation="relu")(users_items)
output_layer = Dense(1, activation="sigmoid", name="output_layer")(hidden_layer)
model = Model(inputs=[users, items], outputs=output_layer)

model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

print(model.summary())
SVG(model_to_dot(model).create(prog='dot', format='svg'))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11837180286961946, 'fraction_numerical': 0.03220524017467249, 'mean_word_length': 4.597992143168922, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 50, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '188201', 'license': 'MIT', 'n_tokens_mistral': 4251, 'n_tokens_neox': 3929, 'n_words': 1206, 'path': 'archive/2018/1_introduction_nn.ipynb', 'repo_name': 'nachitog83/AprendizajeProfundo', 'size': '12824'}