Deep Learning với Tensorflow Module 6 phần 2.1: Transfer learning Điều chỉnh mô hình

MVT
Đang cập nhật

Ở phần trước, chúng ta đã thấy được cách vận transfer learning để trích xuất đặc trưng từ mô hình đã được học để giải quyết vấn đề của chúng ta. Và nó đã đem lại kết quả tốt hơn rất nhiều so với mô hình tự chúng ta xây dựng.

Bây giờ chúng ta sẽ đề cập đến một kiểu transfer learning khác: đó là tinh chỉnh (fine-tuning)

Nội dung trong phần này : 1. Giới thiệu về transfer learning tinh chỉnh. 2. Tải tập dữ liệu và tiền xử lý dữ liệu 3. Sử dụng Keras API để thử nghiệm nhanh mô hình tinh chỉnh (một cách khác để xây dựng mô hình trong Keras) + 3.1 Model 0 : Xây dựng mô hình transfer learning bằng Keras API với EfficientNetB0 + 3.2 Vectơ đặc trưng từ một mô hình train 4. Tạo data augmentation và thử nghiệm với mô hình 5. Thử nghiệm mô hình hóa trên dữ liệu food
+ 5.1. Model 1 : Xây dựng mô hình transfer learning trích xuất đặc trưng trên 1% dữ liệu được tăng tính đa dạng (data augmentation) + 5.2. Model 2 : Xây dựng mô hình transfer learning trích xuất đặc trưng trên 10% dữ liệu được tăng tính đa dạng (data augmentation) + Giới thiệu hàm callback ModelCheckpoint để lưu kết quả train + 5.3. Model 0 : Xây dựng mô hình transfer learning được tinh chỉnh trên 10% dữ liệu train ban đầu + 5.4. Model 2 : Xây dựng mô hình transfer learning được tinh chỉnh trên 10% dữ liệu trainđược tăng tính đa dạng. + 5.5. Model 2 : Xây dựng mô hình transfer learning được tinh chỉnh trên toàn bộ dữ liệu train được tăng tính đa dạng. 6. So sánh kết quả thử nghiệm mô các mô hình hình bằng TensorBoard

Trước khi đi qua từng nội dung, trong suốt các thử nghiệm học máy từ các module trước, bạn có để ý rằng có rất nhiều hàm được sử dụng lặp đi lặp lại từ module này đến module khác không? Chẳng hạn như hàm để giải nén file thì dùng unzip_file(), khai thác các folder và file bên trong 1 folder nào đó với walk_through_directory(), hoặc vẽ đường learning rate của mô hình plot_loss_curves()...

Để notebook được gọn gàng, chúng ta sẽ tạo một file utility_functions.py để lưu giữ các hàm có thể tái sử dụng nhiều lần này

!wget https://www.dropbox.com/s/pdotqeh8255s4bp/utility_functions.py
    utility_functions.p 100%[===================>]   1.20K  --.-KB/s    in 0s      
    2021-09-10 02:04:52 (52.9 MB/s) - ‘utility_functions.py.1’ saved [1225/1225]
from utility_functions import unzip_file, plot_loss_curves, create_tensorboard_callback, walk_through_directory

1. Giới thiệu về transfer learning tinh chỉnh

Transfer learning tinh chỉnh cho phép các trọng số (weights) của mô hình đã được train trước đó được phép thay đổi (không còn bị đóng băng) và được điều chình trong quá trình này để phù hợp hơn với dữ liệu của riêng bạn.

Đối với mô hình transfer learning trích xuất đặc trưng, bạn chỉ có thể thay đổi một vài layer trên cùng (layer gần với dữ liệu đầu ra) của mô hình được train trước để train dữ liệu của bạn, nhưng trong transfer learning tinh chỉnh, bạn có thể train nhiều layer hơn (hoặc có thể là tất cả).

Tính năng transfer learning trích xuất so với transfer learning tinh chỉnh. Sự khác biệt chính giữa cả hai là transfer learning tinh chỉnh có thể thay đổi nhiều layer của mô hình đã được train trước đó hơn trên dữ liệu tùy chọn. Việc tinh chỉnh này thường tốn nhiều dữ liệu hơn là trích xuất tính năng để đạt hiệu quả.

