O Google I / O retorna de 18 a 20 de maio! Reserve espaço e monte sua agenda Cadastre-se agora

Classifique flores com transferência de aprendizagem

Ver no TensorFlow.org Executar no Google Colab Ver no GitHub Baixar caderno Veja o modelo TF Hub

Você já viu uma linda flor e se perguntou que tipo de flor é? Bem, você não é o primeiro, então vamos construir uma maneira de identificar o tipo de flor de uma foto!

Para classificar imagens, um tipo específico de rede neural profunda , chamada rede neural convolucional , provou ser particularmente poderoso. No entanto, as redes neurais convolucionais modernas têm milhões de parâmetros. Treiná-los do zero requer muitos dados de treinamento rotulados e muito poder de computação (centenas de horas de GPU ou mais). Temos apenas cerca de três mil fotos etiquetadas e queremos gastar muito menos tempo, por isso precisamos ser mais inteligentes.

Usaremos uma técnica chamada transferência de aprendizagem, onde pegamos uma rede pré-treinada (treinada em cerca de um milhão de imagens gerais), a usamos para extrair recursos e treinamos uma nova camada no topo para nossa própria tarefa de classificar imagens de flores.

Configurar

import collections
import io
import math
import os
import random
from six.moves import urllib

from IPython.display import clear_output, Image, display, HTML

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

import tensorflow_hub as hub

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn.metrics as sk_metrics
import time
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/compat/v2_compat.py:96: disable_resource_variables (from tensorflow.python.ops.variable_scope) is deprecated and will be removed in a future version.
Instructions for updating:
non-resource variables are not supported in the long term

O conjunto de dados de flores

O conjunto de dados de flores consiste em imagens de flores com 5 rótulos de classe possíveis.

Ao treinar um modelo de aprendizado de máquina, dividimos nossos dados em conjuntos de dados de treinamento e teste. Vamos treinar o modelo em nossos dados de treinamento e, em seguida, avaliar o desempenho do modelo em dados que nunca viu - o conjunto de teste.

Vamos baixar nossos exemplos de treinamento e teste (pode demorar um pouco) e dividi-los em conjuntos de treinamento e teste.

Execute as duas células a seguir:

FLOWERS_DIR = './flower_photos'
TRAIN_FRACTION = 0.8
RANDOM_SEED = 2018


def download_images():
  """If the images aren't already downloaded, save them to FLOWERS_DIR."""
  if not os.path.exists(FLOWERS_DIR):
    DOWNLOAD_URL = 'http://download.tensorflow.org/example_images/flower_photos.tgz'
    print('Downloading flower images from %s...' % DOWNLOAD_URL)
    urllib.request.urlretrieve(DOWNLOAD_URL, 'flower_photos.tgz')
    !tar xfz flower_photos.tgz
  print('Flower photos are located in %s' % FLOWERS_DIR)


def make_train_and_test_sets():
  """Split the data into train and test sets and get the label classes."""
  train_examples, test_examples = [], []
  shuffler = random.Random(RANDOM_SEED)
  is_root = True
  for (dirname, subdirs, filenames) in tf.gfile.Walk(FLOWERS_DIR):
    # The root directory gives us the classes
    if is_root:
      subdirs = sorted(subdirs)
      classes = collections.OrderedDict(enumerate(subdirs))
      label_to_class = dict([(x, i) for i, x in enumerate(subdirs)])
      is_root = False
    # The sub directories give us the image files for training.
    else:
      filenames.sort()
      shuffler.shuffle(filenames)
      full_filenames = [os.path.join(dirname, f) for f in filenames]
      label = dirname.split('/')[-1]
      label_class = label_to_class[label]
      # An example is the image file and it's label class.
      examples = list(zip(full_filenames, [label_class] * len(filenames)))
      num_train = int(len(filenames) * TRAIN_FRACTION)
      train_examples.extend(examples[:num_train])
      test_examples.extend(examples[num_train:])

  shuffler.shuffle(train_examples)
  shuffler.shuffle(test_examples)
  return train_examples, test_examples, classes
# Download the images and split the images into train and test sets.
download_images()
TRAIN_EXAMPLES, TEST_EXAMPLES, CLASSES = make_train_and_test_sets()
NUM_CLASSES = len(CLASSES)

