Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/tutorials/images/transfer_learning.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.
#@title MIT License # # Copyright (c) 2017 François Chollet # IGNORE_COPYRIGHT: cleared by OSS licensing # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE.

Transferência de Aprendizado com uma ConvNet Pré-Treinada

Neste tutorial, você aprenderá a classificar imagens de cães e gatos usando a transferência de aprendizado de uma rede pré-treinada.

Um modelo pré-treinado é uma rede salva que foi treinada anteriormente em um grande dataset, geralmente em uma tarefa de classificação de imagem em larga escala. Você usa o modelo pré-treinado como está ou usa a transferência de aprendizado para personalizar esse modelo para uma determinada tarefa.

A intuição por trás da transferência de aprendizado para classificação de imagens é que, se um modelo for treinado em um dataset grande e geral o suficiente, esse modelo servirá efetivamente como um modelo genérico do mundo visual. Você pode aproveitar esses mapas de características aprendidas sem precisar começar do zero treinando um modelo grande em um grande dataset.

Neste notebook, você tentará duas maneiras de personalizar um modelo pré-treinado:

  1. Extração de características: use as representações aprendidas por uma rede anterior para extrair características significativas de novas amostras. Você simplesmente adiciona um novo classificador, que será treinado do zero, sobre o modelo pré-treinado, para que você possa adaptar novamente os mapas de características aprendidas anteriormente para o dataset.

Você não precisa (re) treinar o modelo inteiro. A rede convolucional de base já contém características que são genericamente úteis para classificar imagens. No entanto, a parte final de classificação do modelo pré-treinado é específica para a tarefa de classificação original e subsequentemente específica para o conjunto de classes em que o modelo foi treinado.

  1. Ajuste fino: descongele algumas das camadas superiores de uma base modelo congelada e treine em conjunto as camadas de classificação recém-adicionadas e as últimas camadas do modelo base. Isso nos permite "ajustar" as representações de características de ordem superior no modelo base para torná-las mais relevantes para a tarefa específica.

Você seguirá o fluxo de trabalho geral de aprendizado de máquina.

  1. Examine e entenda os dados

  2. Crie um pipeline de entrada, neste caso usando o Keras ImageDataGenerator

  3. Componha o modelo

    • Carregue no modelo base pré-treinado (e pesos pré-treinados)

    • Empilhe as camadas de classificação na parte superior

  4. Treine o modelo

  5. Avalie o modelo

import matplotlib.pyplot as plt import numpy as np import os import tensorflow as tf

Pré-Processamento dos Dados

Baixe os dados

Neste tutorial, você usará um dataset contendo milhares de imagens de cães e gatos. Baixe e extraia um arquivo zip contendo as imagens e, em seguida, crie um tf.data.Dataset para treinamento e validação usando o utilitário tf.keras.utils.image_dataset_from_directory. Você pode aprender mais sobre como carregar imagens neste tutorial.

_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip' path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True) PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered') train_dir = os.path.join(PATH, 'train') validation_dir = os.path.join(PATH, 'validation') BATCH_SIZE = 32 IMG_SIZE = (160, 160) train_dataset = tf.keras.utils.image_dataset_from_directory(train_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)
validation_dataset = tf.keras.utils.image_dataset_from_directory(validation_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)

Mostre as duas primeiras imagens e rótulos do dataset de treinamento:

class_names = train_dataset.class_names plt.figure(figsize=(10, 10)) for images, labels in train_dataset.take(1): for i in range(9): ax = plt.subplot(3, 3, i + 1) plt.imshow(images[i].numpy().astype("uint8")) plt.title(class_names[labels[i]]) plt.axis("off")

Como o dataset original não contém um dataset de testes, você criará um. Para fazer isso, determine quantos lotes de dados estão disponíveis no dataset de validação usando tf.data.experimental.cardinality e mova 20% deles para um dataset de teste.