2. Tải tập dữ liệu và tiền xử lý dữ liệu

Trước khi train mô hình với tập dữ liệu lớn, chúng ta sẽ bắt đầu với tập dữ liệu nhỏ hơn, cụ thể ta sẽ tải xuống 10% dữ liệu train và toàn bộ dữ liệu test cho 10 class food.

!wget https://www.dropbox.com/s/mkm0q76ng7uy430/10_food_classes_10_percent.zip
    10_food_classes_10_ 100%[===================>] 155.04M  75.7MB/s    in 2.0s     
    2021-09-10 02:04:58 (75.7 MB/s) - ‘10_food_classes_10_percent.zip.1’ saved [162569862/162569862]
unzip_file("/content/10_food_classes_10_percent.zip")
    Unzipped file
walk_through_directory("/content/10_food_classes_10_percent")
2 thư mục và 0 tập tin trong thư mục /content/10_food_classes_10_percent
10 thư mục và 0 tập tin trong thư mục /content/10_food_classes_10_percent/test
0 thư mục và 250 tập tin trong thư mục /content/10_food_classes_10_percent/test/greek_salad
    ...
0 thư mục và 75 tập tin trong thư mục /content/10_food_classes_10_percent/train/garlic_bread
0 thư mục và 75 tập tin trong thư mục /content/10_food_classes_10_percent/train/clam_chowder

Chúng ta có thể thấy rằng mỗi thư mục train chứa 75 hình ảnh và mỗi thư mục test chứa 250 hình ảnh.

Tiếp theo, ta sẽ định nghĩa đường dẫn thư mục traintest

train_dir = "/content/10_food_classes_10_percent/train"
test_dir = "/content/10_food_classes_10_percent/test"

Bây giờ chúng ta đã có một số dữ liệu hình ảnh, chúng ta cần một cách để đưa chúng sang định dạng tương thích với TensorFlow.

Ở phần trước, chúng ta đã sử dụng ImageDataGenerator để làm điều này. Tuy nhiên, trong phần này sẽ giới thiệu đến bạn một hàm khác cũng tương tự như ImageDataGenerator trên. Đó là image_data_from_directory

Một ưu điểm khi sử dụng tf.keras.preprocessing.image_dataset_from_directory so với ImageDataGenerator là nó tạo ra một đối tượng tf.data.Dataset (API này xử lý nhanh hơn và hiệu quả hơn nhiều so với ImageDataGenerator khi xử lý với dữ liệu lớn)

from tensorflow.keras.preprocessing import image_dataset_from_directory
IMAGE_SHAPE = (224,224)

train_data_10_percent = image_dataset_from_directory(train_dir, 
                                          label_mode="categorical",
                                          image_size=IMAGE_SHAPE,
                                          batch_size=32)

test_data = image_dataset_from_directory(test_dir, 
                                         label_mode="categorical",
                                         image_size=IMAGE_SHAPE,
                                         batch_size=32)
    Found 750 files belonging to 10 classes.
    Found 2500 files belonging to 10 classes.

các tham số chính mà chúng ta quan tâm trong funtion image_dataset_from_directory() :

  • directory : đường dẫn đến thư mục đích nơi mà chúng ta sẽ tải hình ảnh từ đó đến mô hình
  • label_mode : kiểu dữ liệu được đưa vào mô hình
  • image_size : kích thước chung của hình ảnh mà chúng ta sẽ tải vào (height, width).
  • batch_size : số hình ảnh được chia theo từng cụm mà chúng ta sẽ tải vào. VD batch_size là 32 có nghĩa là một cụm xử lý sẽ chứa 32 hình ảnh kèm theo 32 label tương ứng với hình ảnh đó.

Ngoài ra còn một số tham số khác có thể tham khảo tại tf.keras.preprocessing.

