TensorFlow Hub是一种共享预训练模型组件的方法。
TensorFlow Hub是一个用于促进机器学习模型的可重用部分的发布,探索和使用的库。特别是,它提供经过预先训练的TensorFlow模型,可以在新任务中重复使用。(可以理解为做迁移学习:可以使用较小的数据集训练模型,可以改善泛化和加快训练。)GitHub 地址:https://github.com/tensorflow/hub
有关预先训练模型的可搜索列表,请参阅TensorFlow模块中心TensorFlow Module Hub。
本教程演示:
安装命令:pip install -U tensorflow_hub
x1from __future__ import absolute_import, division, print_function, unicode_literals
2
3import matplotlib.pylab as plt
4
5import tensorflow as tf
6
7import tensorflow_hub as hub
8
9from tensorflow.keras import layers
使用hub.module
加载mobilenet,并使用tf.keras.layers.Lambda
将其包装为keras层。
来自tfhub.dev的任何兼容tf2的图像分类器URL都可以在这里工作。
xxxxxxxxxx
71classifier_url ="https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/2" #@param {type:"string"}
2
3IMAGE_SHAPE = (224, 224)
4
5classifier = tf.keras.Sequential([
6 hub.KerasLayer(classifier_url, input_shape=IMAGE_SHAPE+(3,))
7])
下载单个图像以试用该模型。
xxxxxxxxxx
71import numpy as np
2import PIL.Image as Image
3
4grace_hopper = tf.keras.utils.get_file('image.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg')
5grace_hopper = Image.open(grace_hopper).resize(IMAGE_SHAPE)
6grace_hopper = np.array(grace_hopper)/255.0
7grace_hopper.shape
(224, 224, 3)
添加批量维度,并将图像传递给模型。
xxxxxxxxxx
21result = classifier.predict(grace_hopper[np.newaxis, ...])
2result.shape
结果是1001元素向量的logits
,对图像属于每个类的概率进行评级。因此,可以使用argmax
找到排在最前的类别ID:
xxxxxxxxxx
21predicted_class = np.argmax(result[0], axis=-1)
2predicted_class
xxxxxxxxxx
11653
我们有预测的类别ID,获取ImageNet
标签,并解码预测
xxxxxxxxxx
71labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
2imagenet_labels = np.array(open(labels_path).read().splitlines())
3
4plt.imshow(grace_hopper)
5plt.axis('off')
6predicted_class_name = imagenet_labels[predicted_class]
7_ = plt.title("Prediction: " + predicted_class_name.title())
使用TF Hub可以很容易地重新训练模型的顶层以识别数据集中的类。
对于此示例,您将使用TensorFlow鲜花数据集:
xxxxxxxxxx
31data_root = tf.keras.utils.get_file(
2 'flower_photos','https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
3 untar=True)
将此数据加载到我们的模型中的最简单方法是使用 tf.keras.preprocessing.image.ImageDataGenerator
,
所有TensorFlow Hub的图像模块都期望浮点输入在“[0,1]”范围内。使用ImageDataGenerator
的rescale
参数来实现这一目的。图像大小将在稍后处理。
xxxxxxxxxx
21image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1/255)
2image_data = image_generator.flow_from_directory(str(data_root), target_size=IMAGE_SHAPE)
xxxxxxxxxx
11Found 3670 images belonging to 5 classes.
结果对象是一个返回image_batch,label_batch
对的迭代器。
xxxxxxxxxx
41for image_batch, label_batch in image_data:
2 print("Image batch shape: ", image_batch.shape)
3 print("Labe batch shape: ", label_batch.shape)
4 break
xxxxxxxxxx
21Image batch shape: (32, 224, 224, 3)
2Labe batch shape: (32, 5)
现在在图像批处理上运行分类器。
xxxxxxxxxx
51result_batch = classifier.predict(image_batch)
2result_batch.shape # (32, 1001)
3
4predicted_class_names = imagenet_labels[np.argmax(result_batch, axis=-1)]
5predicted_class_names
xxxxxxxxxx
61array(['daisy', 'sea urchin', 'ant', 'hamper', 'daisy', 'ringlet',
2'daisy', 'daisy', 'daisy', 'cardoon', 'lycaenid', 'sleeping bag',
3'Bedlington terrier', 'daisy', 'daisy', 'picket fence',
4'coral fungus', 'daisy', 'zucchini', 'daisy', 'daisy', 'bee',
5'daisy', 'daisy', 'bee', 'daisy', 'picket fence', 'bell pepper',
6'daisy', 'pot', 'wolf spider', 'greenhouse'], dtype='<U30')
现在检查这些预测如何与图像对齐:
xxxxxxxxxx
81plt.figure(figsize=(10,9))
2plt.subplots_adjust(hspace=0.5)
3for n in range(30):
4 plt.subplot(6,5,n+1)
5 plt.imshow(image_batch[n])
6 plt.title(predicted_class_names[n])
7 plt.axis('off')
8_ = plt.suptitle("ImageNet predictions")
有关图像属性,请参阅LICENSE.txt
文件。
结果没有那么完美,但考虑到这些不是模型训练的类(“daisy雏菊”除外),这是合理的。
TensorFlow Hub还可以在没有顶级分类层的情况下分发模型。这些可以用来轻松做迁移学习。
来自tfhub.dev的任何Tensorflow 2兼容图像特征向量URL都可以在此处使用。
xxxxxxxxxx
11feature_extractor_url = "https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/2" #@param {type:"string"}
创建特征提取器。
xxxxxxxxxx
21feature_extractor_layer = hub.KerasLayer(feature_extractor_url,
2 input_shape=(224,224,3))
它为每个图像返回一个1280长度的向量:
xxxxxxxxxx
21feature_batch = feature_extractor_layer(image_batch)
2print(feature_batch.shape)
(32, 1280)
冻结特征提取器层中的变量,以便训练仅修改新的分类器层。
xxxxxxxxxx
11feature_extractor_layer.trainable = False
现在将中心层包装在tf.keras.Sequential
模型中,并添加新的分类层。
xxxxxxxxxx
61model = tf.keras.Sequential([
2 feature_extractor_layer,
3 layers.Dense(image_data.num_classes, activation='softmax')
4])
5
6model.summary()
xxxxxxxxxx
121Model: "sequential_1"
2_________________________________________________________________
3Layer (type) Output Shape Param #
4=================================================================
5keras_layer_1 (KerasLayer) (None, 1280) 2257984
6_________________________________________________________________
7dense (Dense) (None, 5) 6405
8=================================================================
9Total params: 2,264,389
10Trainable params: 6,405
11Non-trainable params: 2,257,984
12_________________________________________________________________
xxxxxxxxxx
21predictions = model(image_batch)
2predictions.shape
xxxxxxxxxx
11TensorShape([32, 5])
使用compile配置训练过程:
xxxxxxxxxx
41model.compile(
2 optimizer=tf.keras.optimizers.Adam(),
3 loss='categorical_crossentropy',
4 metrics=['acc'])
现在使用.fit
方法训练模型。
这个例子只是训练两个周期。要显示训练进度,请使用自定义回调单独记录每个批次的损失和准确性,而不是记录周期的平均值。
xxxxxxxxxx
171class CollectBatchStats(tf.keras.callbacks.Callback):
2 def __init__(self):
3 self.batch_losses = []
4 self.batch_acc = []
5
6 def on_train_batch_end(self, batch, logs=None):
7 self.batch_losses.append(logs['loss'])
8 self.batch_acc.append(logs['acc'])
9 self.model.reset_metrics()
10
11steps_per_epoch = np.ceil(image_data.samples/image_data.batch_size)
12
13batch_stats_callback = CollectBatchStats()
14
15history = model.fit(image_data, epochs=2,
16 steps_per_epoch=steps_per_epoch,
17 callbacks = [batch_stats_callback])
xxxxxxxxxx
41Epoch 1/2
2115/115 [==============================] - 22s 193ms/step - loss: 0.8613 - acc: 0.8438
3Epoch 2/2
4115/115 [==============================] - 23s 199ms/step - loss: 0.5083 - acc: 0.7812
现在,即使只是几次训练迭代,我们已经可以看到模型正在完成任务。
xxxxxxxxxx
51plt.figure()
2plt.ylabel("Loss")
3plt.xlabel("Training Steps")
4plt.ylim([0,2])
5plt.plot(batch_stats_callback.batch_losses)
xxxxxxxxxx
51plt.figure()
2plt.ylabel("Accuracy")
3plt.xlabel("Training Steps")
4plt.ylim([0,1])
5plt.plot(batch_stats_callback.batch_acc)
要重做之前的图,首先获取有序的类名列表:
xxxxxxxxxx
31class_names = sorted(image_data.class_indices.items(), key=lambda pair:pair[1])
2class_names = np.array([key.title() for key, value in class_names])
3class_names
xxxxxxxxxx
21array(['Daisy', 'Dandelion', 'Roses', 'Sunflowers', 'Tulips'],
2dtype='<U10')
通过模型运行图像批处理,并将索引转换为类名。
xxxxxxxxxx
31predicted_batch = model.predict(image_batch)
2predicted_id = np.argmax(predicted_batch, axis=-1)
3predicted_label_batch = class_names[predicted_id]
绘制结果
xxxxxxxxxx
111label_id = np.argmax(label_batch, axis=-1)
2
3plt.figure(figsize=(10,9))
4plt.subplots_adjust(hspace=0.5)
5for n in range(30):
6 plt.subplot(6,5,n+1)
7 plt.imshow(image_batch[n])
8 color = "green" if predicted_id[n] == label_id[n] else "red"
9 plt.title(predicted_label_batch[n].title(), color=color)
10 plt.axis('off')
11_ = plt.suptitle("Model predictions (green: correct, red: incorrect)")
现在您已经训练了模型,将其导出为已保存的模型:
xxxxxxxxxx
71import time
2t = time.time()
3
4export_path = "/tmp/saved_models/{}".format(int(t))
5tf.keras.experimental.export_saved_model(model, export_path)
6
7export_path
xxxxxxxxxx
11'/tmp/saved_models/1557794138'
现在确认我们可以重新加载它,它仍然给出相同的结果:
xxxxxxxxxx
61reloaded = tf.keras.experimental.load_from_saved_model(export_path, custom_objects={'KerasLayer':hub.KerasLayer})
2
3result_batch = model.predict(image_batch)
4reloaded_result_batch = reloaded.predict(image_batch)
5
6abs(reloaded_result_batch - result_batch).max()
0.0
这个保存的模型可以在以后加载推理,或转换为TFLite 和 TFjs。
最新版本:https://www.mashangxue123.com/tensorflow/tf2-tutorials-images-hub_with_keras.html
英文版本:https://tensorflow.google.cn/beta/tutorials/images/hub_with_keras
翻译建议PR:https://github.com/mashangxue/tensorflow2-zh/edit/master/r2/tutorials/images/hub_with_keras.md