<jupyter_start><jupyter_text>Transferência de aprendizado com TensorFlow Hub      Executar no Google Colab        Ver código no GitHub   Modelos de deep learning são o estado da arte para a solução da maioria dos problemas computacionais da atualidade. As aplicações são as mais diversas e dependem apenas da criatividade:* Descrição automática de imagens* Tradução de texto* Remoção de ruído de fundo em áudio de vídeoconferências* Separação de instrumentos e vocais em música* Conversação consistente com assistentes virtuais* Etc...Uma das características de modelos de deep learning é a profundidade das arquiteturas, isto é, modelos de deep learning geralmente possuem muitas camadas empilhadas. A Figura 1 mostra um esquema da arquitera InceptionV3 [1] utilizada para classificação de imagens. Nessa arquitetura, dependendo de quais tipos de camadas são inseridas na contagem, o modelo tem uma profundidade de 42 camadas.Figura 1: Esquema da InceptionV3Fonte: Adaptado de [2]O sucesso de modelos de deep learning se deve a diversos fatores, mas podem ser citados o surgimento de novas técnicas de treinamento de redes neurais, o aumento do poder computacional e a massiva quantidade de dados disponíveis para treinamento. Esta última é essencial pois os modelos de deep learning tem uma grande quantidade de parâmetros treináveis e podem facilmente decorar a base de treinamento.  Transferência de aprendizado No entanto, para a maioria dos problemas reais, as bases de dados não são grandes e não é prático ou viável adquirir novas instâncias. Para esses casos, costuma-se utilizar uma técnica chamada Transferência de aprendizado que consiste em utilizar um modelo pré-treinado em problema similar e adaptar para o problema alvo.Há várias formas de se aplicar transferência de aprendizado com deep learning. Uma das mais comuns é utilizar um modelo pré-treinado como extrator de características. Para fazer isso, remove-se a última camada do modelo e substitui-se por uma nova camada de acordo com o problema alvo. Os demais pesos do modelo original são congelados e somente a última camada é treinada. A Figura 2 exemplifica esse procedimento para um problema hipotético de classificação de animais onde o modelo original foi treinado para reconhecer animais da África e o problema alvo é reconhecer animais da Amazônia.Figura 2: Esquema exemplificando transferência de aprendizado utilizando modelo pré-treinado como extrator de característicasFonte: Elaborado pelo autor  Utilizando o TensorFlow Hub O TensorFlow Hub é uma biblioteca/repositório para facilitar o reuso de módulos de aprendizagem de máquina. São disponibilizados mais de 500 módulos pré-treinados para problemas nos domínios de texto, imagem, vídeo e áudio. Para navegar pelos módulos disponíveis basta acessar o portal [tfhub.dev](https://tfhub.dev/). É possível fazer buscas e filtrar os módulos de acordo com diversos critérios como arquitetura de rede, base de dados de treinamento e domínio do problema.Neste tutorial será feito exemplo de treinamento com transferência de aprendizado utilizando o Tensorflow Hub para demostrar a sua facilidade de uso. Será utilizado o modelo MobileNet [3] pré-treinado na base de imagens ImageNet [4] para classificar imagens da base Rock, Paper, Scissors [5]. Também serão abordados os seguintes assuntos:* Carregamento de bases utilizando o TensorFlow Datasets* Construção de pipilines de entrada utilizando o `tf.data`* Aumento de dados<jupyter_code>!pip install tensorflow tensorflow_datasets tensorflow_hub tensorflow_addons tfds-nightly matplotlib
import tensorflow as tf

import tensorflow_datasets as tfds
import tensorflow_hub as hub
import tensorflow_addons as tfa
import matplotlib.pyplot as plt

from tensorflow.keras.layers import Dense
from tensorflow.keras import Sequential<jupyter_output><empty_output><jupyter_text>Configurações para os experimentos<jupyter_code>BATCH_SIZE = 32
EPOCHS = 10
IMAGE_HEIGHT = 224
IMAGE_WIDTH = 224
IMAGE_PRE_CROP_WIDTH = 240
IMAGE_PRE_CROP_HEIGHT = 240
ROTATION_ANGLE = 45
AUTOTUNE = tf.data.experimental.AUTOTUNE<jupyter_output><empty_output><jupyter_text>Utilitário para exibir alguns exemplos do dataset O pacote tensorflow_datasets possui a função `tfds.show_examples` para exibir instâncias de exemplo para bases de imagens, mas a base precisa ser carregada no formato não supervisionado. No entanto, como carregamos no modo supervisionado, isto é, para o problema de classificação, as funções a seguir colocam no formato esperado pelo `tfds.show_examples`.<jupyter_code>def tuple2dict(image, label):
    sample_dict = {
        'image': image,
        'label': label
    }
    return sample_dict
def show_examples(dataset, info):
    dataset = dataset.map(tuple2dict, num_parallel_calls=AUTOTUNE)
    tfds.show_examples(dataset, info, rows=2, cols=4)
    plt.close()<jupyter_output><empty_output><jupyter_text>Carregando a base de imagens rock_paper_scissors O TensorFlow Datasets (tfds) é uma biblioteca que inclui mais de 170 base de dados prontos para uso no TensorFlow, sendo carregadas já no formato `tf.data.Datasets` utilizado no pipeline do `tf.data`. O catálogo completo pode ser encontrando em [https://www.tensorflow.org/datasets/catalog/](https://www.tensorflow.org/datasets/catalog/).Uma das formas de carregar uma base do o tfds é utilizando a função `tfds.load` com o nome da base escolhida. Como dito anterior, neste tutorial será utilizada a base `rock_paper_scissors`. Algumas das opções disponíveis ao carregar a base:* `split`: quais partições da base deseja carregar. As listas particições variam de base para base e estão disponíveis na página de cada uma.* `shuffle_files`: as bases são armazenadas em arquivos TFRecords, essa opção faz com que a leitura desses arquivos seja aleatória, melhorando a aleatoriedade da geração de batchs* `as_supervised`: essa opção carrega a base com imagem e rótulo separados, como esperado para problemas de aprendizado supervisionado* `with_info`: carrega uma variável que vai ter informações de metadados da base como quantidade de instâncias e rótulos.<jupyter_code>(jokempo_train, jokempo_test), info = tfds.load('rock_paper_scissors',
                                                split=['train', 'test'],
                                                shuffle_files=True,
                                                as_supervised=True,
                                                with_info=True)
print(info)
show_examples(jokempo_train, info)<jupyter_output><empty_output><jupyter_text>Preparando o pipeline para o treinamento O `tf.data` é uma API do TensorFlow que permite construir pipelines de dados. Essa API é altamente flexível, permitindo a construção de pipelines complexos a partir de operações simples.Neste tutorial deseja-se construir um pipeline que leia as imagens do arquivo, faça aumento de dados, redimensione para o tamanho de entrada da rede e produza batches com imagens aleatórias a cada época. Para fazer isso, a API do `tf.data` possui várias operações que podem ser chamadas encadeadamente para serem executadas na sequência desejada: repeat, shufle, cache, etc. Em especial a operação `map` pode executar uma função construída pela usuário para fazer uma transformação nos dados, como a que será usada para aumento de dados.Uma das utilidades do `tf.data` é fornecer dados em tempo ábil para o treinamento, evitando gargalos. Por exemplo, enquanto o treinamento do modelo ocorre na GPU, o pipeline pode executar operações na CPU para interações futuras. Duas operações que podem ser adicionadas ao pipeline com essa finalidade são a `cache` que armazena os dados em memória após a primeira leitura do disco e a `prefetch` que faz explicitamente o carregamento dos dados para as próximas interações enquanto a interação atual de treinamento está sendo executada.A maioria das operações do `tf.data` são configuráveis pelo usuário. Mas também há a opção, ainda experimental, de deixar que o próprio pipeline escolha os melhores valores atraǘes da opção `AUTOTUNE`.<jupyter_code>TRAIN_LENGHT = info.splits['train'].num_examples
TEST_LENGHT = info.splits['test'].num_examples
NUM_CLASSES = info.features['label'].num_classes
def deg2rad(deg):
    pi_on_180 = 0.017453292519943295
    return deg * pi_on_180
def resize_image(image, label):
    image = tf.image.resize(image, [IMAGE_WIDTH, IMAGE_HEIGHT])
    
    return image, label
def read_decode_image(image, label):
    image = tf.cast(image, tf.float32) / 255.
    
    return image, label<jupyter_output><empty_output><jupyter_text>Aumento de dados Quando a base de dados do problema é pequena, uma técnica comum para reduzir o overfeating é a geração de instâncias sintéticas a partir das instâncias existentes. Essas novas instâncis sintéticas devem fazer sentido para o problema tratado.Para problemas de visão computacional são comuns operações como recorte aleatório, espelhamento, rotação, zoom, entre outros. Ao aplicar essas operações deve-se tomar o cuidado para que não destruam o significado da instância. Por exemplo, o espelhamento horizontal funciona bem para a base rock_paper_scissors pois gerariam imagens correspondentes da outra mão. No entanto, para reconhecimento de caracteres em imagens o espelhamento horizontal não é válido pois destrói a informação.Para este tutorial serão aplicadas as operações de rotação, espelhamento horizontal e recorte aleatório.<jupyter_code>def apply_augmentation(image, label):
    rotation_theta = deg2rad(ROTATION_ANGLE)
    
    image = tf.image.resize(image, [IMAGE_PRE_CROP_WIDTH, IMAGE_PRE_CROP_HEIGHT])
    image = tf.image.random_flip_left_right(image)
    
    # Sometimes tfa.image.rotate will make Google Colab crash.
    # Disable the following operation if it happens
    image = tfa.image.rotate(image,
                             tf.random.uniform(shape=[1], minval=-rotation_theta, maxval=rotation_theta),
                             interpolation='BILINEAR')
    image = tf.image.random_crop(image, size=[IMAGE_WIDTH, IMAGE_HEIGHT, 3])
    
    return image, label
jokempo_train = jokempo_train.map(read_decode_image, num_parallel_calls=AUTOTUNE)
jokempo_train = jokempo_train.map(apply_augmentation, num_parallel_calls=AUTOTUNE)
show_examples(jokempo_train, info)<jupyter_output><empty_output><jupyter_text>Preparação do dataset para treinamento:<jupyter_code>jokempo_train = jokempo_train.repeat(EPOCHS)
jokempo_train = jokempo_train.shuffle(buffer_size=TRAIN_LENGHT)
jokempo_train = jokempo_train.batch(BATCH_SIZE, drop_remainder=True)
jokempo_train = jokempo_train.prefetch(buffer_size=AUTOTUNE)<jupyter_output><empty_output><jupyter_text>Para o pipeline do conjunto de teste não se aplica o aumento de dados mas é necessário redimensionar as imagens para o tamanho de entrada da rede:<jupyter_code>jokempo_test = jokempo_test.map(read_decode_image, num_parallel_calls=AUTOTUNE)
jokempo_test = jokempo_test.map(resize_image, num_parallel_calls=AUTOTUNE)
jokempo_test = jokempo_test.repeat(EPOCHS)
jokempo_test = jokempo_test.batch(BATCH_SIZE, drop_remainder=True)
jokempo_test = jokempo_test.prefetch(buffer_size=AUTOTUNE)<jupyter_output><empty_output><jupyter_text>Preparando o modelo com TensorFlow Hub Uma forma de utilizar um modelo do TensorFlow Hub como extrator de características é carregá-lo como uma camada do Keras. Nesse caso, essa "camada" funciona como uma caixa-preta.Para carregar o modelo utiliza-se a função `hub.KerasLaye` com do link do modelo escolhido. Para este tutorial, escolheu-se a MobileNet tamanho padrão (multiplicador 1.00) com entrade de 224x224 treinada na ImageNet. Ao escolher um modelo, deve-se atentar se ele inclui ou não a última camada. Neste tutoral será usado sem a última camada (feature_vector) e será adicionada uma camada densa com a quantidade de classes do problema tratado.Ao carregador o modelo do TensorFlow Hub pode-se configurar algumas opções, neste tutorial são usadas:* `input_shape`: tamanho de entrada da rede. Optou-se por manter o padrão já treinado: 224x224.* `trainable`: se os pesos do modelo serão treináveis. Para este tutorial eles serão congelados. Atenção: nem todos os modelos do TensorFlow Hub permitem retreinar os pesos, deve-se verificar previamente.<jupyter_code>MODEL_URL = 'https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/feature_vector/4'
feature_extractor = hub.KerasLayer(MODEL_URL,
                                   input_shape=(IMAGE_WIDTH, IMAGE_HEIGHT, 3),
                                   trainable=False)
model = Sequential([
  feature_extractor,
  Dense(NUM_CLASSES)
])
model.summary()
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])<jupyter_output><empty_output><jupyter_text>Treinamento do modelo Para pipelines de `tf.data` utiliza-se a função `fit` para o treinar o modelo. Como está sendo utilizada a operação `repeat`, o modelo não tem como saber quando uma nova época começa pois o pipeline repete os dados quando chega ao final de uma interação sobre todas as instâncias. Por isso, utiliza-se a opção `steps_per_epoch` para indicar ao modelo quantas interações serão realizadas para considerar uma época terminada, isto é, a quantidade de instâncias dividida pelo tamanho de um batch.<jupyter_code>history = model.fit(jokempo_train,
                    steps_per_epoch=TRAIN_LENGHT // BATCH_SIZE,
                    epochs=EPOCHS,
                    validation_data=jokempo_test,
                    validation_steps=TEST_LENGHT // BATCH_SIZE,
                    initial_epoch=0)<jupyter_output>Train for 78 steps, validate for 11 steps
