Deep Learning với Tensorflow Module 6 phần 1: Transfer learning khai thác tính năng

MVT
Đang cập nhật

Trong module trước, chúng ta đã xây dựng mô hình CNN cho 2 class và nhiều class. Mỗi mô hình đều có khả năng học dữ liệu train khác nhau, nhưng nhìn chung khả năng học chưa tốt, vẫn cần phải cải thiện rất nhiều. Để cải thiện mô hình, có rất nhiều cách khác nhau mà chúng ta đã thử ở cuối module trước. Trong số những cách đó có đề cập đến transfer learning nhưng vẫn chưa được sử dụng.

Trong module này, chúng ta sẽ tập trung việc xây dựng và cải thiện mô hình với transfer learning để xem liệu nó có đem lại một điều gì đó tốt hơn cho mô hình dự đoán hay không.

Nội dung phần 1 bao gồm :

  1. Giới thiệu về Transfer learning
  2. Giới thiệu các hàm callback để giám sát mô hình train
  3. Sử dụng tập dữ liệu nhỏ để thử nghiệm nhanh khả năng học của mô hình
    • 3.1 Xây dựng mô hình dựa trên RestnetV50
    • 3.2 Xây dựng mô hình dựa trên EfficientNetB0
    • 3.3 Xây dựng mô hình dựa trên ImageNet
  4. Sử dụng tensorboard để so sánh kết quả từ các mô hình
    • 4.1 Upload các thử nghiệm lên TensorBoard
    • 4.2 Liệt kê các thử nghiệm đã được lưu trên Tensorboard
    • 4.3 Xóa thử nghiệm trong tensorboard

🔑Lưu ý : Với những kiểu dữ liệu hình ảnh hoặc dữ liệu có cấu trúc phức tạ như âm thanh, giọng nói thì chúng ta nên sử dụng GPU thay vì CPU để cải thiện tốc độ xử lý cho mô hình.

Trước tiên kiểm tra xem notebook có sử dụng GPU chưa

!nvidia-smi -L

GPU 0: Tesla K80 (UUID: GPU-a5436447-c0aa-234f-06b6-4012ea6f8efe)

Nếu thông báo NVIDIA-SMI has failed .... thì GPU không hoạt động, còn nếu có tên loại GPU như GPU 0: Tesla [K80, T4, P4] thì nó đang hoạt động.

1. Giới thiệu về Transfer learning

📖 Định nghĩa Transfer learning : là một kỹ thuật vận dụng những kỹ năng, tri thức (cũng được gọi là weights) của mô hình cho ứng dụng này để áp dụng xử lý những vấn đề cho một ứng khác. VD: Bạn học tốt môn toán, xác suất, thống kê thì có thể lấy kiến thức đó để giải quyết cho vấn đề phân tích dữ liệu, cho ngành học máy.

Có 2 ưu điểm lợi khi sử transfer learning:

  • Có thể tận dụng lại kiến trúc CNN đã được xây dựng trước đó để giải quyết các vấn đề tương tự trong ứng dụng đang xây dựng
  • Có thể tận dung lại kiến trúc CNN đã được học những kỹ nắng, tri thức trên tập dữ liệu tương tự như tập dữ liệu của chúng ta. Điều này thường đem lại kết quả tốt cho mô hình học với dữ liệu chúng ta đưa vào không cần quá lớn.

Điều này có nghĩa là thay vì phải tự tay xây dựng mô hình CNN ngay từ đầu, chúng ta có thể tái sử dụng lại mô hình đã được ai đó xây dựng trước. Và thay vì phải train mô hình với tập dữ không đủ lớn, chúng ta có tận dụng lại những kiến thức của một mô hình đã được học từ một tập dữ liệu khổng lồ như là ImageNet (với hàng triệu hình ảnh liên quan đến những đối tượng khác nhau). Do đó, sử dụng kỹ thuật này sẽ đem lại kết quả tốt đối với lượng dữ liệu nhỏ hơn.

2. Sử dụng tập dữ liệu nhỏ để thử nghiệm nhanh khả năng học của mô hình

Khi sử dụng Deep learning để giải quyết một vấn đề nào đó, rất có thể trước bạn đã có nhiều người đã xây dựng mô hình để giải quyết vấn đề bạn đang gặp phải. Và một thông tin tốt là bạn có thể tiếp cận được với nhiều mô hình đã được xây dựng trước đó tại TensorFlow Hub. Đây là một kho lưu trữ rất nhiều mô hình được train sẵn, nó giúp bạn có thể import về và kết nối đến những mô hình này một cách đầy đủ chỉ với đường dẫn liên kết (URL) đến vị trí của mô hình được lưu trữ đó.

Như ở mục trên đã nói về ưu điểm của transfer learning, nó không cần sử dụng quá nhiều dữ liệu nhưng mô hình học vẫn rất tốt. Để chứng minh cho điều này, chúng ta chỉ cần lấy 10% dữ liệu gốc để train mô hình.

module_4 : preprocessing data, môt class có 1000 hình ảnh với 750 hình ảnh train, 250 để test. Nên Với 10% dữ liệu train, chúng ta chỉ cần đưa 75 hình vào mô hình train là được.

Chúng ta sẽ tải 10% dữ liệu của 10 class và tiến hành train mô hình với transfer learning

