จำแนกดอกไม้ด้วยการถ่ายโอนการเรียนรู้ Transfer

เคยเห็นดอกไม้สวยๆ แล้วสงสัยว่าดอกอะไร? คุณไม่ใช่คนแรก เรามาสร้างวิธีการระบุประเภทของดอกไม้จากภาพถ่ายกันเถอะ!

สำหรับการแบ่งประเภทของภาพประเภทเฉพาะของเครือข่ายประสาทลึกที่เรียกว่าเครือข่ายประสาทสับสนได้พิสูจน์แล้วว่ามีประสิทธิภาพโดยเฉพาะอย่างยิ่ง อย่างไรก็ตาม โครงข่ายประสาทเทียมแบบโค้งสมัยใหม่มีพารามิเตอร์หลายล้านตัว การฝึกอบรมตั้งแต่เริ่มต้นต้องใช้ข้อมูลการฝึกที่มีป้ายกำกับจำนวนมากและกำลังประมวลผลจำนวนมาก (หลายร้อย GPU ชั่วโมงขึ้นไป) เรามีรูปถ่ายติดป้ายกำกับเพียงสามพันรูปและต้องการใช้เวลาน้อยลงมาก ดังนั้นเราจึงต้องฉลาดขึ้น

เราจะใช้เทคนิคที่เรียกว่าการถ่ายโอนการเรียนรู้ที่เราจะใช้เครือข่ายก่อนการฝึกอบรม (ผ่านการฝึกอบรมเกี่ยวกับล้านภาพทั่วไป) ใช้ในการดึงคุณสมบัติและการฝึกอบรมชั้นใหม่ด้านบนสำหรับงานของเราเองของการจำแนกภาพของดอกไม้

ติดตั้ง

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

ชุดข้อมูลดอกไม้

ชุดข้อมูลดอกไม้ประกอบด้วยรูปภาพดอกไม้ที่มีป้ายกำกับคลาสที่เป็นไปได้ 5 รายการ

เมื่อฝึกโมเดลแมชชีนเลิร์นนิง เราแบ่งข้อมูลออกเป็นชุดข้อมูลการฝึกอบรมและทดสอบ เราจะฝึกโมเดลด้วยข้อมูลการฝึกของเรา จากนั้นจึงประเมินว่าโมเดลทำงานได้ดีเพียงใดกับข้อมูลที่ไม่เคยเห็นมาก่อน - ชุดทดสอบ

มาดาวน์โหลดตัวอย่างการฝึกอบรมและการทดสอบของเรา (อาจใช้เวลาสักครู่) แล้วแยกออกเป็นชุดฝึกและชุดทดสอบ

เรียกใช้สองเซลล์ต่อไปนี้:

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

สำรวจข้อมูล

ชุดข้อมูลดอกไม้ประกอบด้วยตัวอย่างที่มีป้ายกำกับรูปภาพของดอกไม้ แต่ละตัวอย่างมีภาพดอกไม้ JPEG และป้ายกำกับชั้นเรียน: ดอกไม้ประเภทใด มาแสดงภาพสองสามภาพพร้อมกับป้ายกำกับกัน

แสดงรูปภาพที่มีป้ายกำกับ

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

สร้างแบบจำลอง

เราจะโหลด TF-Hub โมดูลคุณลักษณะภาพเวกเตอร์, สแต็คลักษณนามเชิงเส้นที่มันและเพิ่มการฝึกอบรมและการประเมินผลการปฏิบัติการ เซลล์ต่อไปนี้สร้างกราฟ TF ที่อธิบายแบบจำลองและการฝึก แต่จะไม่เรียกใช้การฝึก (ซึ่งจะเป็นขั้นตอนต่อไป)

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)

อบรมเครือข่าย

เมื่อโมเดลของเราถูกสร้างขึ้นแล้ว มาฝึกกันและดูว่ามันทำงานอย่างไรในชุดทดสอบของเรา

# 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

การคาดคะเนไม่ถูกต้อง

มาดูตัวอย่างการทดสอบที่แบบจำลองของเราผิดพลาดอย่างละเอียดยิ่งขึ้น

  • มีตัวอย่างที่ติดฉลากผิดในชุดทดสอบของเราหรือไม่?
  • มีข้อมูลที่ไม่ดีในชุดทดสอบหรือไม่ - รูปภาพที่ไม่ใช่รูปภาพของดอกไม้จริงๆ
  • มีภาพที่คุณสามารถเข้าใจได้ว่าทำไมโมเดลถึงผิดพลาด?
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

แบบฝึกหัด: ปรับปรุงโมเดล!

เราได้ฝึกโมเดลพื้นฐานแล้ว ตอนนี้เรามาลองปรับปรุงกันเพื่อให้ได้ความแม่นยำที่ดีขึ้น (จำไว้ว่าคุณจะต้องเรียกใช้เซลล์อีกครั้งเมื่อคุณทำการเปลี่ยนแปลง)

แบบฝึกหัดที่ 1: ลองใช้โมเดลรูปภาพอื่น

