<jupyter_start><jupyter_text>Transfer Learning with CNN - Segunda Parte - <NAME>  Este trabajo corresponde a la segunda parte del proyecto final de la asignatura «Computación bioinspirada» y en él se expecifica el segundo método explicado en el resumen entregado mediante el Campus Virtual. En esta entrega se hará ver la forma más usada de utilizar la técnica de Transfer Learning, aportando una ruta sobre como introducir un pre-modelo, darle el formato de entrada deseado, así como crear nuestro propio modelo que irá acoplado al anterior.El trabajo se ha enfocado en el pre-modelo de la primera parte: ResNet50, lo mismo para el dataset escogido, que se trata del famoso: CIFAR-10. La explicación de ambos elementos viene en la primera parte de la entrega.  Índice >[Transfer Learning with CNN - Segunda Parte - <NAME>](scrollTo=fI_VRq2nbJBv)>>[Índice](scrollTo=AFWP92gbhOgM)>>>[Análisis de tareas](scrollTo=SLdcFA8xhQ1D)>>>>[Descarga y preparación del dataset](scrollTo=dggtw2G23GnV)>>>>[Creación del pre-modelo.](scrollTo=HzjC7SaPeVHi)>>>>[Creación del nuevo modelo.](scrollTo=ReBf5Bm9QLyo)>>>>[Implementación de la técnica data augmentation](scrollTo=1ZEqVPL9kny3)>>>>[Entrenamiento del modelo.](scrollTo=GXokjJ_zyYOx)>>>>[Visualización de los resultados obtenidos.](scrollTo=GBrdnCKDtWkl)>>>>[Guardado del modelo](scrollTo=N2yAVZ08199W)>>>[Conclusiones](scrollTo=qHMAOqli2EK4)  Análisis de tareas Para tener una mayor organización en torno a la información del proyecto, definiremos diferentes puntos para encontrar más facilmente en qué posición nos encontramos.1. Descarga y preparación del dataset.2. Creación del pre-modelo.3. Creación del nuevo modelo.4. Implementación de la técnica data augmentation.5. Entrenamiento del modelo.6. Visualización de los resultados obtenidos.7. Guardado del modelo.  Descarga y preparación del dataset Keras nos ofrece la posibilidad de importar el dataset CIFAR10 de una manera más sencilla. Aunque también existe la posibilidad de descargar los datos mediante la invocación *wget* a la URL de [skymind.ai/wiki/open-datasets](https://skymind.ai/wiki/open-datasets); aunque de este modo (wget) será algo más complejo.<jupyter_code>from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()<jupyter_output>Using TensorFlow backend.<jupyter_text>Una vez importados, podemos ver el tamaño de las imágenes descargadas; con esto sabremos qué dimensión poner de entrada y si es necesario redimensionarlas.<jupyter_code>import numpy as np
print("Existen {} imágenes de entrenamiento y {} imágenes de test.".format(x_train.shape[0], x_test.shape[0]))
print('Hay {} clases únicas para predecir.'.format(np.unique(y_train).shape[0]))<jupyter_output>Existen 50000 imágenes de entrenamiento y 10000 imágenes de test.
Hay 10 clases únicas para predecir.<jupyter_text>Es necesario etiquetar cada salida. Como tenemos 10 clases que clasificar, deberemos etiquetar cada salida con un array de 10 valores; los cuales pueden estar en 0 o 1, sólo puede haber una con el valor positivo (etiqueta correcta).CIFAR 10 tiene 10 clases para poder clasificarlas, como en el proyecto se nos pide al menos 4, se ha decidido implementar las 10 clases que tenemos disponible.<jupyter_code>from keras.utils import np_utils
y_train = np_utils.to_categorical(y_train, 10)                                   
y_test = np_utils.to_categorical(y_test, 10)<jupyter_output><empty_output><jupyter_text>Finalmente, podemos ver que se han descargado correctamente los datos con la función 'imshow' de la librería matplot.<jupyter_code>from matplotlib import pyplot as plt

fig = plt.figure(figsize=(10, 10))

for i in range(1, 9):
    img = x_train[i-1]
    fig.add_subplot(2, 4, i)
    plt.imshow(img)<jupyter_output>Dimensiones de las imágenes:  (32, 32, 3)<jupyter_text>A continuación, lo único que nos queda para terminar de preparar los datos será el tipado y normalización de estos. Para normalizar los datos se usara la técnica **z-score**; también conocido como puntuación estándar, valores z; el uso de "Z" se debe a la distribución normal es también conocida como la "distribución de Z", indica el número de estándar las desviaciones de una observación o dato está por encima o por debajo de la media. Es una cantidad adimensional obtiene restando la media de la población a partir de un puntaje bruto individual y luego dividiendo la diferencia por la desviación estándar de la población. Este proceso de conversión se llama la estandarización o normalización.<jupyter_code>import numpy as np

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')

mean = np.mean(x_train,axis=(0,1,2,3))
std = np.std(x_train,axis=(0,1,2,3))
x_train = (x_train-mean)/(std+1e-7)
x_test = (x_test-mean)/(std+1e-7)<jupyter_output><empty_output><jupyter_text>Creación del pre-modelo. Una vez que tenemos las imágenes preparadas para suministrarlas en el modelo, tenemos que crear la topología de este. En esta parte se ha elegido un pre-modelo distinto al utilizado en la primera parte. Las razones son la comparación entre un pre-modelo y otro; vemos como en este caso, la cantidad de parámetros es mucho menor que el modelo ResNet50.<jupyter_code>from keras.applications.vgg16 import VGG16

vgg16 = VGG16(weights='imagenet', include_top=False)

for index,layer in enumerate(vgg16.layers):
  if index > 3: break
  layer.trainable = False
    
vgg16.summary()<jupyter_output>_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, None, None, 3)     0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, None, None, 64)    1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, None, None, 64)    36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, None, None, 64)    0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, None, None, 128)   73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, None, None, 128)   147584    
_________________________________________________________________
block2_poo[...]<jupyter_text>En este método de creación del modelo es necesario proporcionar el formato de entrada al pre-modelo; por ello haremos uso de la libreria «Input». Dar formato significa proporcionar a la red las dimensiones que tienen los datos que se le van a pasar.<jupyter_code>from keras.layers import Input