print('\nThe dataset has %d label classes: %s' % (NUM_CLASSES, CLASSES.values()))
print('There are %d training images' % len(TRAIN_EXAMPLES))
print('there are %d test images' % len(TEST_EXAMPLES))
Downloading flower images from http://download.tensorflow.org/example_images/flower_photos.tgz...
Flower photos are located in ./flower_photos

The dataset has 5 label classes: odict_values(['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips'])
There are 2934 training images
there are 736 test images

Explore os dados

O conjunto de dados de flores consiste em exemplos rotulados como imagens de flores. Cada exemplo contém uma imagem de flor JPEG e o rótulo da classe: que tipo de flor é. Vamos mostrar algumas imagens junto com seus rótulos.

Mostrar algumas imagens rotuladas

def get_label(example):
  """Get the label (number) for given example."""
  return example[1]

def get_class(example):
  """Get the class (string) of given example."""
  return CLASSES[get_label(example)]

def get_encoded_image(example):
  """Get the image data (encoded jpg) of given example."""
  image_path = example[0]
  return tf.gfile.GFile(image_path, 'rb').read()

def get_image(example):
  """Get image as np.array of pixels for given example."""
  return plt.imread(io.BytesIO(get_encoded_image(example)), format='jpg')

def display_images(images_and_classes, cols=5):
  """Display given images and their labels in a grid."""
  rows = int(math.ceil(len(images_and_classes) / cols))
  fig = plt.figure()
  fig.set_size_inches(cols * 3, rows * 3)
  for i, (image, flower_class) in enumerate(images_and_classes):
    plt.subplot(rows, cols, i + 1)
    plt.axis('off')
    plt.imshow(image)
    plt.title(flower_class)

NUM_IMAGES = 15
display_images([(get_image(example), get_class(example))
               for example in TRAIN_EXAMPLES[:NUM_IMAGES]])

png

Construir o modelo

Carregaremos um módulo de vetor de recursos de imagem TF-Hub , empilharemos um classificador linear nele e adicionaremos operações de treinamento e avaliação. A célula a seguir constrói um gráfico TF que descreve o modelo e seu treinamento, mas não executa o treinamento (essa será a próxima etapa).

LEARNING_RATE = 0.01

tf.reset_default_graph()

# Load a pre-trained TF-Hub module for extracting features from images. We've
# chosen this particular module for speed, but many other choices are available.
image_module = hub.Module('https://tfhub.dev/google/imagenet/mobilenet_v2_035_128/feature_vector/2')

# Preprocessing images into tensors with size expected by the image module.
encoded_images = tf.placeholder(tf.string, shape=[None])
image_size = hub.get_expected_image_size(image_module)


def decode_and_resize_image(encoded):
  decoded = tf.image.decode_jpeg(encoded, channels=3)
  decoded = tf.image.convert_image_dtype(decoded, tf.float32)
  return tf.image.resize_images(decoded, image_size)


batch_images = tf.map_fn(decode_and_resize_image, encoded_images, dtype=tf.float32)

# The image module can be applied as a function to extract feature vectors for a
# batch of images.
features = image_module(batch_images)


def create_model(features):
  """Build a model for classification from extracted features."""
  # Currently, the model is just a single linear layer. You can try to add
  # another layer, but be careful... two linear layers (when activation=None)
  # are equivalent to a single linear layer. You can create a nonlinear layer
  # like this:
  # layer = tf.layers.dense(inputs=..., units=..., activation=tf.nn.relu)
  layer = tf.layers.dense(inputs=features, units=NUM_CLASSES, activation=None)
  return layer


# For each class (kind of flower), the model outputs some real number as a score
# how much the input resembles this class. This vector of numbers is often
# called the "logits".
logits = create_model(features)
labels = tf.placeholder(tf.float32, [None, NUM_CLASSES])

# Mathematically, a good way to measure how much the predicted probabilities
# diverge from the truth is the "cross-entropy" between the two probability
# distributions. For numerical stability, this is best done directly from the
# logits, not the probabilities extracted from them.
cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels=labels)
cross_entropy_mean = tf.reduce_mean(cross_entropy)

# Let's add an optimizer so we can train the network.
optimizer = tf.train.GradientDescentOptimizer(learning_rate=LEARNING_RATE)
train_op = optimizer.minimize(loss=cross_entropy_mean)