!wget https://www.dropbox.com/s/mkm0q76ng7uy430/10_food_classes_10_percent.zip
    --2021-09-08 17:12:20--  https://www.dropbox.com/s/mkm0q76ng7uy430/10_food_classes_10_percent.zip
    Resolving www.dropbox.com (www.dropbox.com)... 162.125.3.18, 2620:100:6030:18::a27d:5012
    Connecting to www.dropbox.com (www.dropbox.com)|162.125.3.18|:443... connected.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: /s/raw/mkm0q76ng7uy430/10_food_classes_10_percent.zip [following]
    --2021-09-08 17:12:20--  https://www.dropbox.com/s/raw/mkm0q76ng7uy430/10_food_classes_10_percent.zip
    Reusing existing connection to www.dropbox.com:443.
    HTTP request sent, awaiting response... 302 Found
    Location: https://uc648a6571a2a3836ef132a3fdff.dl.dropboxusercontent.com/cd/0/inline/BVxtFf7MAATXB4EHVbFacwrJvOdgOrM_N_HD0aHom0Di_Up0KRhrZFDssOgRE-A1LID5TQafFB8UTcIhUu-0G_PIrl9LzvsueXmOPwJAs208b0chwfRL9Lcl70lqf9vdjf6uOikzSxT5gIwDWmEtPhXn/file# [following]
    --2021-09-08 17:12:20--  https://uc648a6571a2a3836ef132a3fdff.dl.dropboxusercontent.com/cd/0/inline/BVxtFf7MAATXB4EHVbFacwrJvOdgOrM_N_HD0aHom0Di_Up0KRhrZFDssOgRE-A1LID5TQafFB8UTcIhUu-0G_PIrl9LzvsueXmOPwJAs208b0chwfRL9Lcl70lqf9vdjf6uOikzSxT5gIwDWmEtPhXn/file
    Resolving uc648a6571a2a3836ef132a3fdff.dl.dropboxusercontent.com (uc648a6571a2a3836ef132a3fdff.dl.dropboxusercontent.com)... 162.125.3.15, 2620:100:6018:15::a27d:30f
    Connecting to uc648a6571a2a3836ef132a3fdff.dl.dropboxusercontent.com (uc648a6571a2a3836ef132a3fdff.dl.dropboxusercontent.com)|162.125.3.15|:443... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: /cd/0/inline2/BVxuuZqTvv23YouRDuDIdtm6l2Zl0wdICSfS-wXDhS1th6xlvY-K3SPWD1hcJRNYNa5K4RnVclBbDnHjLe8ENoHlK7hCkY2zf_mGLr_Yj62EToXguNvsie-sUrSM6fPadrJysIgifYafTbUqnNCwHsKDrc1s351Fi7ptfcQEiRosnY3Tloa50gV0OoCVvYsUJ_rkVEYZ9xTL1LV7FoAMe725quO1vLQzneF9KVjpGKLXY8vQ9RG1wy3Ab7s_M2wv0bglgoCpS6Jxcv1TbVRdr3UBprbNd2vzY_CGAWwdfvIAFOZJS-p0gzEr4fYKRLHTNtg5UlflZI3BiCdbWCeauDBzffu9mO1flN7tQUw_mj5OB__56j0tg131TwBh5Ti1MxE/file [following]
    --2021-09-08 17:12:20--  https://uc648a6571a2a3836ef132a3fdff.dl.dropboxusercontent.com/cd/0/inline2/BVxuuZqTvv23YouRDuDIdtm6l2Zl0wdICSfS-wXDhS1th6xlvY-K3SPWD1hcJRNYNa5K4RnVclBbDnHjLe8ENoHlK7hCkY2zf_mGLr_Yj62EToXguNvsie-sUrSM6fPadrJysIgifYafTbUqnNCwHsKDrc1s351Fi7ptfcQEiRosnY3Tloa50gV0OoCVvYsUJ_rkVEYZ9xTL1LV7FoAMe725quO1vLQzneF9KVjpGKLXY8vQ9RG1wy3Ab7s_M2wv0bglgoCpS6Jxcv1TbVRdr3UBprbNd2vzY_CGAWwdfvIAFOZJS-p0gzEr4fYKRLHTNtg5UlflZI3BiCdbWCeauDBzffu9mO1flN7tQUw_mj5OB__56j0tg131TwBh5Ti1MxE/file
    Reusing existing connection to uc648a6571a2a3836ef132a3fdff.dl.dropboxusercontent.com:443.
    HTTP request sent, awaiting response... 200 OK
    Length: 162569862 (155M) [application/zip]
    Saving to: ‘10_food_classes_10_percent.zip.1’

    10_food_classes_10_ 100%[===================>] 155.04M  74.2MB/s    in 2.1s    

    2021-09-08 17:12:23 (74.2 MB/s) - ‘10_food_classes_10_percent.zip.1’ saved [162569862/162569862]
# Để giúp cho notebook được gọn gàng hơn, chúng ta sẽ tạo một class tên là helper để xử lý các dữ liệu và các
# gọi lại các hàm có thể tái sử dụng 
import os 
import zipfile
import matplotlib.pyplot as plt
class HelperFunctions(): 
  def unzip_file(self, filepath) : 
    zipref = zipfile.ZipFile(filepath)
    zipref.extractall()
    zipref.close()
    print("Unzipped File")
  def walk_through_directory(self, dirpath)  :
    for pathname, dirnames, filenames in os.walk(dirpath) : 
      print(f"Có {len(dirnames)} folder và {len(filenames)} files trong thư mục {pathname}")
  def plot_loss_curves(self, model_history) : 
    history = model_history.history
    acc,val_acc = history["accuracy"], history["val_accuracy"]
    loss, val_loss = history["loss"], history["val_loss"]

    plt.figure(figsize=(16,6))
    plt.subplot(121)
    plt.plot(acc, label="train accuracy")
    plt.plot(val_acc, label="val accuracy")
    plt.title("Accuracy")
    plt.legend()

    plt.subplot(122)
    plt.plot(loss,label="train loss")
    plt.plot(val_loss, label="val loss")
    plt.title("loss")
    plt.legend()