train_data_10_percent
    <BatchDataset shapes: ((None, 224, 224, 3), (None, 10)), types: (tf.float32, tf.float32)>
  • (None, 224, 224, 3) đề cập đến hình dạng tensor của hình ảnh. Trong đó None là số cụm (batch_size) được xác định khi bắt đầu gọi hàm fit của mô hình, 224 tại vị trí 12 lần lượt là là heightwidth3 là các kênh màu (đỏ, lục, lam).
  • (None, 10) đề cập đến hình dạng tensor của label. None là số cụm (batch_size) được xác định khi bắt đầu gọi hàm fit của mô hình, 10 là số lượng labels đại diện cho 10 class khác nhau.

Bạn có thể coi None như một trình giữ chỗ đang chờ được lấp đầy với tham số batch_size từ image_dataset_from_directory().

Một ưu điểm khác của việc sử dụng API tf.data.Dataset là có các phương thức liên kết đi kèm với nó.

Ví dụ, nếu chúng ta muốn tìm tên của các class mà chúng ta đang làm việc, chúng ta có thể sử dụng thuộc tính class_names.

class_names = train_data_10_percent.class_names
print(class_names)
    ['bruschetta','clam_chowder', 'filet_mignon', 'garlic_bread', 'greek_salad', 'pad_thai', 'panna_cotta', 'prime_rib', 'pulled_pork_sandwich','spaghetti_bolognese']

Hoặc nếu chúng ta muốn xem một cụm dữ liệu mẫu, chúng ta có thể sử dụng phương thức take().