# The "softmax" function transforms the logits vector into a vector of
# probabilities: non-negative numbers that sum up to one, and the i-th number
# says how likely the input comes from class i.
probabilities = tf.nn.softmax(logits)

# We choose the highest one as the predicted class.
prediction = tf.argmax(probabilities, 1)
correct_prediction = tf.equal(prediction, tf.argmax(labels, 1))

# The accuracy will allow us to eval on our test set. 
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
WARNING:tensorflow:From <ipython-input-1-76baecbc8e0d>:20: calling map_fn (from tensorflow.python.ops.map_fn) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Use fn_output_signature instead
WARNING:tensorflow:From <ipython-input-1-76baecbc8e0d>:20: calling map_fn (from tensorflow.python.ops.map_fn) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Use fn_output_signature instead
INFO:tensorflow:Saver not created because there are no variables in the graph to restore
INFO:tensorflow:Saver not created because there are no variables in the graph to restore
/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/legacy_tf_layers/core.py:171: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
  warnings.warn('`tf.layers.dense` is deprecated and '
/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/engine/base_layer_v1.py:1719: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  warnings.warn('`layer.apply` is deprecated and '

Treine a rede

Agora que nosso modelo está construído, vamos treiná-lo e ver como ele funciona em nosso conjunto de teste.

# How long will we train the network (number of batches).
NUM_TRAIN_STEPS = 100
# How many training examples we use in each step.
TRAIN_BATCH_SIZE = 10
# How often to evaluate the model performance.
EVAL_EVERY = 10

def get_batch(batch_size=None, test=False):
  """Get a random batch of examples."""
  examples = TEST_EXAMPLES if test else TRAIN_EXAMPLES
  batch_examples = random.sample(examples, batch_size) if batch_size else examples
  return batch_examples

def get_images_and_labels(batch_examples):
  images = [get_encoded_image(e) for e in batch_examples]
  one_hot_labels = [get_label_one_hot(e) for e in batch_examples]
  return images, one_hot_labels

def get_label_one_hot(example):
  """Get the one hot encoding vector for the example."""
  one_hot_vector = np.zeros(NUM_CLASSES)
  np.put(one_hot_vector, get_label(example), 1)
  return one_hot_vector

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  for i in range(NUM_TRAIN_STEPS):
    # Get a random batch of training examples.
    train_batch = get_batch(batch_size=TRAIN_BATCH_SIZE)
    batch_images, batch_labels = get_images_and_labels(train_batch)
    # Run the train_op to train the model.
    train_loss, _, train_accuracy = sess.run(
        [cross_entropy_mean, train_op, accuracy],
        feed_dict={encoded_images: batch_images, labels: batch_labels})
    is_final_step = (i == (NUM_TRAIN_STEPS - 1))
    if i % EVAL_EVERY == 0 or is_final_step:
      # Get a batch of test examples.
      test_batch = get_batch(batch_size=None, test=True)
      batch_images, batch_labels = get_images_and_labels(test_batch)
      # Evaluate how well our model performs on the test set.
      test_loss, test_accuracy, test_prediction, correct_predicate = sess.run(
        [cross_entropy_mean, accuracy, prediction, correct_prediction],
        feed_dict={encoded_images: batch_images, labels: batch_labels})
      print('Test accuracy at step %s: %.2f%%' % (i, (test_accuracy * 100)))
Test accuracy at step 0: 31.79%
Test accuracy at step 10: 55.57%
Test accuracy at step 20: 67.26%
Test accuracy at step 30: 74.86%
Test accuracy at step 40: 76.49%
Test accuracy at step 50: 77.72%
Test accuracy at step 60: 78.67%
Test accuracy at step 70: 79.89%
Test accuracy at step 80: 79.76%
Test accuracy at step 90: 77.04%
Test accuracy at step 99: 81.93%
def show_confusion_matrix(test_labels, predictions):
  """Compute confusion matrix and normalize."""
  confusion = sk_metrics.confusion_matrix(
    np.argmax(test_labels, axis=1), predictions)
  confusion_normalized = confusion.astype("float") / confusion.sum(axis=1)
  axis_labels = list(CLASSES.values())
  ax = sns.heatmap(
      confusion_normalized, xticklabels=axis_labels, yticklabels=axis_labels,
      cmap='Blues', annot=True, fmt='.2f', square=True)
  plt.title("Confusion matrix")
  plt.ylabel("True label")
  plt.xlabel("Predicted label")

show_confusion_matrix(batch_labels, test_prediction)

png

Previsões incorretas

Vamos dar uma olhada mais de perto nos exemplos de teste em que nosso modelo errou.

  • Existem exemplos com rótulos incorretos em nosso conjunto de teste?
  • Existem dados inválidos no conjunto de teste - imagens que não são realmente fotos de flores?
  • Existem imagens onde você pode entender por que o modelo cometeu um erro?
incorrect = [
    (example, CLASSES[prediction])
    for example, prediction, is_correct in zip(test_batch, test_prediction, correct_predicate)
    if not is_correct
]
display_images(
  [(get_image(example), "prediction: {0}\nlabel:{1}".format(incorrect_prediction, get_class(example)))
   for (example, incorrect_prediction) in incorrect[:20]])

png

Exercícios: Melhore o modelo!

Treinamos um modelo de linha de base, agora vamos tentar melhorá-lo para obter uma melhor precisão. (Lembre-se de que você precisará executar novamente as células ao fazer uma alteração.)

Exercício 1: Experimente um modelo de imagem diferente.

Com o TF-Hub, tentar alguns modelos de imagem diferentes é simples. Basta substituir o identificador "https://tfhub.dev/google/imagenet/mobilenet_v2_050_128/feature_vector/2" na chamada hub.Module() por um identificador de módulo diferente e rerun. Você pode ver todos os módulos de imagem disponíveis em tfhub.dev .

Uma boa escolha pode ser um dos outros módulos MobileNet V2 . Muitos dos módulos - incluindo os módulos MobileNet - foram treinados no conjunto de dados ImageNet que contém mais de 1 milhão de imagens e 1000 classes. A escolha de uma arquitetura de rede oferece uma compensação entre velocidade e precisão de classificação: modelos como MobileNet ou NASNet Mobile são rápidos e pequenos, arquiteturas mais tradicionais como Inception e ResNet foram projetadas para precisão.

Para a arquitetura Inception V3 maior, você também pode explorar os benefícios do pré-treinamento em um domínio mais próximo de sua própria tarefa: ele também está disponível como um módulo treinado no conjunto de dados iNaturalist de plantas e animais.

Exercício 2: adicione uma camada oculta.

Empilhe uma camada oculta entre os recursos de imagem extraídos e o classificador linear (na função create_model() acima). Para criar uma camada oculta não linear com, por exemplo, 100 nós, use tf.layers.dense com unidades definidas como 100 e ativação definida como tf.nn.relu . Alterar o tamanho da camada oculta afeta a precisão do teste? Adicionar uma segunda camada oculta melhora a precisão?

Exercício 3: Altere os hiperparâmetros.

Aumentar o número de etapas de treinamento melhora a precisão final? Você pode alterar a taxa de aprendizado para fazer seu modelo convergir mais rapidamente? O tamanho do lote de treinamento afeta o desempenho do seu modelo?

Exercício 4: tente um otimizador diferente.

Substitua o GradientDescentOptimizer básico por um otimizador mais sofisticado, por exemplo, AdagradOptimizer . Isso faz diferença para o treinamento do seu modelo? Se você quiser saber mais sobre os benefícios dos diferentes algoritmos de otimização, confira este post .

Quer saber mais?

Se você estiver interessado em uma versão mais avançada deste tutorial, confira o tutorial de retreinamento de imagem do TensorFlow que o orienta na visualização do treinamento usando TensorBoard, técnicas avançadas como aumento do conjunto de dados por distorção de imagens e substituição do conjunto de dados de flores para aprender um classificador de seu próprio conjunto de dados.

Você pode aprender mais sobre o TensorFlow em tensorflow.org e ver a documentação da API TF-Hub disponível em tensorflow.org/hub . Encontre os módulos TensorFlow Hub disponíveis em tfhub.dev, incluindo mais módulos de vetor de recurso de imagem e módulos de incorporação de texto.

Além disso, confira o Curso intensivo de aprendizado de máquina, que é a introdução prática e rápida do Google ao aprendizado de máquina.