helper_functions = HelperFunctions()
helper_functions.unzip_file("10_food_classes_10_percent.zip")

Unzipped File

helper_functions.walk_through_directory("10_food_classes_10_percent")
    Có 2 folder và 0 files trong thư mục 10_food_classes_10_percent
    Có 10 folder và 0 files trong thư mục 10_food_classes_10_percent/train
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/panna_cotta
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/prime_rib
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/clam_chowder
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/greek_salad
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/spaghetti_bolognese
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/filet_mignon
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/garlic_bread
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/pulled_pork_sandwich
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/pad_thai
    Có 0 folder và 75 files trong thư mục 10_food_classes_10_percent/train/bruschetta
    Có 10 folder và 0 files trong thư mục 10_food_classes_10_percent/test
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/panna_cotta
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/prime_rib
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/clam_chowder
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/greek_salad
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/spaghetti_bolognese
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/filet_mignon
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/garlic_bread
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/pulled_pork_sandwich
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/pad_thai
    Có 0 folder và 250 files trong thư mục 10_food_classes_10_percent/test/bruschetta

Đồng bộ và chuẩn hóa dữ liệu (Xử lý dữ liệu trước khi đưa vào mô hình)

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
train_dir = "10_food_classes_10_percent/train"
test_dir = "10_food_classes_10_percent/test"

train_datagen = ImageDataGenerator(rescale=1/255.)
test_datagen = ImageDataGenerator(rescale=1/255.)

train_data = train_datagen.flow_from_directory(
    train_dir,
    target_size=(224,224),
    batch_size=32,
    class_mode="categorical"
)

test_data =  test_datagen.flow_from_directory(
    test_dir,
    target_size=(224,224),
    batch_size=32,
    class_mode="categorical"
)

Found 750 images belonging to 10 classes. Found 2500 images belonging to 10 classes.

Dữ liệu đã được đồng bộ và chuẩn hóa. Nhưng trước khi xây dựng mô hình, chúng ta sẽ thiết lập một số công cụ để giám sát quá trình train của mô hình.

2. Giới thiệu các hàm callback để giám sát mô hình train

Callbacks là một hàm bổ sung được thêm vào mô hình để theo dõi quá trình train của nó, các hàm này ghi lại những thay đổi của mô hình theo như yêu cầu. Một số hàm callbacks được sử dụng phổ biến để theo dõi mô hình train là :

  • Experiment tracking with TensorBoard : Ghi lại nhật ký hoạt động của nhiều mô hình, sau đó xem và so sánh các mô hình này một cách trực quan trên TensorBoard ( một bảng điều khiển để giám sát các neural network). Sử dụng callbacks này hữu ích khi so sánh kết quả của các mô hình khác nhau cùng train trên một tập dữ liệu.
  • Model checkpointing : Khi mô hình đang hoạt động bạn có thể ngưng lại đột ngột và có thể quay lại tiếp tục train mô hình tại nơi đã bị ngắt, không phải bắt đầu lại từ đầu. Điều này rất hữu ích đối với mô hình train mất nhiều thời gian hoặc không thể train toàn bộ trong 1 lần.
  • Early stopping : Để mô hình có thể train trong một khoảng thời gian tùy ý (bao nhiêu epoch cũng được). Khi nó train đến một thời điểm nào đó không còn khả năng tiến bộ nữa thì sẽ ngắt không học nữa. Điều này hữu ích khi bạn có tập dữ liệu lớn và quá trình train đủ lâu