input_layer = Input(shape=(32,32,3),name = 'image_input')<jupyter_output><empty_output><jupyter_text>Una vez que tenemos definida la entrada a nuestro modelo, procedemos a unirlos:<jupyter_code>output_vgg16 = vgg16(input_layer)<jupyter_output><empty_output><jupyter_text>A partir de aquí ya tenemos preparada la primera parte de la topología que vamos a crear. El próximo paso a tomar es la creación de nuestro propio modelo (expecializado en la clasificación de las clases de CIFAR-10).    Creación del nuevo modelo. En este nuevo paso, se llevará a cabo la unión entre el pre-modelo «VGG16» y nuestro nuevo modelo. Para ello haremos uso de la facilidad que nos da Keras mediante la librería «*layers*». Este modelo cuenta con una capa de regularización: **BatchNormalization**.  *¿Qué es batch normalization?* Su función es de normalizar la activación de la capa anterior en cada lote; es decir, aplica una transformación que mantiene la activación media cerca de 0 y la desviación estándar de activación cerca de 1. La técnica de 'Batch Normalitation' logra la misma precisión con menos etapas, lo que acelera el proceso de entrenamiento.![Batch normalization](https://www.learnopencv.com/wp-content/uploads/2018/07/ModelAccuracy.png)<jupyter_code>from keras.layers import Flatten, Dense, Dropout, Conv2D, Activation, BatchNormalization
from keras import regularizers

out_model = Conv2D(32, (3,3), padding='same', kernel_regularizer=regularizers.l2(1e-4), input_shape=x_train.shape[1:])(output_vgg16)
out_model = Activation('relu')(out_model)
out_model = BatchNormalization()(out_model)
out_model = Flatten(name='flatten')(out_model)
out_model = Dense(1024, activation='relu', name='fc1')(out_model)
out_model = Dropout(0.2)(out_model)
out_model = Dense(1024, activation='relu', name='fc2')(out_model)
out_model = Dense(10, activation='softmax', name='predictions')(out_model)<jupyter_output><empty_output><jupyter_text>Una vez hecha la unión entre ambos modelos, definiremos el modelo definitivo. Éste último modelo contará con la entrada definida anteriormente con la función «Input» y la salida model (definido anteriormente, también). En este caso, nos ayudaremos de la librería «*Model*» de Keras.<jupyter_code>from keras.models import Model

model = Model(input=input_layer, output=out_model)
model.summary()<jupyter_output>_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
image_input (InputLayer)     (None, 32, 32, 3)         0         
_________________________________________________________________
vgg16 (Model)                multiple                  14714688  
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 1, 1, 32)          147488    
_________________________________________________________________
activation_1 (Activation)    (None, 1, 1, 32)          0         
_________________________________________________________________
batch_normalization_1 (Batch (None, 1, 1, 32)          128       
_________________________________________________________________
flatten (Flatten)            (None, 32)                0         
_________________________________________________________________
fc1 (Dense[...]<jupyter_text>Implementación de la técnica data augmentation Puesto que es posible la aparación del overfitting, echaremos mano a la técnica de «data augmentation»; el objetivo de esta técnica es combatir contra el overfitting, generando más datos. Esta generación se basa en la utilización de los datos descargados (CIFAR-10) y mediante rotaciones, nublados, etc. crear nuevas imágenes, y así nutrir a la red de más datos.<jupyter_code>from keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(     
            featurewise_center=False,  # set input mean to 0 over the dataset
            samplewise_center=False,  # set each sample mean to 0
            featurewise_std_normalization=False,  # divide inputs by std of the dataset
            samplewise_std_normalization=False,  # divide each input by its std
            zca_whitening=False,  # apply ZCA whitening
            rotation_range=15,  # randomly rotate images in the range (degrees, 0 to 180)
            width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)
            height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)
            horizontal_flip=True,  # randomly flip images
            vertical_flip=False)  # randomly flip images)