for images, labels in train_data_10_percent.take(1) : 
  print(images, labels)
    tf.Tensor(
    [[[[2.90867348e+01 2.90867348e+01 1.90867348e+01]
       [2.83724480e+01 2.83724480e+01 1.63724480e+01]
       [2.82346954e+01 3.06632671e+01 1.64489822e+01]
       ...
       ...
       [1.89949303e+01 1.61377525e+01 1.93520470e+01]
       [2.41683292e+01 1.81683292e+01 2.21683292e+01]
       [2.70560322e+01 1.70560322e+01 2.50560322e+01]]]], shape=(32, 224, 224, 3), dtype=float32) tf.Tensor(
    [[0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
     ...
     [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]], shape=(32, 10), dtype=float32)

Đặc biệt, khi sử dụng API tf.data.Dataset, các label sẽ tự động được one_hot. VD: clam_chowder có giá trị label là 2 trong tổng số 10 label thì khi mã hóa dưới dạng one_hot sẽ là [0,0,1,0,0,0,0,0,0,0]

3. Sử dụng Keras API để thử nghiệm nhanh mô hình tinh chỉnh (một cách khác để xây dựng mô hình trong Keras)

3.1 Model 0 : Xây dựng mô hình transfer learning bằng Keras API với EfficientNetB0

chúng tôi sẽ sử dụng module tf.keras.application vì nó chứa một loạt các mô hình thị giác máy tính đã được đào train trên ImageNet cũng như hàm API Keras để xây dựng mô hình.

Chúng ta sẽ thực hiện các bước sau:

  1. Khởi tạo mô hình đã được train trước đó là mô hình cơ sở bằng việc lựa chọn mô hình EfficientNetB0 từ tf.keras.application, thiết lập include_topFalse vì kết quả đầu ra với dữ liệu của chúng ta muốn dự đoán cho 10 class thay vì là 1000 như của mô hình đó.
  2. Đặt thuộc tính trainable của mô hình cơ sở này là False để giữ nguyên các trọng số của mô hình.
  3. Định nghĩa Input layer cho mô hình (định nghĩa kích thước mỗi hình được đưa mô hình)
  4. [Tùy chọn] Chuẩn hóa giá trị inputs cho mô hình. Mô số mô hình thị giác máy tính có yêu cầu phải chuẩn hóa input như ResNetV250, còn với EfficientNetB0 thì nó không cần vì nó đã có sẵn trong mô hình cơ sở.
  5. Đưa Input layer đó vào mô hình cơ sở
  6. Nhóm các output (đầu ra) của mô hình cơ sở thành một hình dạng tương thích với activation function của output layer ( chuyển các tensor đầu ra của mô hình cơ sở có cùng hình dạng với tensor của label). Điều này có thể thực hiện bằng tf.keras.layers.GlobalAveragePooling2D() hoặc tf.keras.layers.GlobalMaxPooling2D().
  7. Tạo một activation function cho output layer bằng cách sử dụng tf.keras.layers.Dense() sao cho phù hợp với kiểu mô hình.
  8. Tạo mô hình bằng tf.keras.Model và đưa 2 giá trị inputs, outputs được xây dựng ở bước 37 vào đó.
  9. Compile (biên dịch) mô hình bằng cách sử dụng hàm loss thích hợp và chọn trình tối ưu hóa .
  10. Điều chỉnh mô hình cho số lượng epochs mong muốn và sử dụng các callback function (trong trường hợp của chúng này, chúng ta sẽ bắt đầu với hàm call bacjk TensorBoard để ghi lại quá trình hoạt động của mô hình).

Trước khi xây dựng mô hình, chúng ta sẽ import thư viện Tensorflow và kiểm tra xem google colab hiện tại có sử dụng GPU không

import tensorflow as tf
!nvidia-smi -L
    GPU 0: Tesla K80 (UUID: GPU-b521b993-1f39-5024-932c-aa30515328a9)
IMAGE_SHAPE=(224,224,3)
# 1. Khởi tạo mô hình đã được train trước đó là mô hình cơ sở bằng việc lựa chọn mô hình EfficientNetB0
base_model = tf.keras.applications.EfficientNetB0(include_top=False)

# 2. Đặt thuộc tính trainable của mô hình cơ sở này là False để giữ nguyên các trọng số của mô hình.
base_model.trainable = False 

# 3. Định nghĩa Input layer cho mô hình (định nghĩa kích thước mỗi hình được đưa mô hình)
inputs = tf.keras.Input(shape=IMAGE_SHAPE,name="input_layer")

# 4.[Tùy chọn] Chuẩn hóa giá trị inputs cho mô hình
# x = tf.keras.layers.Rescaling(scale=1/255.)(inputs) # Với EfficientNetB0 hiện tại chúng ta không cần bước này

# 5. Đưa Input layer đó vào mô hình cơ sở
x = base_model(inputs)

print(x.shape)
# 6.Nhóm các output (đầu ra) của mô hình cơ sở thành một hình dạng tương thích với 
# activation function của output layer
x = tf.keras.layers.GlobalAveragePooling2D(name="global_avg_pool_2d")(x)
print(f"After globalAvgPooling2D : {x.shape}")

# 7. Tạo một activation function cho output layer bằng cách sử dụng tf.keras.layers.Dense()
# sao cho phù hợp với kiểu mô hình
outputs = tf.keras.layers.Dense(len(class_names), activation="softmax", name="output")(x)

# 8. Tạo mô hình bằng tf.keras.Model và đưa 2 giá trị inputs, outputs được xây dựng ở bước 3 và 7 vào đó.
model_0 = tf.keras.Model(inputs, outputs)

# 9. Compile (biên dịch) mô hình bằng cách sử dụng hàm loss thích hợp và chọn trình tối ưu hóa .
model_0.compile(
    loss="categorical_crossentropy",
    optimizer="adam",
    metrics=["accuracy"]
)

# 10. Điều chỉnh mô hình cho số lượng epochs mong muốn và sử dụng các callback function 
model_0_history = model_0.fit(
    train_data_10_percent,
    steps_per_epoch=len(train_data_10_percent),
    epochs=5, 
    validation_data=test_data,
    validation_steps=len(test_data),
    callbacks=[
                create_tensorboard_callback("transfer_learning_fine_tuning", "efficientB0_10_percent_data")
    ]
)
    Epoch 1/5
    24/24 [==============================] - 26s 732ms/step - loss: 1.8282 - accuracy: 0.4173 - val_loss: 1.2683 - val_accuracy: 0.7388
    ...
    Epoch 5/5
    24/24 [==============================] - 14s 593ms/step - loss: 0.5181 - accuracy: 0.8907 - val_loss: 0.5391 - val_accuracy: 0.8604

Sau hơn một phút train, mô hình của chúng ta hoạt động cực kỳ tốt trên cả quá trình đào tạo (độ chính xác ~90% ) và bộ kiểm tra (độ chính xác ~86%). Tất cả là nhờ vào sức mạnh của transfer learning. Điều quan trọng cần lưu ý là transfer learning mà chúng ta sử dụng ở đây được gọi là transfer learning trích xuất đặc tính, tương tự như những gì chúng ta đã làm với các mô hình TensorFlow Hub ở phần trước.

Nói cách khác, chúng ta đã chuyển dữ liệu tùy chỉnh của mình sang một mô hình đã được train trước (EfficientNetB0).

Chúng ta cũng đã sử dụng hàm API Keras để xây dựng mô hình của mình thay vì API Sequential.

📖 Bonus: Để thấy được những ưu điểm của kỹ thuật sử dụng Functional Keras API so với Sequential API, bạn có thể tham khảo tại TensorFlow Functional API documentation.

Kiểm tra layers của model_0

for layer_index, layer in enumerate(model_0.layers) : 
  print(layer_index, layer.name, layer.trainable)
    0 input_layer True
    1 efficientnetb0 False
    2 global_avg_pool_2d True
    3 output True

Kiến trúc tổng quan của model_0

model_0.summary()
    Model: "model"         
    =================================================================
    Total params: 4,062,381
    Trainable params: 12,810
    Non-trainable params: 4,049,571
    _________________________________________________________________

model_0 có 4 layer nhưng thực sự trong layer efficientnetb0 có 236 đến 236 layer.

Bạn có thể thấy hình dạng của input layer trong mô hình là [(None, 224, 224, 3)], nhưng đến output layer, hình dạng của nó lại là (None, 10), với None là trình giữ chỗ cho kích thước lô.

Cũng lưu ý rằng, các tham số duy nhất có thể train trong mô hình là các tham số trong output layer.

Kiểm tra layers trong base_model

for layer_index, layer in enumerate(model_0.layers[1].layers) : 
  print(layer_index, layer.name, layer.trainable)
    0 input_1 False
    1 rescaling False
    ...
    234 top_conv False
    235 top_bn False
    236 top_activation False

Có rất nhiều layer ... để viết tay tất cả những các layer này sẽ mất một thời gian khá dài để thực hiện, nhưng chúng ta vẫn có thể cải thiện chúng nhờ vào sức mạnh của transfer learning.

Check xem mô kiến trúc tổng quan của base_model

base_model.summary()

Kiểm tra learning curves của mô hình

plot_loss_curves(model_0_history)

3.2 Lấy một vectơ đặc trưng từ một mô hình train

🤔 Câu hỏi: Điều gì xảy ra với tf.keras.layers.GlobalAveragePooling2D()? Tôi chưa thấy nó bao giờ.

tf.keras.layers.GlobalAveragePooling2D() biến tensor 4D thành tensor 2D bằng cách lấy trung bình các giá trị trên các trục bên trong.

# VD 
input_shape = (1,3,3,3)
random_tensor = tf.random.normal(input_shape)
print(f"Random tensor:\n {random_tensor}\n")
print(f"Shape of random tensor : {random_tensor.shape}")

global_avg_pool_2d = tf.keras.layers.GlobalAveragePooling2D()(random_tensor)
print(f"Global average pooling 2d: {global_avg_pool_2d}")
print(f"Shape of global_avg_pool_2d : {global_avg_pool_2d.shape}")
    Random tensor:
     [[[[-0.85918516 -1.6557547  -0.37063125]
       [ 1.0982836   2.6986556   0.33071968]
       [-0.4787695  -1.2003943  -0.08261332]]
        ...
      [[ 0.63724226  0.45865574 -0.01746214]
       [ 0.6404494  -1.2814807  -1.0716159 ]
       [ 1.2379069  -1.0169531   1.274494  ]]]]

    Shape of random tensor : (1, 3, 3, 3)
    Global average pooling 2d: [[-0.09024313 -0.35125595  0.14408617]]
    Shape of global_avg_pool_2d : (1, 3)

Khi gọi hàm tf.keras.layers.GlobalAveragePooling2D() cho một tensor nào đó, nó sẽ lấy giá trị trung bình của tensor đó qua 2 trục giữa khiến cho hình dạng ban đầu từ (1, 3, 3, 3) bị rút gọn lại còn (1,3).

Có thể làm thủ công như sau:

tf.reduce_mean(random_tensor,axis=[1,2])
    <tf.Tensor: shape=(1, 3), dtype=float32, numpy=array([[-0.09024313, -0.35125595,  0.14408617]], dtype=float32)>

Việc gộp trung bình này không chỉ giúp cho hình dạng của output trở nên tương thích với yêu cầu hình dạng của input mà nó còn cô đọng thông tin được tìm thấy bởi mô hình cơ sở thành một vectơ đặc trưng có kích thước thấp hơn.

🔑 Lưu ý: Một trong những lý do khiến transfer learning trích xuất đặc tính được đặt tên như vậy là do những gì xảy ra bên trong một mô hình được trước sẽ xuất ra một vectơ đặc trưng ( là một dãy số dài được gộp lại lấy giá trị trung bình)

Vậy còn tf.keras.GlobalMaxPool2D() thì sao ? Với random_tensor như trên ta sẽ thử xem nó sẽ là gì

print(f"Random tensor:\n {random_tensor}\n")
print(f"Shape of random tensor : {random_tensor.shape}")

global_max_pool_2d = tf.keras.layers.GlobalMaxPooling2D()(random_tensor)
print(f"Global average pooling 2d: {global_max_pool_2d}")
print(f"Shape of global_max_pool_2d : {global_max_pool_2d.shape}")
    Random tensor:
     [[[[-0.85918516 -1.6557547  -0.37063125]
       [ 1.0982836   2.6986556   0.33071968]
       [-0.4787695  -1.2003943  -0.08261332]]
       ...
       [[ 0.63724226  0.45865574 -0.01746214]
       [ 0.6404494  -1.2814807  -1.0716159 ]
       [ 1.2379069  -1.0169531   1.274494  ]]]]

    Shape of random tensor : (1, 3, 3, 3)
    Global average pooling 2d: [[1.2379069 2.6986556 1.274494 ]]
    Shape of global_max_pool_2d : (1, 3)

Chúng ta có thể mô phỏng quá trình này bằng cách sử dụng tf.reduce_max() và chỉ định các trục thích hợp.

tf.reduce_max(random_tensor, axis=[1,2])
    <tf.Tensor: shape=(1, 3), dtype=float32, numpy=array([[1.2379069, 2.6986556, 1.274494 ]], dtype=float32)>

4. Tạo data augmentation và thử nghiệm với mô hình

Data augmentation là cách làm cho tập dữ liệu train của bạn đa dạng hơn mà không cần thêm nhiều dữ liệu hơn.

Trước đây, chúng ta đã từng tạo data augmentation với ImageDataGenerator, nhưng lần này, chúng ta sẽ không làm như vậy mà sẽ xây dựng data augmentation ngay trên mô hình với tensorflow.keras.layers.experimental.preprocessing.

Đây là một tính năng tương đối mới được thêm vào từ TensorFlow 2.2+ nhưng nó rất mạnh mẽ. Việc thêm một data augmentation layer vào mô hình có những ưu điểm sau : + Việc xử lý trước những hình ảnh (tặng cường tính đa dạng cho chúng) diễn ra ngay trên GPU thay vì CPU, mà GPU thì xử lý với tới độ nhanh hơn rất nhiều. Đối với dữ liệu hình ảnh môi trường xử lý tốt nhất và nhanh nhất là GPU, trong khi với những dữ liệu bằng văn bản hoặc dữ liệu có cấu trúc thì phù hợp với CPU hơn. + Sự tăng tính đa dạng cho dữ liệu hình ảnh chỉ xảy ra cho quá trình train vì vậy chúng ta vẫn có thể export toàn bộ mô hình và sử dụng nó ở những nơi khác. Và nếu ai đó muốn train mô hình tương tự như chúng ta, bao gồm các dữ liệu được tăng tính đa dạng, họ có thể train được.

Ví dụ về việc sử dụng tăng tính đa dạng cho dữ liệu ở layer tiên trong mô hình (EfficientNetB0).

Để sử dụng kỹ thuật tăng tính đa dạng (data augmentation) cho dữ liệu ngay trong mô hình, chúng ta sẽ tạo một mô hình Keras Sequential chỉ bao gồm các layer tiền xử lý dữ liệu, sau đó chúng ta có thể sử dụng mô hình Sequential này trong một mô hình dạng hàm khác.

Các phép biến đổi tăng tính đa dạng cho dữ liệu mà chúng tôi sẽ sử dụng là: * RandomFlip -lật hình ảnh trên trục ngang hoặc trục dọc. * RandomRotation - xoay ngẫu nhiên hình ảnh theo trên một lương đã xác định. * RandomZoom - phóng to ngẫu nhiên một hình ảnh trên một lượng đã chỉ định. * RandomHeight - thay đổi ngẫu nhiên chiều cao hình ảnh theo một lượng xác định. * RandomWidth - thay đổi ngẫu nhiên chiều rộng hình ảnh theo một lượng xác định. * Rescaling - chuẩn hóa các giá trị pixel của hình ảnh từ 0 đến 1, điều này đáng nói vì nó là bắt buộc đối với một số mô hình hình ảnh nhưng vì chúng ta đang triển khai mô hình EfficientNetB0 nên không bắt buộc.

import tensorflow as tf 
from tensorflow.keras import Sequential, layers, Model
from tensorflow.keras.layers.experimental import preprocessing
data_augmentation = Sequential([
  preprocessing.RandomFlip(mode="horizontal"),
  preprocessing.RandomRotation(0.25),
  preprocessing.RandomZoom(0.2),
  preprocessing.RandomHeight(0.3),
  preprocessing.RandomWidth(0.2),
  # preprocessing.Rescaling(scale=1/255.) # Với EfficientNetB0 chúng ta không cần rescale
],name="data_augmentation")

Mô hình tuần tự tăng độ đa dạng của dữ liệu đã sẵn sàng. Như bạn sẽ thấy ngay sau đó, chúng tôi sẽ có thể sắp xếp "mô hình" này như một layer vào mô hình transfer learning sau này.

Nhưng trước khi bắt đầu xây dựng mô hình, chúng ta sẽ hiển thị hình ảnh bất kỳ với data augmentation

import random 
import os
import matplotlib.pyplot as plt 

target_class = random.choice(os.listdir(train_dir))
target_class_path = os.path.join(train_dir, target_class)
random_image_name = random.choice(os.listdir(target_class_path))
random_image_path = os.path.join(target_class_path, random_image_name)
image = plt.imread(random_image_path)
plt.figure(figsize=(16,6))
plt.subplot(121)
plt.imshow(image)
plt.title("Origin image")
plt.axis(False)

plt.subplot(122)
augmented_image = data_augmentation(tf.expand_dims(image,axis=0))  # vì mô hình data augmentation bắt buộc có dạng (None, height, width, 3)
augmented_image = tf.squeeze(augmented_image, axis=0)/255. # để hiển thị hình ảnh, sau bạn cần chuẩn hóa chúng và bỏ đi chiều đầu tiên
plt.imshow(augmented_image)
plt.title("Augmented Image")
plt.axis(False)
plt.suptitle(target_class)

Chạy ô phía trên một vài lần và bạn có thể thấy các những hình ảnh ngẫu nhiên sẽ bị biến dạng khác nhau tùy thuộc các tỉ lệ ngẫu nhiên của các tham số đã được thiết lập.

Làm điều này sẽ làm cho tập dữ liệu train của chúng ta trở nên đa dạng hơn. Bạn có thể nghĩ như thể bạn đang chụp ảnh đồ ăn trong đời thực, không phải tất cả các hình ảnh đều hoàn hảo, một số hình ảnh sẽ được có hình dáng theo những cách kỳ lạ. Đây là những loại hình ảnh mà chúng ta muốn mô hình có thể xử lý.

Nói về mô hình, chúng ta sẽ áp dụng tất cả các bước giống như model_0 ngoại trừ sẽ thêm mô hình Tuần tự tăng tính đa dạng cho dữ liệu dưới dạng một layer ngay sau input layer

Phần này sẽ được thực hiện ở bài viết tiếp theo.


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