val_batches = tf.data.experimental.cardinality(validation_dataset) test_dataset = validation_dataset.take(val_batches // 5) validation_dataset = validation_dataset.skip(val_batches // 5)
print('Number of validation batches: %d' % tf.data.experimental.cardinality(validation_dataset)) print('Number of test batches: %d' % tf.data.experimental.cardinality(test_dataset))

Configure o dataset para melhor desempenho

Use a pré-busca em buffer para carregar imagens do disco sem bloquear a E/S. Para saber mais sobre esse método, consulte o guia desempenho de dados.

AUTOTUNE = tf.data.AUTOTUNE train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE) validation_dataset = validation_dataset.prefetch(buffer_size=AUTOTUNE) test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)

Use ampliação de dados

Quando você não tem um grande dataset de imagens, é uma boa prática introduzir artificialmente a diversidade de amostras aplicando transformações aleatórias, mas realistas, às imagens de treinamento, como rotação e inversão horizontal. Isto ajuda a expor o modelo a diferentes aspectos dos dados de treinamento e a reduzir o overfitting. Você pode aprender mais sobre ampliação de dados neste tutorial.

data_augmentation = tf.keras.Sequential([ tf.keras.layers.RandomFlip('horizontal'), tf.keras.layers.RandomRotation(0.2), ])

Observação: estas camadas ficam ativas somente durante o treinamento, quando você faz uma chamada a Model.fit. Elas ficam inativas quando o modelo é usado no modo de inferência em Model.evaluate, Model.predict ou Model.call.

Vamos aplicar essas camadas repetidamente na mesma imagem e ver o resultado.

for image, _ in train_dataset.take(1): plt.figure(figsize=(10, 10)) first_image = image[0] for i in range(9): ax = plt.subplot(3, 3, i + 1) augmented_image = data_augmentation(tf.expand_dims(first_image, 0)) plt.imshow(augmented_image[0] / 255) plt.axis('off')

Redimensione valores de pixel

Em alguns instantes, você fará o download do tf.keras.applications.MobileNetV2 para usar como modelo básico. Este modelo espera valores de pixel em [-1, 1], mas neste ponto, os valores de pixel em suas imagens estão em [0, 255]. Para redimensioná-las, use o método de pré-processamento incluído no modelo.

preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input

Observação: como alternativa, você pode redimensionar os valores de pixel de [0, 255] para [-1, 1] usando tf.keras.layers.Rescaling.

rescale = tf.keras.layers.Rescaling(1./127.5, offset=-1)

Observação: Se estiver usando outro tf.keras.applications, certifique-se de verificar o documento da API para determinar se eles esperam pixels em [-1, 1] ou [0, 1], ou use a função preprocess_input incluída.

Crie o modelo base a partir das ConvNets pré-treinadas

Você criará o modelo base a partir do modelo MobileNet V2 desenvolvido no Google. Isso é pré-treinado no dataset ImageNet, um grande dataset composto por 1,4 milhões de imagens e 1000 classes. O ImageNet é um dataset de treinamento de pesquisa com uma ampla variedade de categorias, como jaca e seringa. Essa base de conhecimento nos ajudará a classificar cães e gatos de nosso dataset específico.

Primeiro, você precisa escolher qual camada do MobileNet V2 usará para extração de características. A última camada de classificação (na parte superior, como a maioria dos diagramas dos modelos de aprendizado de máquina vai de baixo para cima) não é muito útil. Em vez disso, você seguirá a prática comum de depender da última camada antes da operação de nivelamento. Essa camada é chamada de "camada de gargalo". Os recursos da camada de gargalo retêm mais generalidade em comparação com a camada final/superior.

Primeiro, instancie um modelo MobileNet V2 pré-carregado com pesos treinados no ImageNet. Ao especificar o argumento include_top = False, você carrega uma rede que não inclui as camadas de classificação na parte superior, o que é ideal para a extração de características.

# Create the base model from the pre-trained model MobileNet V2 IMG_SHAPE = IMG_SIZE + (3,) base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')

Este extrator de características converte cada imagem 160x160x3 em um bloco de características 5x5x1280. Veja o que ele faz com o lote de imagens de exemplo:

image_batch, label_batch = next(iter(train_dataset)) feature_batch = base_model(image_batch) print(feature_batch.shape)

Extração de Características

Nesta etapa, você congelará a base convolucional criada a partir da etapa anterior e utilizará como extrator de características. Além disso, você adiciona um classificador sobre ele e treina o classificador de nível superior.

Congele a base convolucional

É importante congelar a base convolucional antes de compilar e treinar o modelo. O congelamento (definindo layer.trainable = False) evita que os pesos de uma determinada camada sejam atualizados durante o treinamento. O MobileNet V2 tem muitas camadas, portanto, definir o sinalizador trainable de todo o modelo como False irá congelar todas elas.

base_model.trainable = False

Observações importantes sobre as camadas BatchNormalization

Muitos modelos contêm camadas tf.keras.layers.BatchNormalization. Esta camada é um caso especial e devem ser tomadas precauções no contexto do tuning, conforme mostrado mais adiante neste tutorial.

Quando você define layer.trainable = False, a camada BatchNormalization será executada no modo de inferência e não atualizará suas estatísticas de média e variância.

Ao descongelar um modelo que contém camadas BatchNormalization para fazer o tuning, você deve manter as camadas BatchNormalization no modo de inferência passando training = False ao chamar o modelo base. Caso contrário, as atualizações aplicadas aos pesos não treináveis ​​destruirão o que o modelo aprendeu.

Para mais detalhes, consulte o Guia de aprendizado por transferência.

# Let's take a look at the base model architecture base_model.summary()

Adicione um cabeçalho de classificação

Para gerar previsões a partir do bloco de características, calcule a média dos espaços 5x5, usando uma camada tf.keras.layers.GlobalAveragePooling2D para converter as características em um único vetor de 1280 elementos por imagem.

global_average_layer = tf.keras.layers.GlobalAveragePooling2D() feature_batch_average = global_average_layer(feature_batch) print(feature_batch_average.shape)

Aplique uma camada tf.keras.layers.Dense para converter esses recursos em uma única previsão por imagem. Você não precisa de uma função de ativação aqui porque esta previsão será tratada como um logit ou um valor bruto de previsão. Números positivos predizem a classe 1, números negativos predizem a classe 0.

prediction_layer = tf.keras.layers.Dense(1) prediction_batch = prediction_layer(feature_batch_average) print(prediction_batch.shape)

Crie um modelo encadeando as camadas de ampliação de dados, redimensionamento, base_model e extrator de recursos usando a API Keras Functional. Conforme mencionado anteriormente, use training=False pois nosso modelo contém uma camada BatchNormalization.

inputs = tf.keras.Input(shape=(160, 160, 3)) x = data_augmentation(inputs) x = preprocess_input(x) x = base_model(x, training=False) x = global_average_layer(x) x = tf.keras.layers.Dropout(0.2)(x) outputs = prediction_layer(x) model = tf.keras.Model(inputs, outputs)
model.summary()

Os mais de 8 milhões de parâmetros no MobileNet estão congelados, mas existem 1,2 mil parâmetros treináveis ​​na camada Densa. Eles são divididos entre dois objetos tf.Variable, os pesos e os bias.

len(model.trainable_variables)
tf.keras.utils.plot_model(model, show_shapes=True)

Compile o modelo

Compile o modelo antes de treiná-lo. Como existem duas classes, use a perda tf.keras.losses.BinaryCrossentropy com from_logits=True, pois o modelo fornece uma saída linear.

base_learning_rate = 0.0001 model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate), loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=[tf.keras.metrics.BinaryAccuracy(threshold=0, name='accuracy')])

Treine o modelo

Após treinar por 10 épocas, você deverá perceber uma precisão de aproximadamente 96% no dataset de validação.

initial_epochs = 10 loss0, accuracy0 = model.evaluate(validation_dataset)
print("initial loss: {:.2f}".format(loss0)) print("initial accuracy: {:.2f}".format(accuracy0))
history = model.fit(train_dataset, epochs=initial_epochs, validation_data=validation_dataset)

