在 TensorFlow.org 上查看 | 在 Google Colab 中运行 | 在 GitHub 上查看 | 下载笔记本 | 查看 TF Hub 模型 |
您是否曾经看到过一朵美丽的花,并想知道它是什么品种?您并非第一个有此疑问的人,所以让我们构建一种方法,通过照片识别花卉的类型!
对于图像分类,一种名为 *卷积神经网络* 的特定类型的 *深度神经网络* 被证明特别有效。但是,现代卷积神经网络具有数百万个参数。从头开始训练它们需要大量标记的训练数据和大量的计算能力(数百个 GPU 小时或更多)。我们只有大约 3000 张标记的照片,并且希望花费更少的时间,因此我们需要更聪明一些。
我们将使用一种名为 *迁移学习* 的技术,其中我们将使用一个预训练网络(在约一百万张通用图像上训练),使用它来提取特征,并在其之上训练一个新层,用于我们自己的花卉图像分类任务。
设置
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
花卉数据集
花卉数据集包含花卉图像,有 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))
探索数据
花卉数据集包含标记的花卉图像示例。每个示例包含一张 JPEG 花卉图像和类别标签:它是什么类型的花。让我们一起显示一些图像及其标签。
显示一些标记的图像
构建模型
我们将加载一个 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))
训练网络
现在我们的模型已经构建完毕,让我们训练它,看看它在测试集上的表现。
# 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)))
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)
错误预测
让我们仔细看看模型预测错误的测试示例。
- 测试集中是否存在任何错误标记的示例?
- 测试集中是否存在任何不良数据 - 不是花卉图片的图像?
- 是否存在一些图像,您可以理解模型为什么犯错?
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]])
练习:改进模型!
我们已经训练了一个基线模型,现在让我们尝试改进它,以获得更高的准确率。(请记住,在进行更改后,您需要重新运行单元格。)
练习 1:尝试不同的图像模型。
使用 TF-Hub,尝试一些不同的图像模型非常简单。只需将 "https://tfhub.dev/google/imagenet/mobilenet_v2_050_128/feature_vector/2"
句柄替换为 hub.Module()
调用中的不同模块句柄,然后重新运行所有代码。您可以在 tfhub.dev 上查看所有可用的图像模块。
一个不错的选择可能是其他 MobileNet V2 模块 之一。许多模块(包括 MobileNet 模块)都在 ImageNet 数据集 上训练,该数据集包含超过 100 万张图像和 1000 个类别。选择网络架构可以提供速度和分类准确率之间的权衡:像 MobileNet 或 NASNet Mobile 这样的模型速度快且体积小,而像 Inception 和 ResNet 这样的更传统架构是为准确率而设计的。
对于更大的 Inception V3 架构,您还可以探索在更接近您自身任务的领域进行预训练的好处:它也可以作为 在 iNaturalist 数据集上训练的模块,该数据集包含植物和动物。
练习 2:添加隐藏层。
在提取的图像特征和线性分类器之间堆叠一个隐藏层(在上面的 create_model()
函数中)。要创建具有例如 100 个节点的非线性隐藏层,请使用 tf.layers.dense,将 units 设置为 100,并将 activation 设置为 tf.nn.relu
。更改隐藏层的大小是否会影响测试精度?添加第二个隐藏层是否会提高精度?
练习 3:更改超参数。
增加训练步骤数量是否会提高最终精度?您是否可以更改学习率以使您的模型更快收敛?训练批次大小是否会影响您的模型性能?
练习 4:尝试不同的优化器。
用更复杂的优化器替换基本的 GradientDescentOptimizer,例如 AdagradOptimizer。这是否会对您的模型训练产生影响?如果您想了解更多关于不同优化算法的好处,请查看 这篇博文。
想要了解更多?
如果您对本教程的更高级版本感兴趣,请查看 TensorFlow 图像再训练教程,该教程将引导您使用 TensorBoard 可视化训练,以及数据增强等高级技术,例如通过扭曲图像来增强数据集,以及用您自己的数据集替换花卉数据集来学习图像分类器。
您可以在 tensorflow.org 上了解更多关于 TensorFlow 的信息,并在 tensorflow.org/hub 上查看 TF-Hub API 文档。在 tfhub.dev 上查找可用的 TensorFlow Hub 模块,包括更多图像特征向量模块和文本嵌入模块。
还可以查看 机器学习速成课程,这是 Google 对机器学习的快速、实用的介绍。