Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Classificare i fiori con Transfer Learning

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza su GitHub Scarica notebook Vedi modello TF Hub

Hai mai visto un bel fiore e ti sei chiesto che tipo di fiore sia? Beh, non sei il primo, quindi creiamo un modo per identificare il tipo di fiore da una foto!

Per classificare le immagini, un particolare tipo di rete neurale profonda , chiamata rete neurale convoluzionale, si è rivelato particolarmente potente. Tuttavia, le moderne reti neurali convoluzionali hanno milioni di parametri. L'addestramento da zero richiede molti dati di addestramento etichettati e molta potenza di calcolo (centinaia di ore GPU o più). Abbiamo solo circa tremila foto etichettate e vogliamo dedicare molto meno tempo, quindi dobbiamo essere più intelligenti.

Useremo una tecnica chiamata transfer learning in cui prendiamo una rete pre-addestrata (addestrata su circa un milione di immagini generali), la usiamo per estrarre le caratteristiche e addestriamo un nuovo livello sopra per il nostro compito di classificare le immagini dei fiori.

Impostare

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

Il set di dati dei fiori

Il dataset dei fiori è costituito da immagini di fiori con 5 possibili etichette di classe.

Durante l'addestramento di un modello di apprendimento automatico, suddividiamo i nostri dati in set di dati di addestramento e test. Addestreremo il modello sui nostri dati di addestramento e quindi valuteremo il rendimento del modello su dati che non ha mai visto: il set di test.

Scarichiamo la nostra formazione e gli esempi di test (potrebbe richiedere del tempo) e suddividiamoli in set di addestramento e test.

Esegui le seguenti due celle:

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

Esplora i dati

Il set di dati sui fiori è costituito da esempi etichettati come immagini di fiori. Ogni esempio contiene un'immagine di fiore JPEG e l'etichetta della classe: che tipo di fiore è. Visualizziamo alcune immagini insieme alle loro etichette.

Mostra alcune immagini etichettate

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

Costruisci il modello

Caricheremo un modulo vettoriale di funzionalità immagine TF-Hub , impileremo un classificatore lineare su di esso e aggiungeremo operazioni di formazione e valutazione. La cella seguente crea un grafico TF che descrive il modello e il suo addestramento, ma non esegue l'addestramento (quello sarà il passaggio successivo).

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

Warning:tensorflow:From <ipython-input-1-76baecbc8e0d>:34: dense (from tensorflow.python.keras.legacy_tf_layers.core) is deprecated and will be removed in a future version.
Instructions for updating:
Use keras.layers.Dense instead.

Warning:tensorflow:From <ipython-input-1-76baecbc8e0d>:34: dense (from tensorflow.python.keras.legacy_tf_layers.core) is deprecated and will be removed in a future version.
Instructions for updating:
Use keras.layers.Dense instead.

Warning:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/legacy_tf_layers/core.py:187: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.__call__` method instead.

Warning:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/legacy_tf_layers/core.py:187: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.__call__` method instead.

Allena la rete

Ora che il nostro modello è stato costruito, addestriamolo e vediamo come si comporta sul nostro set di test.

# 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: 21.06%
Test accuracy at step 10: 53.94%
Test accuracy at step 20: 66.03%
Test accuracy at step 30: 69.16%
Test accuracy at step 40: 73.64%
Test accuracy at step 50: 77.45%
Test accuracy at step 60: 79.35%
Test accuracy at step 70: 79.62%
Test accuracy at step 80: 80.16%
Test accuracy at step 90: 80.98%
Test accuracy at step 99: 79.21%

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

Previsioni errate

Diamo uno sguardo più da vicino agli esempi di test che il nostro modello ha sbagliato.

  • Ci sono esempi con etichette errate nel nostro set di test?
  • Ci sono dati errati nel set di prova - immagini che in realtà non sono immagini di fiori?
  • Ci sono immagini in cui puoi capire perché il modello ha commesso un errore?
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

Esercizi: migliora il modello!

Abbiamo addestrato un modello di base, ora proviamo a migliorarlo per ottenere una migliore precisione. (Ricorda che dovrai rieseguire le celle quando apporti una modifica.)

Esercizio 1: prova un modello di immagine diverso.

Con TF-Hub, provare alcuni modelli di immagine diversi è semplice. Basta sostituire l'handle "https://tfhub.dev/google/imagenet/mobilenet_v2_050_128/feature_vector/2" nella chiamata hub.Module() con un handle di tutti i diversi moduli e rerun. Puoi vedere tutti i moduli immagine disponibili su tfhub.dev .

Una buona scelta potrebbe essere uno degli altri moduli MobileNet V2 . Molti dei moduli, inclusi i moduli MobileNet, sono stati addestrati sul set di dati ImageNet che contiene oltre 1 milione di immagini e 1000 classi. La scelta di un'architettura di rete fornisce un compromesso tra velocità e precisione di classificazione: modelli come MobileNet o NASNet Mobile sono veloci e piccoli, le architetture più tradizionali come Inception e ResNet sono state progettate per la precisione.

Per l'architettura Inception V3 più ampia, puoi anche esplorare i vantaggi del pre-training su un dominio più vicino al tuo compito: è anche disponibile come modulo addestrato sul set di dati iNaturalist di piante e animali.

Esercizio 2: aggiungi un livello nascosto.

Impila un livello nascosto tra le caratteristiche dell'immagine estratta e il classificatore lineare (nella funzione create_model() sopra). Per creare uno strato nascosto non lineare con ad esempio 100 nodi, utilizzare tf.layers.dense con le unità impostate su 100 e l'attivazione impostata su tf.nn.relu . La modifica della dimensione dello strato nascosto influisce sulla precisione del test? L'aggiunta di un secondo livello nascosto migliora la precisione?

Esercizio 3: modifica degli iperparametri.

L'aumento del numero di passaggi di formazione migliora la precisione finale? Potete modificare il tasso di apprendimento per far convergere più rapidamente il vostro modello? La dimensione del batch di addestramento influisce sulle prestazioni del modello?

Esercizio 4: prova un ottimizzatore diverso.

Sostituisci GradientDescentOptimizer di base con un ottimizzatore più sofisticato, ad esempio AdagradOptimizer . Fa differenza per l'addestramento del tuo modello? Se vuoi saperne di più sui vantaggi di diversi algoritmi di ottimizzazione, dai un'occhiata a questo post .

Vuoi saperne di più?

Se sei interessato a una versione più avanzata di questo tutorial, dai un'occhiata al tutorial di riqualificazione di immagini di TensorFlow che ti guida attraverso la visualizzazione dell'addestramento utilizzando TensorBoard, tecniche avanzate come l'aumento del set di dati distorcendo le immagini e la sostituzione del set di dati dei fiori per imparare un classificatore di immagini su il tuo set di dati.

Puoi saperne di più su TensorFlow su tensorflow.org e vedere la documentazione dell'API TF-Hub disponibile su tensorflow.org/hub . Trova i moduli TensorFlow Hub disponibili su tfhub.dev, inclusi altri moduli vettoriali per le funzioni di immagine e moduli di incorporamento del testo.

Dai un'occhiata anche al Machine Learning Crash Course, che è l'introduzione pratica e veloce di Google al machine learning.