Curvas de aprendizado

Vamos dar uma olhada nas curvas de aprendizado da acurácia/perda do treinamento e da validação ao usar o modelo base do MobileNet V2 como um extrator de características fixo.

acc = history.history['accuracy'] val_acc = history.history['val_accuracy'] loss = history.history['loss'] val_loss = history.history['val_loss'] plt.figure(figsize=(8, 8)) plt.subplot(2, 1, 1) plt.plot(acc, label='Training Accuracy') plt.plot(val_acc, label='Validation Accuracy') plt.legend(loc='lower right') plt.ylabel('Accuracy') plt.ylim([min(plt.ylim()),1]) plt.title('Training and Validation Accuracy') plt.subplot(2, 1, 2) plt.plot(loss, label='Training Loss') plt.plot(val_loss, label='Validation Loss') plt.legend(loc='upper right') plt.ylabel('Cross Entropy') plt.ylim([0,1.0]) plt.title('Training and Validation Loss') plt.xlabel('epoch') plt.show()

Nota: Se você está se perguntando por que as métricas de validação são claramente melhores que as métricas de treinamento, o principal fator é que camadas como tf.keras.layers.BatchNormalization e tf.keras.layers.Dropout afetam a acurácia durante o treinamento. Eles são desativados ao calcular a perda de validação.

Em menor grau, é também porque as métricas de treinamento relatam a média de uma época, enquanto as métricas de validação são avaliadas após a época, portanto, as métricas de validação veem um modelo que foi treinado um pouco mais.

Ajuste fino (tuning)

No experimento de extração de características, você treinava apenas algumas camadas sobre um modelo base do MobileNet V2. Os pesos da rede pré-treinada não foram atualizados durante o treinamento.

Uma maneira de aumentar ainda mais o desempenho é treinar (ou "ajustar") os pesos das camadas superiores do modelo pré-treinado, juntamente com o treinamento do classificador adicionado. O processo de treinamento forçará os pesos a serem ajustados com mapas de características genéricas para recursos associados especificamente ao dataset.

Nota: Isso só deve ser tentado depois de você treinar o classificador de nível superior com o modelo pré-treinado definido como não treinável. Se você adicionar um classificador inicializado aleatoriamente sobre um modelo pré-treinado e tentar treinar todas as camadas em conjunto, a magnitude das atualizações de gradiente será muito grande (devido aos pesos aleatórios do classificador) e seu modelo pré-treinado esquecerá o que aprendeu.

Além disso, você deve tentar ajustar um pequeno número de camadas superiores em vez de todo o modelo MobileNet. Na maioria das redes convolucionais, quanto mais alta a camada, mais especializada ela é. As primeiras camadas aprendem recursos muito simples e genéricos que generalizam para quase todos os tipos de imagens. À medida que você aumenta, as características são cada vez mais específicas para o dataset no qual o modelo foi treinado. O objetivo do ajuste fino é adaptar essas características especializados para trabalhar com o novo dataset, em vez de substituir o aprendizado genérico.

Descongele as camadas superiores do modelo

Tudo o que você precisa fazer é descongelar o base_model e definir as camadas inferiores para que não possam ser treinadas. Em seguida, recompile o modelo (necessário para que essas alterações entrem em vigor) e reinicie o treinamento.

base_model.trainable = True
# Let's take a look to see how many layers are in the base model print("Number of layers in the base model: ", len(base_model.layers)) # Fine-tune from this layer onwards fine_tune_at = 100 # Freeze all the layers before the `fine_tune_at` layer for layer in base_model.layers[:fine_tune_at]: layer.trainable = False

Compile o modelo

Compile o modelo usando uma taxa de aprendizado muito menor.

model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), optimizer = tf.keras.optimizers.RMSprop(learning_rate=base_learning_rate/10), metrics=[tf.keras.metrics.BinaryAccuracy(threshold=0, name='accuracy')])
model.summary()
len(model.trainable_variables)

