Перенос обучения с помощью TensorFlow Hub

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть на GitHub Скачать блокнот См. модель концентратора TF

TensorFlow Hub — это хранилище предварительно обученных моделей TensorFlow.

В этом руководстве показано, как:

  1. Используйте модели из TensorFlow Hub с tf.keras .
  2. Используйте модель классификации изображений от TensorFlow Hub.
  3. Выполните простое обучение переносу, чтобы точно настроить модель для ваших собственных классов изображений.

Настраивать

import numpy as np
import time

import PIL.Image as Image
import matplotlib.pylab as plt

import tensorflow as tf
import tensorflow_hub as hub

import datetime

%load_ext tensorboard

Классификатор ImageNet

Вы начнете с использования модели классификатора, предварительно обученной на наборе эталонных данных ImageNet — начальное обучение не требуется!

Скачать классификатор

Выберите предварительно обученную модель MobileNetV2 из TensorFlow Hub и оберните ее как слой Keras с помощью hub.KerasLayer . Здесь будет работать любая совместимая модель классификатора изображений из TensorFlow Hub, включая примеры, представленные в раскрывающемся списке ниже.

mobilenet_v2 ="https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/4"
inception_v3 = "https://tfhub.dev/google/imagenet/inception_v3/classification/5"

classifier_model = mobilenet_v2
IMAGE_SHAPE = (224, 224)

classifier = tf.keras.Sequential([
    hub.KerasLayer(classifier_model, input_shape=IMAGE_SHAPE+(3,))
])

Запустите его на одном изображении

Загрузите одно изображение, чтобы примерить модель:

grace_hopper = tf.keras.utils.get_file('image.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg')
grace_hopper = Image.open(grace_hopper).resize(IMAGE_SHAPE)
grace_hopper
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg
65536/61306 [================================] - 0s 0us/step
73728/61306 [====================================] - 0s 0us/step

png

grace_hopper = np.array(grace_hopper)/255.0
grace_hopper.shape
(224, 224, 3)

Добавьте пакетное измерение (с помощью np.newaxis ) и передайте изображение в модель:

result = classifier.predict(grace_hopper[np.newaxis, ...])
result.shape
(1, 1001)

Результатом является вектор логитов из 1001 элемента, оценивающий вероятность каждого класса для изображения.

Идентификатор верхнего класса можно найти с помощью tf.math.argmax :

predicted_class = tf.math.argmax(result[0], axis=-1)
predicted_class
<tf.Tensor: shape=(), dtype=int64, numpy=653>

Расшифруйте предсказания

Возьмите идентификатор predicted_class (например, 653 ) и выберите метки набора данных ImageNet, чтобы расшифровать предсказания:

labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt
16384/10484 [==============================================] - 0s 0us/step
24576/10484 [======================================================================] - 0s 0us/step
plt.imshow(grace_hopper)
plt.axis('off')
predicted_class_name = imagenet_labels[predicted_class]
_ = plt.title("Prediction: " + predicted_class_name.title())

png

Простое трансферное обучение

Но что, если вы хотите создать собственный классификатор, используя свой собственный набор данных, в котором есть классы, не включенные в исходный набор данных ImageNet (на котором обучалась предварительно обученная модель)?

Для этого вы можете:

  1. Выберите предварительно обученную модель в TensorFlow Hub; и
  2. Переобучите верхний (последний) слой, чтобы он распознавал классы из вашего пользовательского набора данных.

Набор данных

В этом примере вы будете использовать набор данных цветов TensorFlow:

data_root = tf.keras.utils.get_file(
  'flower_photos',
  'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
   untar=True)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 7s 0us/step
228827136/228813984 [==============================] - 7s 0us/step

Сначала загрузите эти данные в модель, используя данные изображения с диска с помощью tf.keras.utils.image_dataset_from_directory , который сгенерирует tf.data.Dataset :

batch_size = 32
img_height = 224
img_width = 224

train_ds = tf.keras.utils.image_dataset_from_directory(
  str(data_root),
  validation_split=0.2,
  subset="training",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size
)

val_ds = tf.keras.utils.image_dataset_from_directory(
  str(data_root),
  validation_split=0.2,
  subset="validation",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size
)
Found 3670 files belonging to 5 classes.
Using 2936 files for training.
Found 3670 files belonging to 5 classes.
Using 734 files for validation.

Набор данных цветов имеет пять классов:

class_names = np.array(train_ds.class_names)
print(class_names)
['daisy' 'dandelion' 'roses' 'sunflowers' 'tulips']

Во-вторых, поскольку соглашение TensorFlow Hub для моделей изображений предполагает входные данные с плавающей запятой в диапазоне [0, 1] , для достижения этого используйте слой предварительной обработки tf.keras.layers.Rescaling .