datagen.fit(x_train)<jupyter_output><empty_output><jupyter_text>Podemos comprobar que los nuevos datos se han creado correctamente mediante la función «flow» de la librería de ImageDataGenerator.<jupyter_code>from matplotlib import pyplot as plt
from scipy.misc import toimage

for X_batch, y_batch in datagen.flow(x_train, y_train, batch_size=9):
    for i in range(0, 9):
        plt.subplot(330 + 1 + i)
        plt.imshow(toimage(X_batch[i].reshape(32, 32, 3)))
    plt.show()
    break<jupyter_output>/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:7: DeprecationWarning: `toimage` is deprecated!
`toimage` is deprecated in SciPy 1.0.0, and will be removed in 1.2.0.
Use Pillow's ``Image.fromarray`` directly instead.
  import sys<jupyter_text>Entrenamiento del modelo. Una vez que tenemos todo preparado podemos empezar con el entrenamiento de la red; no sin antes **compilar** el modelo creado. En la función de compilado coge gran importancia el optimizador escogido, puesto que nos llevará a distintos tipos de ajuste en la red.Para poder controlar algo, en primer lugar debemos poder observarlo. En este sentido, para controlar la salida de la red neuronal, deberíamos poder medir cuan lejos esta la salida que obtuvimos de la que se esperaba obtener. Este es el trabajo de la función de pérdida de la red. Esta función toma las predicciones que realiza el modelo y los valores objetivos (lo que realmente esperamos que la red produzca), y calcula cuán lejos estamos de ese valor, de esta manera, podemos capturar que tan bien esta funcionando el modelo para el ejemplo especificado.El truco fundamental del 'Deep Learning' es utilizar el valor que nos devuelve esta función de pérdida para retroalimentar la red y ajustar los pesos en la dirección que vayan reduciendo la pérdida del modelo para cada ejemplo. Este ajuste, es el trabajo del optimizador; el cuál implementa el **backpropagation**.<jupyter_code>model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])<jupyter_output><empty_output><jupyter_text>Una vez que tenemos compilado el modelo podremos ir a lo importante, el entrenamiento de éste y la comprobación de los resultados obtenidos por ella. En este caso se ha hecho uso de los *callbacks*, éstos nos sirven para ejecutar código mientras la red está entrenando. '**Learning Rate Scheduler**' nos ayuda a modificar la tasa de aprendizaje; dependiendo de ésta, el descenso del gradiente se hará a mayor o a menor velocidad.![descenso del gradiente](data:image/jpeg;base64,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)<jupyter_code>from keras.callbacks import LearningRateScheduler

