ถ่ายทอดการเรียนรู้ด้วย TensorFlow Hub

ดูบน TensorFlow.org ทำงานใน Google Colab ดูบน 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

คุณจะเริ่มต้นด้วยการใช้แบบจำลองลักษณนามที่ได้รับการฝึกอบรมล่วงหน้าบนชุดข้อมูลเปรียบเทียบของ 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 ของล็อก ซึ่งให้คะแนนความน่าจะเป็นของแต่ละคลาสสำหรับรูปภาพ

ID ระดับบนสุดสามารถพบได้ด้วย tf.math.argmax :

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

ถอดรหัสคำทำนาย

นำ predicted_class ID (เช่น 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 เพื่อให้คุณสามารถส่งข้อมูลจากดิสก์โดยไม่มีปัญหาการบล็อก I/O

นี่คือวิธี 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)
ตัวยึดตำแหน่ง32

แนบหัวการจำแนกประเภท

ในการทำให้โมเดลสมบูรณ์ ให้ห่อเลเยอร์ตัวแยกคุณลักษณะในโมเดล 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']
ตัวยึดตำแหน่ง43

พล็อตการทำนายแบบจำลอง:

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'
ตัวยึดตำแหน่ง46

ยืนยันว่าคุณสามารถโหลด 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)
ตัวยึดตำแหน่ง51
['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 ในงานด้านรูปภาพ ข้อความ เสียง และวิดีโอ