normalization_layer = tf.keras.layers.Rescaling(1./255)
train_ds = train_ds.map(lambda x, y: (normalization_layer(x), y)) # Where x—images, y—labels.
val_ds = val_ds.map(lambda x, y: (normalization_layer(x), y)) # Where x—images, y—labels.

В-третьих, завершите входной конвейер, используя буферизованную предварительную выборку с Dataset.prefetch , чтобы вы могли получать данные с диска без проблем с блокировкой ввода-вывода.

Это некоторые из наиболее важных методов tf.data , которые вы должны использовать при загрузке данных. Заинтересованные читатели могут узнать больше о них, а также о том, как кэшировать данные на диск и о других методах, в руководстве по повышению производительности с помощью API tf.data .

AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
for image_batch, labels_batch in train_ds:
  print(image_batch.shape)
  print(labels_batch.shape)
  break
(32, 224, 224, 3)
(32,)
2022-01-26 05:06:19.465331: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

Запустите классификатор на пакете изображений

Теперь запустите классификатор для пакета изображений:

result_batch = classifier.predict(train_ds)
predicted_class_names = imagenet_labels[tf.math.argmax(result_batch, axis=-1)]
predicted_class_names
array(['daisy', 'coral fungus', 'rapeseed', ..., 'daisy', 'daisy',
       'birdhouse'], dtype='<U30')

Проверьте, как эти предсказания совпадают с изображениями:

plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
  plt.subplot(6,5,n+1)
  plt.imshow(image_batch[n])
  plt.title(predicted_class_names[n])
  plt.axis('off')
_ = plt.suptitle("ImageNet predictions")

png

Результаты далеки от совершенства, но разумны, учитывая, что это не те классы, для которых была обучена модель (за исключением «маргаритки»).

Скачать безголовую модель

TensorFlow Hub также распространяет модели без верхнего слоя классификации. Их можно использовать для простого выполнения трансферного обучения.

Выберите предварительно обученную модель MobileNetV2 в TensorFlow Hub . Здесь будет работать любая совместимая векторная модель признаков изображения из TensorFlow Hub, включая примеры из раскрывающегося меню.

mobilenet_v2 = "https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4"
inception_v3 = "https://tfhub.dev/google/tf2-preview/inception_v3/feature_vector/4"

feature_extractor_model = mobilenet_v2

Создайте экстрактор функций, обернув предварительно обученную модель как слой Keras с помощью hub.KerasLayer . Используйте аргумент trainable=False , чтобы заморозить переменные, чтобы обучение модифицировало только новый слой классификатора:

feature_extractor_layer = hub.KerasLayer(
    feature_extractor_model,
    input_shape=(224, 224, 3),
    trainable=False)

Экстрактор функций возвращает вектор длиной 1280 для каждого изображения (в этом примере размер пакета изображений остается равным 32):

feature_batch = feature_extractor_layer(image_batch)
print(feature_batch.shape)
(32, 1280)

Прикрепите заголовок классификации

Чтобы завершить модель, оберните слой извлечения признаков в модель tf.keras.Sequential и добавьте полносвязный слой для классификации:

num_classes = len(class_names)

model = tf.keras.Sequential([
  feature_extractor_layer,
  tf.keras.layers.Dense(num_classes)
])

model.summary()
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 keras_layer_1 (KerasLayer)  (None, 1280)              2257984   
                                                                 
 dense (Dense)               (None, 5)                 6405      
                                                                 
=================================================================
Total params: 2,264,389
Trainable params: 6,405
Non-trainable params: 2,257,984
_________________________________________________________________
predictions = model(image_batch)
predictions.shape
TensorShape([32, 5])

Обучите модель

Используйте Model.compile для настройки процесса обучения и добавьте обратный вызов tf.keras.callbacks.TensorBoard для создания и хранения журналов:

model.compile(
  optimizer=tf.keras.optimizers.Adam(),
  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
  metrics=['acc'])

log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = tf.keras.callbacks.TensorBoard(
    log_dir=log_dir,
    histogram_freq=1) # Enable histogram computation for every epoch.

Теперь используйте метод Model.fit для обучения модели.

Чтобы этот пример был кратким, вы будете тренироваться всего 10 эпох. Чтобы позже визуализировать ход обучения в TensorBoard, создайте и сохраните журналы обратного вызова TensorBoard .

NUM_EPOCHS = 10

history = model.fit(train_ds,
                    validation_data=val_ds,
                    epochs=NUM_EPOCHS,
                    callbacks=tensorboard_callback)
