Clasifique las flores con el aprendizaje por transferencia

Ver en TensorFlow.org Ejecutar en Google Colab Ver en GitHub Descargar cuaderno Ver modelo TF Hub

¿Alguna vez has visto una flor hermosa y te has preguntado qué tipo de flor es? Bueno, no eres el primero, ¡así que construyamos una manera de identificar el tipo de flor a partir de una foto!

Para la clasificación de imágenes, un tipo particular de red neuronal profundo, llamado una red neuronal convolucional ha demostrado ser particularmente potente. Sin embargo, las redes neuronales convolucionales modernas tienen millones de parámetros. Entrenarlos desde cero requiere una gran cantidad de datos de entrenamiento etiquetados y mucha potencia informática (cientos de horas de GPU o más). Solo tenemos unas tres mil fotos etiquetadas y queremos dedicar mucho menos tiempo, por lo que debemos ser más inteligentes.

Vamos a utilizar una técnica llamada transferencia de aprendizaje donde tomamos una red entrenada previamente (entrenado en aproximadamente un millón de imágenes generales), lo utilizan para extraer características, y formar una nueva capa en la parte superior de nuestra propia tarea de clasificar las imágenes de flores.

Configuración

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.7/site-packages/tensorflow/python/compat/v2_compat.py:111: 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

El conjunto de datos de flores

El conjunto de datos de flores consta de imágenes de flores con 5 posibles etiquetas de clase.

Cuando entrenamos un modelo de aprendizaje automático, dividimos nuestros datos en conjuntos de datos de prueba y entrenamiento. Entrenaremos el modelo con nuestros datos de entrenamiento y luego evaluaremos qué tan bien se desempeña el modelo con datos que nunca ha visto: el conjunto de prueba.

Descarguemos nuestros ejemplos de entrenamiento y prueba (puede llevar un tiempo) y dividímoslos en conjuntos de entrenamiento y de prueba.

Ejecute las siguientes dos celdas:

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 los datos

El conjunto de datos de flores consta de ejemplos que son imágenes etiquetadas de flores. Cada ejemplo contiene una imagen de flor JPEG y la etiqueta de clase: qué tipo de flor es. Muestremos algunas imágenes junto con sus etiquetas.

Muestra algunas imágenes etiquetadas

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

Construye el modelo

Vamos a cargar un TF-Hub módulo de función de la imagen del vector, apilar un clasificador lineal en él, y añadir de formación y evaluación de operaciones. La siguiente celda crea un gráfico TF que describe el modelo y su entrenamiento, pero no ejecuta el entrenamiento (ese será el siguiente paso).

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 /tmp/ipykernel_3995/2879154528.py: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 /tmp/ipykernel_3995/2879154528.py: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.7/site-packages/ipykernel_launcher.py:34: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/legacy_tf_layers/core.py:255: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  return layer.apply(inputs)

Entrena la red

Ahora que nuestro modelo está construido, entreneémoslo y veamos cómo funciona en nuestro conjunto de prueba.

# 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: 22.01%
Test accuracy at step 10: 52.04%
Test accuracy at step 20: 63.99%
Test accuracy at step 30: 69.97%
Test accuracy at step 40: 74.59%
Test accuracy at step 50: 75.00%
Test accuracy at step 60: 75.00%
Test accuracy at step 70: 78.26%
Test accuracy at step 80: 80.98%
Test accuracy at step 90: 79.21%
Test accuracy at step 99: 80.30%
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

Predicciones incorrectas

Echemos un vistazo más de cerca a los ejemplos de prueba en los que nuestro modelo se equivocó.

  • ¿Hay ejemplos mal etiquetados en nuestro conjunto de pruebas?
  • ¿Hay datos incorrectos en el conjunto de prueba, imágenes que en realidad no son imágenes de flores?
  • ¿Hay imágenes en las que puedas entender por qué el modelo se equivocó?
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

Ejercicios: ¡Mejora el modelo!

Hemos entrenado un modelo de línea de base, ahora intentemos mejorarlo para lograr una mayor precisión. (Recuerde que deberá volver a ejecutar las celdas cuando realice un cambio).

Ejercicio 1: pruebe con un modelo de imagen diferente.

Con TF-Hub, probar algunos modelos de imágenes diferentes es simple. Basta con sustituir el "https://tfhub.dev/google/imagenet/mobilenet_v2_050_128/feature_vector/2" mango en el hub.Module() llamada con un mango de módulo diferente y vuelva a ejecutar todo el código. Se puede ver todos los módulos de imagen disponibles en tfhub.dev .

Una buena elección podría ser uno de los otros módulos MobileNet V2 . Muchos de los módulos - incluyendo el MobileNet módulos - fueron entrenados en la IMAGEnet conjunto de datos que contiene más de 1 millón de imágenes y 1000 clases. La elección de una arquitectura de red ofrece una compensación entre velocidad y precisión de clasificación: modelos como MobileNet o NASNet Mobile son rápidos y pequeños, las arquitecturas más tradicionales como Inception y ResNet se diseñaron para brindar precisión.

Para la arquitectura de mayor Inception V3, también se puede explorar los beneficios de la formación previa en un dominio más cerca de su propia tarea: también está disponible como un módulo de formación sobre el conjunto de datos iNaturalist de plantas y animales.

Ejercicio 2: agregue una capa oculta.

Pila de una capa oculta entre las características de imagen extraídos y el clasificador lineal (en función create_model() más arriba). Para crear una capa oculta no lineal con, por ejemplo 100 nodos, el uso tf.layers.dense con unidades fijadas a 100 y el conjunto de activación para tf.nn.relu . ¿Cambiar el tamaño de la capa oculta afecta la precisión de la prueba? ¿Agregar una segunda capa oculta mejora la precisión?

Ejercicio 3: Cambiar hiperparámetros.

Hace cada vez mayor número de pasos de formación mejora la precisión final? Se puede cambiar la velocidad de aprendizaje para hacer su modelo convergen más rápidamente? ¿Tiene el tamaño de lote de entrenamiento afecta el rendimiento de su modelo?

Ejercicio 4: pruebe con un optimizador diferente.

Sustituir el GradientDescentOptimizer básico con un optimizador sofisticado más, por ejemplo AdagradOptimizer . ¿Hace alguna diferencia en el entrenamiento de tu modelo? Si desea obtener más información sobre los beneficios de los diferentes algoritmos de optimización, echa un vistazo a este post .

¿Querer aprender más?

Si está interesado en una versión más avanzada de este tutorial, echa un vistazo a la imagen TensorFlow reconversión tutorial que camina a través de la visualización de la formación utilizando TensorBoard, técnicas avanzadas como el conjunto de datos de aumento mediante la distorsión de las imágenes, y la sustitución de las flores conjunto de datos para aprender un clasificador de imagen en su propio conjunto de datos.

Usted puede aprender más sobre TensorFlow en tensorflow.org y ver la documentación de la API TF-Hub está disponible en tensorflow.org/hub . Encuentra módulos TensorFlow Hub disponibles en tfhub.dev incluyendo módulos del vector Más imágenes Características y módulos de texto de incrustación.

También puedes ver el Aprendizaje Curso acelerado de la máquina que es de ritmo rápido, introducción práctica de Google de aprendizaje automático.