Trước tiên, chúng ta sẽ sử dụng Tensorboard Callback (tf.keras.callbacks.TensorBoard(). Chức nắng chính của nó là lưu giữ quá trình hoạt động của mô hình train vào một đường dẫn log_dir cụ thể mà ta chỉ đến. Theo mặc định, sau mỗi epoch thì nó sẽ tự động ghi vào log quá trình hoạt động của mô hình tại epoch đó, thể hiện qua tham số update_freq='epoch'. Sự theo dõi này sẽ làm cho quá trình train mô hình chậm hơn vì nó phải mất thời gian để ghi lại quá trình này vào log.

import datetime 
def create_tensorboard_callback(dir_name, experiment_name) : 
  log_dir = os.path.join(dir_name, experiment_name, datetime.datetime.now().strftime("%d%m%Y-%H%M%S"))
  tensorboard_cb = tf.keras.callbacks.TensorBoard(log_dir)
  print(f"Đã lưu tensorboard callback vào : {log_dir}")
  return tensorboard_cb

Bởi vì có thể chạy nhiều thử nghiệm nên chúng ta sẽ tạo logs ghi lại quá trình hoạt động của mô hình theo đường dẫn [dir_name]/[experiment_name]/[current_timestamp] với :

  • dir_name : Đường dẫn gốc cho toàn bộ log
  • experiment_name : Tên mô hình thử nghiệm
  • current_timestamp : Thời gian thử nghiệm mô hình

Do có thể cùng một mô hình nhưng thử nghiệm nhiều lần nên ta sẽ sử dụng thời gian để nhận dạng cho tưng thử nghiệm đó.

Tạo mô hình sử dụng Tensorflow Hub

Trước đây, để tạo mô hình chúng ta sử dụng tensorflow để khởi tạo mô hình liên kết giữa các layer với nhau. Với TensorFlow Hub, quá trình xử lý này cũng tương tự ngoại trừ phần lớn các layer đã được dựng sẵn trong nó.

Chúng ta sẽ trải nghiệm với 3 mô hình từ TensorFlow Hub :

  1. ResNetV2
    • Một kiến trúc mô hình thị giác máy hiện đại tính từ 2016.
  2. EfficientNet
    • Một kiến trúc mô hình thị giác máy tính hiện đại từ 2019.
  3. ImageNet

Hiện đại có nghĩa là tại một số thời điểm, cả 2 mô hình đã đạt tỉ lệ lỗi thấp nhất trên ImageNet (ILSVRC-2012-CLS), một tiêu chuẩn vàng cho việc đánh giá trong thị giác máy tính

Làm như thế nào để tìm được mô hình phù hợp trên Tensorflow hub?

Đây là một vài bước bạn có thể tham khảo :

  1. Truy cập tfhub.dev
  2. Lựa chọn Problem domain cần giải quyết, hiện tại chúng ta đang làm việc với hình ảnh nên chọn Image
  3. Sẽ có rất nhiều Problem domain hiện lên, tùy thuộc vào trường hợp nào mà ta sẽ chọn để nó Tensorflow Hub lọc cho ta, ở đây chúng ta đang làm việc với phân loại hình ảnh nên chọn Image Classification còn lại chúng ta sẽ bỏ đi. Lưu ý : Image feature vector đừng bỏ vì nó có thể được sử dụng trong mọi vấn đề.
  4. Các mô hình được liệt kê là tất cả những mô hình có khả năng được sử dụng để giải quyết vấn đề của chúng ta

🤔 Câu hỏi: Tôi thấy có rất nhiều lựa chọn, làm sao biết được cái nào là tốt nhất?

Bạn có thể xem lượt tải của các mô hình, càng lớn thì có nghĩa có nhiều người sử dụng nó hoặc các mô hình hiện đại đạt độ chính xác cao trên paperswithcode.com, một mã nguồn thu thập các kết quả train mô hình deep learning mới nhất được ghi lại trong các bản báo cáo.

Vì chúng ta đang làm việc với phân loại hình ảnh, mục tiêu của chúng ta là tìm mô hình thực hiện tốt nhất trên ImageNet

Để tìm mô hình trên, chúng ta sẽ thu hẹp tìm kiếm bắng cách sử dụng tab Architecture

  1. Chọn tab Architecture và bạn sẽ thấy có một menu hiện lên với danh sách tên các kiến trúc. Theo nguyên tắc một kiến trúc sẽ có nhiều phiên bản, mỗi phiên bản sẽ thể hiện qua con số. Những phiên bản về sau sẽ thực hiện tốt hơn những phiên bản trước. VD : EfficientNet-B4 sẽ thực hiên tốt hơn EfficientNet-B0. Tuy nhiên, mô hình tốt hơn thì thời gian thực hiện tính toán của nó cũng sẽ lâu hơn.
  2. Lựa chọn ResnetV2 50 và bạn sẽ thấy danh sách các mô hình của nó.
  3. Click vào mô hình có tên imagenet/resnet_v2_50/feature_vector sau đó kéo xuống dưới sẽ có nút Copy URL và hướng dẫn cách sử dụng mô hình. Bạn bấm vào nút đó để copy đường dẫn

Khi Copy tại nút Copy URL nên có đường dẫn này : https://tfhub.dev/google/imagenet/resnet_v2_50/feature_vector/5

Tôi nghĩ rằng chúng ta đang thực hiện phân loại hình ảnh, tại sao chúng ta lại chọn feature vector mà không phải là classification? 🤔

feature vector là nơi các kiểu transfer learning khác nhau được đưa vào như transfer learning giữ nguyên trạng, transfer learning được trích xuất, transfer learning được tinh chỉnh.

  1. Transfer learning giữ nguyên trạng là khi đưa mô hình đã được train vào công việc hiện tại nó không có bất kỳ thay đổi nào. Chẳng hạn như một mô hình thị giác máy tính được train trên tập dữ liệu ImageNet với 1000 class hình ảnh khác nhau. Điều này đồng nghĩa tạo ra 1000 giá trị xác suất dự đoán khác nhau. (mỗi một giá trị tượng trưng cho 1 class)
  2. Transfer learning trích xuất tính năng là khi bạn lấy những mẫu cơ bản ( hay được gọi là các trọng số) từ một mô hình đã được train và điều chỉnh kết quả đầu ra của nó để phù hợp với vấn đề bạn đang giải quyết. VD : giả sử mô hình đã được train với 236 layers khác nhau (EfficientNetB0 có 236 layers), nhưng layer trên cùng của mô hình được train trong ImageNet sẽ tạo ra giá trị xuất ra cho 1000 class. Nhưng bạn chỉ muốn tạo ra giá trị dự đoán cho 10 class nên bạn có thể loại bỏ layer đó và thay thế layer khác với giá trị xuất ra là 10 để phù hợp với sô class bạn đang làm việc. Điều quan trọng ở đây là chỉ có một hoặc một vài layer trên cùng có thể train, còn các layer khác ta cần đóng băng không cho phép train. Bằng cách này, tất cả những mẫu cơ bản của mô hình đã được train vẫn được giữ nguyên trong các layer còn lại, và bạn có thể tận dụng chúng để giải quyết vấn đề của mình. Kiểu transfer learning này rất hữu ích khi dữ liệu của bạn tương tự với dữ liệu của mô hình đã được train.
  3. Transfer learning được tinh chỉnh là khi bạn lấy những mẫu cơ bản ( hay được gọi là các trọng số) của mô hình đã được train trước đó và thay đổi (tinh chỉnh) chúng cho phù hợp với vấn đề cho riêng bạn, nghĩa là sẽ train lại một số, nhiều hoặc tất cả các layer trong mô hình đã được train trước đó. Điều này hữu ích khi bạn train một tập dữ liệu lớn ( trên 100 hình ảnh cho 1 class) nơi mà dữ liệu của bạn có thể khác với dữ liệu của mô hình đã được train.

Quy trình làm việc chung là "đóng băng" tất cả các mẫu (kiến thức) đã học ở các layer phía dưới của một mô hình đã được train để chúng không thể train lại. Và một vài layer trên cùng có thể thay đổi giá trị đầu ra của nó theo dữ liệu tùy chỉnh của bạn (trích xuất tinh chỉnh)

Sau khi bạn train lại vài layer trên cùng, bạn có thể dần dần "mở băng" và train lại nhiều layer ở phía dưới hơn, sau đó chạy quá trình train trên dữ liệu của bạn để tinh chỉnh mô hình được train trước đó.

🤔 Tại sao lại chỉ train 2-3 layers trên cùng mà không phải layer ở dưới?

Layer thấp hơn trong mô hình thị giác máy tính thì càng gần với layer đầu vào (input layer), các tính năng mà nó học được càng lớn. VD layer dưới cùng trong mô hình thị giác máy tính được dùng để nhận diện hình ảnh của chó hoặc mèo có thể được các đường nét của chân, đuôi... Trong khi đó, những layer gần với kết quả đầu ra có thể chỉ học được hình dạng của răng. Thông thường, bạn sẽ muốn giữ lại các đặc điểm nhận chi tiết hơn (có thể hiểu là nét đặc trưng của chúng), bởi vì cả 2 loài này có nhiều điểm tương đồng với nhau, vì vậy, sự khác biệt vẫn giữ lại trong các điểm chi tiết hơn.

Giải thích trên đây khá dài dòng, bây giờ bắt đầu thôi.

Đầu tiên, chúng ta sẽ import các thư viện có liên quan

import tensorflow as tf
import tensorflow_hub as hub
from tensorflow.keras import Sequential, layers

Bây giờ, chúng ta sẽ lấy đường dẫn liên kết (URL) vector đặc trưng của 3 kiến trúc thị giác máy tính từ Tensorflow hub bằng cách sử dụng các bước trên.

Chúng ta sẽ sử dụng cả 3 để so sánh xem cái nào hoạt động tốt hơn.

🔑 Lưu ý : việc so sánh sự hiệu quả giữa các kiến trúc mô hình khác nhau thử nghiệm trên cùng một dữ liệu là một phương pháp rất phổ biến. Một lý do đơn giản vì chúng ta đang phân vân lựa chọn giữa các mô hình, vậy thì đây chính là phép đo tốt nhất để lựa chọn mô hình phù hợp nhất cho vấn đề cần giải quyết.

imagenet_url = "https://tfhub.dev/google/imagenet/mobilenet_v1_100_224/classification/5"
resnet_url = "https://tfhub.dev/google/imagenet/resnet_v2_50/feature_vector/5"
efficientnet_url = "https://tfhub.dev/tensorflow/efficientnet/b0/feature-vector/1"

Những URL trên đây sẽ liên kết đến những mô hình được train trước đó trong Tensorflow Hub

Khi đưa các URL này vào trong mô hình, mô hình sẽ tư động tải xuống để sử dụng. Để làm được điều này, chúng ta có thể sử dụng [KerasLayer()](https://www.tensorflow.org/hub/api_docs/python/hub/KerasLayer) có sẵn trong Tensorflow Hub để load mô hình.

Vì hiện tại có đến 3 mô hình mà chúng ta muốn xây dựng, tất cả các cấu trúc, trình tự từ khởi tạo đến compile và fit... đều giống nhau, chỉ khác URL như trên nên chúng ta có thể viết một hàm hoặc một class để có thể tái sử được nhiều lần.

IMAGE_SHAPE=(224,224)

def create_model(model_url,num_classes=10) : 
  """
  Args : 
    model_url (str): A TensorFlow Hub feature extraction URL.
    num_classes (int): Number of output neurons in output layer,
      should be equal to number of target classes, default 10.

  Returns:
    An uncompiled Keras Sequential model with model_url as feature
    extractor layer and Dense output layer with num_classes outputs.
  """
  feature_extractor_layer = hub.KerasLayer(model_url, 
                                           trainable=False,
                                           input_shape=IMAGE_SHAPE+(3,), # Tạo kích thước hình ảnh đưa vào (224,224,3)
                                           name="feature_extractor_layer")
  model = Sequential([
    feature_extractor_layer,
    layers.Dense(num_classes, activation="softmax")      
  ])
  return model

Trước tiên chúng ta sẽ tạo một hàm sử dụng kiến trúc ImageNet làm lớp trích xuất tính năng.

Sau khi mô hình được khởi tạo, chúng ta sẽ biên dịch nó bằng cách sử dụng categorical_crossentropy làm hàm loss, trình tối ưu hóa Adam và lấy độ chính xác làm phép đo lường cho mô hình.

xây dựng mô hình dựa trên RestnetV50

resnet_model = create_model(imagenet_url) 
resnet_model.compile(
    loss="categorical_crossentropy", 
    optimizer="adam",
    metrics=["accuracy"]
)

Mô hình resnet_model hiện tại của chúng ta sẽ có kiến trúc như thê này. Một mô hình xương sống ResNet50V2 với một Dense layer tùy chỉnh ở trên cùng (10 class thay vì 1000 class) .*Lưu ý: Hình ảnh hiển thị ResNet34 thay vì ResNet50. Ảnh nguồn: https://arxiv.org/abs/1512.03385.*

Chúng ta đã có sẵn dữ liệu train trong train_data_10_percent cũng như dữ liệu test được lưu dưới dạngtest_data.

Trước khi chúng ta gọi hàm fit, có một thứ nữa chúng ta sẽ thêm vào đó là hàm callback. Cụ thể hơn, gọi callback TensorBoard để chúng ta có thể theo dõi độ hiệu quả của mô hình trên TensorBoard.

Chúng ta có thể thêm một callback function vào mô hình bằng cách sử dụng tham số callbacks trong hàm fit.

Trong callbacks là một list có chứa hàm create_tensorboard_callback() đã tạo trước đó với một vài tham số cụ thể để chúng tôi biết chúng tôi đang chạy thử nghiệm nào.

Đầu tiên chúng ta vẫn giữ cho mô hình train với 5 epochs

resnet_model_history = resnet_model.fit(
    train_data,
    steps_per_epoch=len(train_data),
    epochs=5,
    validation_data=test_data,
    validation_steps=len(test_data),
    callbacks=[
               create_tensorboard_callback("tensorflow_hub", "resnet50V2")
    ]
)
    Đã lưu tensorboard callback vào : tensorflow_hub/resnet50V2/08092021-171230
    Epoch 1/5
    24/24 [==============================] - 23s 820ms/step - loss: 3.6949 - accuracy: 0.2760 - val_loss: 1.7873 - val_accuracy: 0.5216
    Epoch 2/5
    24/24 [==============================] - 18s 764ms/step - loss: 1.3078 - accuracy: 0.6240 - val_loss: 1.1592 - val_accuracy: 0.6720
    Epoch 3/5
    24/24 [==============================] - 18s 783ms/step - loss: 0.8421 - accuracy: 0.7453 - val_loss: 1.0649 - val_accuracy: 0.6960
    Epoch 4/5
    24/24 [==============================] - 18s 786ms/step - loss: 0.6460 - accuracy: 0.7867 - val_loss: 0.8741 - val_accuracy: 0.7464
    Epoch 5/5
    24/24 [==============================] - 18s 782ms/step - loss: 0.4688 - accuracy: 0.8533 - val_loss: 0.9083 - val_accuracy: 0.7276

Chỉ với 5 epoch, mô hình ResNetV250 đã có thể thổi bay bất kỳ kiến ​​trúc nào chúng tôi tạo ra trước đó, đạt được độ chính xác >85% trên bộ dữ liệu train và độ chính xác ~73% trên bộ test ... với chỉ 10 phần trăm của dữ liệu hình ảnh train!

Điều đó cho thấy sức mạnh của transfer learning. Và đó là một trong những lý do chính bất cứ khi nào bạn đang cố gắng lập mô hình bộ dữ liệu cho riêng mình, bạn nên xem xét những mô hình được train trước đó xem có cái nào cũng đã giải quyết vấn đề bạn đang gặp hay không.

Kiểm tra learning curve của mô hình trên bằng hàm plot_loss_curves trong helper_function

helper_functions.plot_loss_curves(resnet_model_history)

Tổng quan kiến trúc của resnet_model

resnet_model.summary()
    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    feature_extractor_layer (Ker (None, 1001)              4254889   
    _________________________________________________________________
    dense (Dense)                (None, 10)                10020     
    =================================================================
    Total params: 4,264,909
    Trainable params: 10,020
    Non-trainable params: 4,254,889
    _________________________________________________________________

Bạn có thể xem sức mạnh của TensorFlow Hub tại đây. Layer trích xuất thuộc tính có đên 4,254,889 tham số là các mẫu định sẵn mà mô hình đã học được trên tập dữ liệu ImageNet. Vì chúng ta đã đặt trainable = False, nên các mẫu này vẫn đóng băng (không thể train) trong quá trình train được.

Điều này có nghĩa là trong quá trình train, mô hình sẽ chỉ có thể cập nhật 10,020 tham số trong layer trên cùng để xuất ra cho phù hợp với tập dữ liệu.

ResNetV250 đã được train, đến lúc làm điều tương tự với mô hình EfficientNetB0.

xây dựng mô hình dựa trên EfficientNetB0

efficientnet_model = create_model(efficientnet_url)

efficientnet_model.compile(
    loss="categorical_crossentropy", 
    optimizer="adam",
    metrics=["accuracy"]
)

efficientnet_model_history = efficientnet_model.fit(
    train_data,
    steps_per_epoch=len(train_data), 
    epochs=5, 
    validation_data=test_data,
    validation_steps=len(test_data),
    callbacks=[
               create_tensorboard_callback("tensorflow_hub", "efficientB0")
    ]
)
    Đã lưu tensorboard callback vào : tensorflow_hub/efficientB0/08092021-172210
    Epoch 1/5
    24/24 [==============================] - 33s 994ms/step - loss: 1.7190 - accuracy: 0.5080 - val_loss: 1.1768 - val_accuracy: 0.7712
    Epoch 2/5
    24/24 [==============================] - 19s 816ms/step - loss: 0.9698 - accuracy: 0.7920 - val_loss: 0.7744 - val_accuracy: 0.8376
    Epoch 3/5
    24/24 [==============================] - 19s 814ms/step - loss: 0.6824 - accuracy: 0.8533 - val_loss: 0.6158 - val_accuracy: 0.8540
    Epoch 4/5
    24/24 [==============================] - 20s 860ms/step - loss: 0.5398 - accuracy: 0.8880 - val_loss: 0.5372 - val_accuracy: 0.8672
    Epoch 5/5
    24/24 [==============================] - 20s 858ms/step - loss: 0.4562 - accuracy: 0.9053 - val_loss: 0.4919 - val_accuracy: 0.8736

EfficientNetB0 thậm chí còn học tốt hơn cả ResnetV50, với độ chính xác trên tập dữ liệu train ~90%, và trên tập dữ liệu test >87%.

helper_functions.plot_loss_curves(efficientnet_model_history)

Leaning curves của EfficientB0 trông có vẻ đi rất song song giữa traintest. Nếu train lâu hơn, rất có thể mô hình sẽ còn cải thiện tốt hơn.

Tổng quan kiến trúc mô hình EfficientB0

efficientnet_model.summary()
    Model: "sequential_1"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    feature_extractor_layer (Ker (None, 1280)              4049564   
    _________________________________________________________________
    dense_1 (Dense)              (None, 10)                12810     
    =================================================================
    Total params: 4,062,374
    Trainable params: 12,810
    Non-trainable params: 4,049,564
    _________________________________________________________________

xây dựng mô hình dựa trên ImageNet

imagenet_model = create_model(imagenet_url)

imagenet_model.compile(
    loss="categorical_crossentropy", 
    optimizer="adam",
    metrics=["accuracy"]
)

imagenet_model_history = imagenet_model.fit(
    train_data,
    steps_per_epoch=len(train_data), 
    epochs=5, 
    validation_data=test_data,
    validation_steps=len(test_data),
    callbacks=[
               create_tensorboard_callback("tensorflow_hub", "ImageNet")
    ]
)
    Đã lưu tensorboard callback vào : tensorflow_hub/ImageNet/08092021-173100
    Epoch 1/5
    24/24 [==============================] - 23s 887ms/step - loss: 4.0113 - accuracy: 0.2480 - val_loss: 1.8793 - val_accuracy: 0.4924
    Epoch 2/5
    24/24 [==============================] - 19s 815ms/step - loss: 1.2777 - accuracy: 0.6160 - val_loss: 1.2334 - val_accuracy: 0.6312
    Epoch 3/5
    24/24 [==============================] - 19s 823ms/step - loss: 0.8434 - accuracy: 0.7347 - val_loss: 1.1377 - val_accuracy: 0.6760
    Epoch 4/5
    24/24 [==============================] - 19s 827ms/step - loss: 0.6769 - accuracy: 0.7987 - val_loss: 0.9940 - val_accuracy: 0.7124
    Epoch 5/5
    24/24 [==============================] - 19s 831ms/step - loss: 0.4613 - accuracy: 0.8560 - val_loss: 0.9108 - val_accuracy: 0.7284
helper_functions.plot_loss_curves(imagenet_model_history)

imagenet_model.summary()
    Model: "sequential_2"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    feature_extractor_layer (Ker (None, 1001)              4254889   
    _________________________________________________________________
    dense_2 (Dense)              (None, 10)                10020     
    =================================================================
    Total params: 4,264,909
    Trainable params: 10,020
    Non-trainable params: 4,254,889
    _________________________________________________________________

4. Sử dụng tensorboard để so sánh kết quả từ các mô hình

mặc dù chúng ta đã so sánh khả năng học của các mô hình bằng cách xem xét độ chính xác trên các tập dữ liệu traintest. Nhưng điều gì sẽ xảy ra nếu bạn có nhiều mô hình hơn nữa? Có thể bạn sẽ không thể nhớ được hết tất cả.

Chính nhờ có sự băn khoăn như vậy, TensorBoard (được cài đặt sẵn trong Google Colab) đã được ra đời.

Điều tốt là, kể từ khi chúng tôi thiết lập lệnh gọi lại TensorBoard, tất cả nhật ký đào tạo của mô hình của chúng ta đã được lưu tự động. Để hình dung, chúng ta có thể upload các kết quả lên TensorBoard.dev.

🔑 Lưu ý: Những thử nghiệm này là công khai, đừng upload những dữ liệu nhạy cảm. Bạn có thể xóa các thử nghiệm nếu cần.

4.1 Upload các thử nghiệm lên TensorBoard

Để upload một loạt các nhật ký TensorFlow lên TensorBoard, chúng ta có thể sử dụng lệnh sau:

Upload TensorBoard dev records

!tensorboard dev upload --logdir ./tensorflow_hub/ \
  --name "EfficientNetB0 vs. ResNet50V2" \ 
  --description "Comparing two different TF Hub feature extraction models architectures using 10% of training images" \ 
  --one_shot

Với: * --logdir đường dẫn đến vị trí upload * --name Tên thử nghiệm (bạn có thể đặt tùy ý) * --description Mô tả ngắn gọn về thử nghiệm (Tùy ý) * --one_shot thoát khỏi Tensorboard uploaded sau khi quá trình này hoàn tất

Chạy lệnh tensorboard_dev_upload lần đầu sẽ yêu cầu bạn cấp quyền cho phép upload lên TensorBoard.dev. Sau khi bạn đã cho phép upload, quá trình này sẽ bắt đầu thực hiện công việc của upload.

# Upload TensorBoard dev records
!tensorboard dev upload --logdir ./tensorflow_hub/ \
  --name "EfficientNetB0 vs. ResNet50V2 vs ImageNet" \
  --description "Comparing three different TF Hub feature extraction models architectures using 10% of training images" \
  --one_shot
    2021-09-08 17:47:52.568801: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    2021-09-08 17:47:52.577161: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    2021-09-08 17:47:52.577662: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero

    ***** TensorBoard Uploader *****

    This will upload your TensorBoard logs to https://tensorboard.dev/ from
    the following directory:

    ./tensorflow_hub/

    This TensorBoard will be visible to everyone. Do not upload sensitive
    data.

    Your use of this service is subject to Google's Terms of Service
    <https://policies.google.com/terms> and Privacy Policy
    <https://policies.google.com/privacy>, and TensorBoard.dev's Terms of Service
    <https://tensorboard.dev/policy/terms/>.

    This notice will not be shown again while you are logged into the uploader.
    To log out, run `tensorboard dev auth revoke`.

    Continue? (yes/NO) y

    Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=373649185512-8v619h5kft38l4456nm2dj4ubeqsrvh6.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=JwfPakqShcHRHcdVsLt8U9zHHaiswA&prompt=consent&access_type=offline
    Enter the authorization code: 4/1AX4XfWgOJw5-JR7uGqYxS7-ylI8w3Uw8cSXXUsvkmGlT6j8SXn3en36ypmM

    New experiment created. View your TensorBoard at: https://tensorboard.dev/experiment/c8Z2nU5fQP61rJ3xHNx6tQ/

    [2021-09-08T17:48:14] Started scanning logdir.
    [2021-09-08T17:48:17] Total uploaded: 120 scalars, 0 tensors, 4 binary objects (6.5 MB)
    [2021-09-08T17:48:17] Done scanning logdir.

    Done. View your TensorBoard at https://tensorboard.dev/experiment/c8Z2nU5fQP61rJ3xHNx6tQ/

4.2 Liệt kê các thử nghiệm đã được lưu trên Tensorboard

!tensorboard dev list

    2021-09-08 17:49:14.483172: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    2021-09-08 17:49:14.496163: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    2021-09-08 17:49:14.496706: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    https://tensorboard.dev/experiment/c8Z2nU5fQP61rJ3xHNx6tQ/
        Name                 EfficientNetB0 vs. ResNet50V2 vs ImageNet
        Description          Comparing three different TF Hub feature extraction models architectures using 10% of training images
        Id                   c8Z2nU5fQP61rJ3xHNx6tQ
        Created              2021-09-08 17:48:14 (1 minute ago)
        Updated              2021-09-08 17:48:17 (1 minute ago)
        Runs                 8
        Tags                 5
        Scalars              120
        Tensor bytes         0
        Binary object bytes  6865782
    https://tensorboard.dev/experiment/NWUkb0ZESPWjLB35PqJjzA/
        Name                 NLP modelling experiments
        Description          A series of different NLP modellings experiments with various models
        Id                   NWUkb0ZESPWjLB35PqJjzA
        Created              2021-08-29 03:17:38
        Updated              2021-08-29 03:18:08
        Runs                 14
        Tags                 5
        Scalars              1524
        Tensor bytes         0
        Binary object bytes  3134635
    Total: 2 experiment(s)

4.3 Xóa thử nghiệm trong tensorboard

!tensorboard dev delete --experiment_id NWUkb0ZESPWjLB35PqJjzA
    2021-09-08 17:51:57.555890: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    2021-09-08 17:51:57.564195: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    2021-09-08 17:51:57.564702: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    Deleted experiment NWUkb0ZESPWjLB35PqJjzA.

Kiểm tra xem đã xóa chưa

!tensorboard dev list
    2021-09-08 17:52:18.801168: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    2021-09-08 17:52:18.809233: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    2021-09-08 17:52:18.809749: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    https://tensorboard.dev/experiment/c8Z2nU5fQP61rJ3xHNx6tQ/
        Name                 EfficientNetB0 vs. ResNet50V2 vs ImageNet
        Description          Comparing three different TF Hub feature extraction models architectures using 10% of training images
        Id                   c8Z2nU5fQP61rJ3xHNx6tQ
        Created              2021-09-08 17:48:14 (4 minutes ago)
        Updated              2021-09-08 17:48:17 (4 minutes ago)
        Runs                 8
        Tags                 5
        Scalars              120
        Tensor bytes         0
        Binary object bytes  6865782
    Total: 1 experiment(s)

Bài viết có liên quan