Epoch 1/10
92/92 [==============================] - 7s 42ms/step - loss: 0.7904 - acc: 0.7210 - val_loss: 0.4592 - val_acc: 0.8515
Epoch 2/10
92/92 [==============================] - 3s 33ms/step - loss: 0.3850 - acc: 0.8713 - val_loss: 0.3694 - val_acc: 0.8787
Epoch 3/10
92/92 [==============================] - 3s 33ms/step - loss: 0.3027 - acc: 0.9057 - val_loss: 0.3367 - val_acc: 0.8856
Epoch 4/10
92/92 [==============================] - 3s 33ms/step - loss: 0.2524 - acc: 0.9237 - val_loss: 0.3210 - val_acc: 0.8869
Epoch 5/10
92/92 [==============================] - 3s 33ms/step - loss: 0.2164 - acc: 0.9373 - val_loss: 0.3124 - val_acc: 0.8896
Epoch 6/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1888 - acc: 0.9469 - val_loss: 0.3070 - val_acc: 0.8937
Epoch 7/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1668 - acc: 0.9550 - val_loss: 0.3032 - val_acc: 0.9005
Epoch 8/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1487 - acc: 0.9619 - val_loss: 0.3004 - val_acc: 0.9005
Epoch 9/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1335 - acc: 0.9687 - val_loss: 0.2981 - val_acc: 0.9019
Epoch 10/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1206 - acc: 0.9748 - val_loss: 0.2964 - val_acc: 0.9046

Запустите TensorBoard, чтобы просмотреть, как показатели меняются с каждой эпохой, и отслеживать другие скалярные значения:

%tensorboard --logdir logs/fit

Проверьте прогнозы

Получите упорядоченный список имен классов из прогнозов модели:

predicted_batch = model.predict(image_batch)
predicted_id = tf.math.argmax(predicted_batch, axis=-1)
predicted_label_batch = class_names[predicted_id]
print(predicted_label_batch)
['roses' 'dandelion' 'tulips' 'sunflowers' 'dandelion' 'roses' 'dandelion'
 'roses' 'tulips' 'dandelion' 'tulips' 'tulips' 'sunflowers' 'tulips'
 'dandelion' 'roses' 'daisy' 'tulips' 'dandelion' 'dandelion' 'dandelion'
 'tulips' 'sunflowers' 'roses' 'sunflowers' 'dandelion' 'tulips' 'roses'
 'roses' 'sunflowers' 'tulips' 'sunflowers']

Постройте прогнозы модели:

plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)

for n in range(30):
  plt.subplot(6,5,n+1)
  plt.imshow(image_batch[n])
  plt.title(predicted_label_batch[n].title())
  plt.axis('off')
_ = plt.suptitle("Model predictions")

png

Экспортируйте и перезагрузите модель

Теперь, когда вы обучили модель, экспортируйте ее как SavedModel для последующего повторного использования.

t = time.time()

export_path = "/tmp/saved_models/{}".format(int(t))
model.save(export_path)

export_path
2022-01-26 05:07:03.429901: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/saved_models/1643173621/assets
INFO:tensorflow:Assets written to: /tmp/saved_models/1643173621/assets
'/tmp/saved_models/1643173621'

Подтвердите, что вы можете перезагрузить SavedModel и что модель может выводить те же результаты:

reloaded = tf.keras.models.load_model(export_path)
result_batch = model.predict(image_batch)
reloaded_result_batch = reloaded.predict(image_batch)
abs(reloaded_result_batch - result_batch).max()
0.0
reloaded_predicted_id = tf.math.argmax(reloaded_result_batch, axis=-1)
reloaded_predicted_label_batch = class_names[reloaded_predicted_id]
print(reloaded_predicted_label_batch)
['roses' 'dandelion' 'tulips' 'sunflowers' 'dandelion' 'roses' 'dandelion'
 'roses' 'tulips' 'dandelion' 'tulips' 'tulips' 'sunflowers' 'tulips'
 'dandelion' 'roses' 'daisy' 'tulips' 'dandelion' 'dandelion' 'dandelion'
 'tulips' 'sunflowers' 'roses' 'sunflowers' 'dandelion' 'tulips' 'roses'
 'roses' 'sunflowers' 'tulips' 'sunflowers']
plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
  plt.subplot(6,5,n+1)
  plt.imshow(image_batch[n])
  plt.title(reloaded_predicted_label_batch[n].title())
  plt.axis('off')
_ = plt.suptitle("Model predictions")

png

Следующие шаги

Вы можете использовать SavedModel для загрузки для логического вывода или преобразовать ее в модель TensorFlow Lite (для машинного обучения на устройстве) или в модель TensorFlow.js (для машинного обучения в JavaScript).

Откройте для себя дополнительные руководства , чтобы узнать, как использовать предварительно обученные модели из TensorFlow Hub для задач с изображениями, текстом, аудио и видео.