ด้วย TF-Hub การลองใช้โมเดลรูปภาพที่แตกต่างกันสองสามแบบเป็นเรื่องง่าย เพียงแค่เปลี่ยน "https://tfhub.dev/google/imagenet/mobilenet_v2_050_128/feature_vector/2" จับใน hub.Module() โทรที่มีที่จับของโมดูลที่แตกต่างกันและรันโค้ดทั้งหมด ท่านสามารถเข้าดูโมดูลภาพทั้งหมดที่มี tfhub.dev

ทางเลือกที่ดีอาจจะเป็นหนึ่งของอื่น ๆ MobileNet V2 โมดูล หลายโมดูล - รวมทั้ง MobileNet โมดูล - ได้รับการฝึกฝนใน ImageNet ชุดข้อมูล ที่มีกว่า 1 ล้านภาพและ 1000 ชั้นเรียน การเลือกสถาปัตยกรรมเครือข่ายทำให้เกิดข้อแลกเปลี่ยนระหว่างความเร็วและความแม่นยำในการจำแนกประเภท: โมเดลต่างๆ เช่น MobileNet หรือ NASNet Mobile นั้นมีขนาดเล็กและรวดเร็ว สถาปัตยกรรมดั้งเดิมอย่าง Inception และ ResNet ได้รับการออกแบบมาเพื่อความแม่นยำ

สำหรับสถาปัตยกรรมขนาดใหญ่จัดตั้งกองทุน V3 คุณยังสามารถสำรวจประโยชน์ของการก่อนการฝึกอบรมในโดเมนใกล้ชิดกับงานของคุณเองก็ยังสามารถใช้ได้เป็น โมดูลการฝึกอบรมในชุดข้อมูลที่ iNaturalist ของพืชและสัตว์

แบบฝึกหัดที่ 2: เพิ่มเลเยอร์ที่ซ่อนอยู่

สแต็คชั้นที่ซ่อนอยู่ระหว่างคุณลักษณะภาพสกัดและลักษณนามเชิงเส้น (ในการทำงาน create_model() ด้านบน) ในการสร้างที่ไม่ใช่เชิงเส้นชั้นที่ซ่อนอยู่ด้วยเช่น 100 โหนดใช้ tf.layers.dense กับหน่วยตั้ง 100 ชุดและเปิดใช้งานเพื่อ tf.nn.relu การเปลี่ยนขนาดของเลเยอร์ที่ซ่อนอยู่ส่งผลต่อความแม่นยำในการทดสอบหรือไม่? การเพิ่มเลเยอร์ที่ซ่อนอยู่ที่สองช่วยเพิ่มความแม่นยำหรือไม่?

แบบฝึกหัดที่ 3: เปลี่ยนไฮเปอร์พารามิเตอร์

ไม่เพิ่มจำนวนของขั้นตอนการฝึกอบรมเพิ่มความถูกต้องสุดท้าย? คุณสามารถเปลี่ยนอัตราการเรียนรู้ที่จะทำให้บรรจบรูปแบบของคุณได้อย่างรวดเร็ว? ไม่ขนาดชุดฝึกอบรมส่งผลกระทบต่อประสิทธิภาพการทำงานรูปแบบของคุณหรือไม่

แบบฝึกหัดที่ 4: ลองใช้เครื่องมือเพิ่มประสิทธิภาพอื่น

แทนที่ GradientDescentOptimizer ขั้นพื้นฐานที่มีการเพิ่มประสิทธิภาพตบตามากขึ้นเช่น AdagradOptimizer มันสร้างความแตกต่างให้กับการฝึกโมเดลของคุณหรือไม่? หากคุณต้องการที่จะเรียนรู้เพิ่มเติมเกี่ยวกับประโยชน์ของขั้นตอนวิธีการเพิ่มประสิทธิภาพที่แตกต่างกันให้ตรวจสอบ การโพสต์นี้

ต้องการเรียนรู้เพิ่มเติมหรือไม่?

หากคุณมีความสนใจในรุ่นที่สูงขึ้นของการกวดวิชานี้ให้ตรวจสอบ ภาพ TensorFlow ฝึกอบรมกวดวิชา ที่คุณเดินผ่านการแสดงการฝึกอบรมโดยใช้ TensorBoard เทคนิคขั้นสูงเช่นการเสริมชุดข้อมูลโดยบิดเบือนภาพและการเปลี่ยนดอกไม้ชุดข้อมูลที่จะเรียนรู้ลักษณนามภาพ ชุดข้อมูลของคุณเอง

คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับ TensorFlow ที่ tensorflow.org และดูเอกสาร TF-Hub API ที่มีอยู่ใน tensorflow.org/hub ค้นหาใช้ได้โมดูล TensorFlow Hub ณ tfhub.dev รวมถึงคุณลักษณะภาพโมดูลเวกเตอร์และโมดูลข้อความฝัง

ยังตรวจสอบ เครื่องการเรียนรู้หลักสูตรความผิดพลาด ซึ่งเป็นอย่างรวดเร็ว, การแนะนำการปฏิบัติของ Google ในการเรียนรู้ของเครื่อง