העבר למידה עם TensorFlow Hub

הצג באתר TensorFlow.org הפעל בגוגל קולאב הצג ב-GitHub הורד מחברת ראה דגם TF Hub

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

תתחיל בשימוש במודל מסווג שהוכשר מראש במערך הנתונים של ה-benchmark של 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 flowers:

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 החשובות ביותר שבהן עליך להשתמש בעת טעינת נתונים. קוראים מתעניינים יכולים ללמוד עוד עליהם, כמו גם כיצד לשמר נתונים בדיסק וטכניקות אחרות, במדריך ביצועים טובים יותר עם tf.data API .

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

ייצא וטען מחדש את הדגם שלך

כעת לאחר שאימנת את הדגם, ייצא אותו כ-SaveModel לשימוש חוזר מאוחר יותר.

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 במשימות תמונה, טקסט, אודיו ווידאו.