Continue treinando o modelo

Se você treinou para convergência anteriormente, esta etapa melhorará sua acurácia em alguns pontos percentuais.

fine_tune_epochs = 10 total_epochs = initial_epochs + fine_tune_epochs history_fine = model.fit(train_dataset, epochs=total_epochs, initial_epoch=history.epoch[-1], validation_data=validation_dataset)

Vamos dar uma olhada nas curvas de aprendizado da acurácia/perda do treinamento e da validação ao ajustar as últimas camadas do modelo base do MobileNet V2 e treinar o classificador sobre ele. A perda de validação é muito maior do que a perda de treinamento, portanto, você pode obter um overfitting.

Você também pode obter um overfitting, pois o novo dataset de treinamento é relativamente pequeno e semelhante aos datasets originais do MobileNet V2.

Após o ajuste fino, o modelo atinge quase 98% de acurácia.

acc += history_fine.history['accuracy'] val_acc += history_fine.history['val_accuracy'] loss += history_fine.history['loss'] val_loss += history_fine.history['val_loss']
plt.figure(figsize=(8, 8)) plt.subplot(2, 1, 1) plt.plot(acc, label='Training Accuracy') plt.plot(val_acc, label='Validation Accuracy') plt.ylim([0.8, 1]) plt.plot([initial_epochs-1,initial_epochs-1], plt.ylim(), label='Start Fine Tuning') plt.legend(loc='lower right') plt.title('Training and Validation Accuracy') plt.subplot(2, 1, 2) plt.plot(loss, label='Training Loss') plt.plot(val_loss, label='Validation Loss') plt.ylim([0, 1.0]) plt.plot([initial_epochs-1,initial_epochs-1], plt.ylim(), label='Start Fine Tuning') plt.legend(loc='upper right') plt.title('Training and Validation Loss') plt.xlabel('epoch') plt.show()

Avaliação e previsão

Finalmente, você pode verificar o desempenho do modelo em novos dados usando o dataset de testes.

loss, accuracy = model.evaluate(test_dataset) print('Test accuracy :', accuracy)

E agora você está pronto para usar este modelo para prever se seu animal de estimação é um gato ou um cachorro.

# Retrieve a batch of images from the test set image_batch, label_batch = test_dataset.as_numpy_iterator().next() predictions = model.predict_on_batch(image_batch).flatten() # Apply a sigmoid since our model returns logits predictions = tf.nn.sigmoid(predictions) predictions = tf.where(predictions < 0.5, 0, 1) print('Predictions:\n', predictions.numpy()) print('Labels:\n', label_batch) plt.figure(figsize=(10, 10)) for i in range(9): ax = plt.subplot(3, 3, i + 1) plt.imshow(image_batch[i].astype("uint8")) plt.title(class_names[predictions[i]]) plt.axis("off")

Resumo:

  • Usando um modelo pré-treinado para extração de características: Ao trabalhar com um pequeno dataset, é uma prática comum tirar proveito das características aprendidas por um modelo treinado em um dataset maior no mesmo domínio. Isso é feito instanciando o modelo pré-treinado e adicionando um classificador totalmente conectado na parte superior. O modelo pré-treinado é "congelado" e apenas os pesos do classificador são atualizados durante o treinamento. Nesse caso, a base convolucional extraiu todas as características associadas a cada imagem e você acabou de treinar um classificador que determina a classe da imagem, considerando esse conjunto de características extraídas.

  • Ajuste fino de um modelo pré-treinado: Para melhorar ainda mais o desempenho, é possível redirecionar as camadas de nível superior dos modelos pré-treinados para o novo dataset via ajuste fino. Nesse caso, você ajustou seus pesos para que seu modelo aprendesse características de alto nível específicas ao dataset. Essa técnica geralmente é recomendada quando o dataset de treinamento é grande e muito semelhante ao dataset original em que o modelo pré-treinado foi treinado.

Para saber mais, veja o Guia de aprendizado por transferência.