Epoch 1/10
78/78 [==============================] - 10s 134ms/step - loss: 0.4306 - accuracy: 0.8598 - val_loss: 0.2708 - val_accuracy: 0.9403
Epoch 2/10
78/78 [==============================] - 4s 49ms/step - loss: 0.1066 - accuracy: 0.9872 - val_loss: 0.3014 - val_accuracy: 0.9119
Epoch 3/10
78/78 [==============================] - 4s 51ms/step - loss: 0.0642 - accuracy: 0.9912 - val_loss: 0.2601 - val_accuracy: 0.9233
Epoch 4/10
78/78 [==============================] - 4s 51ms/step - loss: 0.0515 - accuracy: 0.9952 - val_loss: 0.2212 - val_accuracy: 0.9318
Epoch 5/10
78/78 [==============================] - 3s 44ms/step - loss: 0.0378 - accuracy: 0.9948 - val_loss: 0.2078 - val_accuracy: 0.9460
Epoch 6/10
78/78 [==============================] - 4s 49ms/step - loss: 0.0279 - accuracy: 0.9980 - val_loss: 0.2242 - val_accuracy: 0.9148
Epoch 7/10
78/78 [==============================] - 4s 50ms/step - loss: 0.0220 - accuracy: 0.9988 - val_loss:[...]<jupyter_text>Resultados<jupyter_code>plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.ylim([0.5, 1.1])
plt.title('Acurácia do Modelo')
plt.ylabel('acurácia')
plt.xlabel('Época')
plt.legend(['train', 'test'], loc='lower left')
plt.show()
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.ylim([-0.05, 0.55])
plt.title('Loss do Modelo')
plt.ylabel('loss')
plt.xlabel('Época')
plt.legend(['train', 'test'], loc='lower left')
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.07638934778299082, 'fraction_numerical': 0.020881517214035552, 'mean_word_length': 4.7152567975830815, 'pattern_counts': {'":': 0, '<': 44, '<?xml version=': 0, '>': 44, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '482255', 'license': 'MIT', 'n_tokens_mistral': 5128, 'n_tokens_neox': 4842, 'n_words': 1676, 'path': 'Transfer-Learning-with-TensorflowHub.ipynb', 'repo_name': 'alcunha/transfer-learning-with-tfhub', 'size': '15133'}