def lr_schedule(epoch):
    lrate = 0.001
    if epoch > 35:
        lrate = 0.0005
    return lrate

batch_size = 256
history = model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),\
                    steps_per_epoch=x_train.shape[0] // batch_size,epochs=40,\
                    verbose=1,validation_data=(x_test,y_test), callbacks=[LearningRateScheduler(lr_schedule)])<jupyter_output>Epoch 1/40
195/195 [==============================] - 43s 218ms/step - loss: 2.2755 - acc: 0.1651 - val_loss: 2.2036 - val_acc: 0.2544
Epoch 2/40
195/195 [==============================] - 38s 196ms/step - loss: 2.1642 - acc: 0.2887 - val_loss: 2.0460 - val_acc: 0.3585
Epoch 3/40
195/195 [==============================] - 37s 190ms/step - loss: 2.0255 - acc: 0.3702 - val_loss: 1.8842 - val_acc: 0.4307
Epoch 4/40
195/195 [==============================] - 37s 190ms/step - loss: 1.8829 - acc: 0.4403 - val_loss: 1.7337 - val_acc: 0.4936
Epoch 5/40
195/195 [==============================] - 37s 191ms/step - loss: 1.7400 - acc: 0.5033 - val_loss: 1.5807 - val_acc: 0.5525
Epoch 6/40
195/195 [==============================] - 37s 190ms/step - loss: 1.5984 - acc: 0.5543 - val_loss: 1.4398 - val_acc: 0.5955
Epoch 7/40
195/195 [==============================] - 37s 190ms/step - loss: 1.4641 - acc: 0.5915 - val_loss: 1.3165 - val_acc: 0.6245
Epoch 8/40
195/195 [==============================] - 3[...]<jupyter_text>Visualización de los resultados obtenidos. Una vez que tenemos el modelo entrenado, podemos ver las gráficas para la validación de nuestro modelo y comprobación de la existencia de overfitting o underfitting.![Imagen acerca de undefitting y overfitting](https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Overfitting_svg.svg/300px-Overfitting_svg.svg.png)Un ejemplo de overfitting puede ser el anterior; si nuestro modelo genera esta gráfica muy posiblemente no generalice bien, y sufrirá de overfitting.<jupyter_code>from matplotlib import pyplot as plt

ent_loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1, len(ent_loss) + 1)

plt.plot(epochs, ent_loss, 'b', label='Training')
plt.plot(epochs, val_loss, 'r', label='Validation')
plt.title('Loss of Training and Validation')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()
score  = model.evaluate(x_test, y_test)

print('Accuracy on the Test Images: ', score[1]*100)<jupyter_output>10000/10000 [==============================] - 6s 596us/step
Accuracy on the Test Images:  82.19999999999999<jupyter_text>Guardado del modelo Una vez que hemos comprobado que nuestro modelo está bien implementado y operable. Podemos pasar al guardado de la topología y de sus pesos. Este guardado irá destinado al disco duro de nuestro ordenador personal, en el cual reside el programa gráfico que se ha implementado.<jupyter_code>model.save('model.h5')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.076018549188473, 'fraction_numerical': 0.06864855912553826, 'mean_word_length': 7.29145211122554, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 98, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '76347', 'license': 'Apache-2.0', 'n_tokens_mistral': 11386, 'n_tokens_neox': 10495, 'n_words': 1785, 'path': 'Transfer Learning with Convolutional Neural Networks - Dificultad media/.ipynb_checkpoints/Transfer Learning with Convolutional Neural Networks - CIFAR10 - Segunda parte-checkpoint.ipynb', 'repo_name': 'ropuertop/transfer-learning-convolutional-neural-network', 'size': '24184'}