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

MVT
Đang cập nhật

Ở phần trước chúng ta đã thực hiện 4 bước xây dựng mô hình transfer learning tinh chỉnh. Trong phần này chúng ta sẽ tiếp tục

Nội dung trong phần này : 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

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 đa tăng cường tính đa dạng (data augmentation)

Trước tiên chúng ta sẽ tải tập dữ liệu chứa 1% trong tổng dữ liệu đã được thực hiện trong module 4: preprocessing data

!wget https://www.dropbox.com/s/dryeebi147n37ra/10_food_classes_1_percent.zip
    10_food_classes_1_p 100%[===================>] 122.75M  78.4MB/s    in 1.6s    

    2021-09-10 02:06:41 (78.4 MB/s) - ‘10_food_classes_1_percent.zip.1’ saved [128714735/128714735]
unzip_file("/content/10_food_classes_1_percent.zip")
    Unzipped file
walk_through_directory("10_food_classes_1_percent")
2 thư mục và 0 tập tin trong thư mục 10_food_classes_1_percent
10 thư mục và 0 tập tin trong thư mục 10_food_classes_1_percent/test
   ...
0 thư mục và 7 tập tin trong thư mục 10_food_classes_1_percent/train/garlic_bread
0 thư mục và 7 tập tin trong thư mục 10_food_classes_1_percent/train/clam_chowder
  • Tập dữ liệu train hiên tại mỗi class chỉ có 1% dữ liệu tức là chỉ có 7 hình để train cho 1 class.
  • Tập dữ liệu test không có gì thay đổi so với 10% dữ liệu như trên.
train_dir_1_percent_data_10_class = "10_food_classes_1_percent/train"
test_dir_1_percent_data_10_class = "10_food_classes_1_percent/test"
train_data_1_percent = image_dataset_from_directory(
    train_dir_1_percent_data_10_class, 
    image_size=(224,224),
    batch_size=32, 
    label_mode="categorical"
)

test_data = image_dataset_from_directory(
    test_dir_1_percent_data_10_class, 
    image_size=(224,224),
    batch_size=32, 
    label_mode="categorical"
)
    Found 70 files belonging to 10 classes.
    Found 2500 files belonging to 10 classes.
train_data_1_percent.class_names
    ['bruschetta',
     'clam_chowder',
     'filet_mignon',
     'garlic_bread',
     'greek_salad',
     'pad_thai',
     'panna_cotta',
     'prime_rib',
     'pulled_pork_sandwich',
     'spaghetti_bolognese']

Kiểm tra xem class names của mô hình 1% dữ liệu có khác với 10% dữ liệu hay không

class_names_10_percent_data = train_data_10_percent.class_names
class_names_1_percent_data = train_data_1_percent.class_names 
assert set(class_names_10_percent_data) == set(class_names_1_percent_data)

Nếu class_names của 2 tập dữ liệu khác nhau, nó sẽ lập tức báo lỗi

input_shape = (224,224,3)
base_model = tf.keras.applications.EfficientNetB0(include_top=False)
base_model.trainable = False

# Tạo input layer
inputs = layers.Input(shape=input_shape, name="input_layer")

# Tăng tính đa dạng cho inputs trên
augmented_inputs = data_augmentation(inputs)

# Chuẩn hóa dữ liệu inputs (đối với EfficientNetB0 thì bỏ qua bước này)
# normed_inputs = layers.Rescaling(scale=1/255.)

# Đưa augmented_inputs vào mô hình base_model
x = base_model(augmented_inputs)

# Nhóm các output đặc trưng của mô hình cơ sở 
global_avg_pool_2d = layers.GlobalAveragePooling2D(name="global_avg_pool_2d")(x)

# Đặt một dense layer như là outputs 
outputs = layers.Dense(len(class_names), activation="softmax", name="output_layer")(global_avg_pool_2d)

# Tạo mô hình với 2 tham số inputs và outputs 
model_1 = Model(inputs, outputs)

# Biên dịch mô hình với các hàm loss, kỹ thuật tối ưu hóa và phương pháp đo lường cho mô hình
model_1.compile(
    loss="categorical_crossentropy",
    optimizer="adam",
    metrics=["accuracy"]
)

# Fit mô hình
model_1_history = model_1.fit(
    train_data_1_percent,
    steps_per_epoch=len(train_data_1_percent),
    epochs=5, 
    validation_data=test_data,
    validation_steps=int(0.25*len(test_data)),
    callbacks=[
      create_tensorboard_callback("transfer_learning_fine_tuning", "efficientB0_1_percent_data_augmentation")
    ]
)
    Đã lưu tensorboard vào transfer_learning_fine_tuning/efficientB0_1_percent_data_augmentation/10092021-020646
    Epoch 1/5
    ...
    Epoch 5/5
    3/3 [==============================] - 7s 3s/step - loss: 1.6743 - accuracy: 0.6143 - val_loss: 1.8225 - val_accuracy: 0.4391

Mô hình chỉ sử dụng 1% dữ liệu (7 hình ảnh đã được biến dạng ở mỗi class) để train, nhưng với số lượng hình ảnh ít ỏi đó, nó vẫn cho ra kết quả dự đoán rất tốt so với 50,67% của mô hình original Food-101 paper nhưng được train với tất cả dữ liệu.

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

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

Trong mô hình đã có data_augmentation là một layer được tích hợp ngay trong mô hình. Điều này cũng có nghĩa là khi chúng ta lưu lại mô hình này và load nó ở bất kỳ nơi nào khác thì data augmentation layers này vẫn gắn liền với mô hình.

Một điều quan trọng nữa là data augmentation chỉ chạy trong quá trình train Vì vậy, nếu chúng ta đánh giá hoặc sử dụng mô hình của dự đoán một class nào đó của một hình ảnh, thì các data augmentation layer sẽ tự động ngắt.

results_1_percent_data_aug = model_1.evaluate(test_data)
results_1_percent_data_aug
    79/79 [==============================] - 11s 133ms/step - loss: 1.8288 - accuracy: 0.4368
    [1.8288201093673706, 0.4368000030517578]

Kết quả ở đây có thể tốt hơn hoặc kém hơn một chút so với kết quả đầu ra của nhật ký của mô hình của chúng ta trong quá trình train, vì trong lúc train chúng ta chỉ đánh giá mô hình của mình trên 25% dữ liệu test bằng cách sử dụng dòng validation_steps = int(0,25 * len (test_data)). Việc rút ngắn số lượng dữ liệu test trong lúc train giúp tiết kiệm thời gian xử lý của mô hình.

Tiếp theo, kiểm tra learning curves mà mô hình học qua mỗi epoch:

plot_loss_curves(model_1_history)

Có vẻ như các chỉ số đo lường hiệu suất trên cả hai tập dữ liệu có cùng quỹ đạo, hướng đi khá tương đồng nhau, và có khả năng mô hình sẽ cải thiện thêm nếu chúng ta tiếp tục train lâu hơn. Nhưng chúng ta sẽ chuyển sang train kiểu dữ liệu khác thay vì train lâu hơn như vậy.

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 đa tăng cường tính đa dạng (data augmentation)

Ở phần này chúng ta đã download tập dữ liệu trước đó để train model_0, nên không cần phải thực hiện các bước tải dữ liệu và khám phá khái quát thành phần của tập dữ liệu đó. Chúng ta sẽ xây dựng mô hình luôn. Phần này sẽ không giải thích lại vì nó tương tự như mô hình trên, bạn có thể tham khảo mô hình trên để nắm rõ hơn.

IMAGE_SHAPE=(224,224,3)

base_model = tf.keras.applications.EfficientNetB0(include_top=False)

base_model.trainable = False

inputs = layers.Input(shape=IMAGE_SHAPE, name="input_layer")

augmented_inputs = data_augmentation(inputs)

x = base_model(augmented_inputs)

global_avg_pooling_2d = layers.GlobalAveragePooling2D()(x)

outputs = layers.Dense(len(class_names), activation="softmax", name="outputs")(global_avg_pooling_2d)

model_2 = Model(inputs, outputs)

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

Giới thiệu hàm callback ModelCheckpoint để lưu kết quả train

Trong mô hình này, để tăng thêm sự mới mẻ chúng ta sẽ thêm một callback function có tên ModelCheckpoint.

ModelCheckpoint callback cung cấp cho bạn khả năng lưu lại hoặc toàn bộ mô hình theo định dạng SavedModel hoặc chỉ lưu trọng số (mẫu) vào một thư mục được chỉ định khi nó đào tạo.

Điều này rất hữu ích nếu bạn nghĩ rằng mô hình của mình sẽ được train trong một thời gian dài nhưng không thể train được liên tục và bạn muốn sao lưu mô hình đã train lại. Điều đó cũng có nghĩa nếu bạn nghĩ rằng mô hình của mình có thể tốt hơn khi được train lâu hơn, thì bạn có thể tải lại mô hình đó từ vị trí đã lưu và tiếp tục train tiếp thay vì phải train lại từ đầu.

Ví dụ: giả sử bạn fit mô hình transfer learning trích xuất đặc trưng trong 5 epochs. Khi bạn kiểm tra các đường learning curves và thấy nó vẫn đang được cải thiện, lúc đó bạn muốn xem liệu việc tinh chỉnh thêm 5 epoch nữa liệu có thể tốt hơn được không. Lúc này, bạn có load hàm callback checkpoint, giải nén một số (hoặc tất cả) các layer của mô hình cơ sở và sau đó tiếp tục train. Trên thực tế, đó chính xác là những gì chúng ta sẽ làm.

Nhưng trước tiên, chúng ta sẽ tạo hàm ModelCheckpoint để chỉ định một thư mục mà chúng ta muốn lưu vào.

def create_checkpoint_callback(checkpoint_dir, experiment_name) : 
  checkpoint_path = os.path.join(checkpoint_dir, experiment_name +".ckpt")  
  model_checkpoint_cb = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, 
                                     monitor="val_loss", 
                                     verbose=1,
                                     save_weights_only=True, # Chỉ lưu trọng số, nếu False sẽ save toàn bộ mô hình
                                     save_best_only=False, #True để save mô hình nào là tốt nhất, 
                                     save_freq="epoch" #lưu sau mỗi epoch
                                    )
  print(f"Đã lưu checkpoint callback tại :{checkpoint_path}")
  return model_checkpoint_cb

🤔 Câu hỏi: Lưu toàn bộ mô hình (định dạng SavedModel) và chỉ lưu trọng số có gì khác nhau?

Định dạng SavedModel lưu toàn bộ kiến ​​trúc, trọng số và cấu hình train của mô hình vào trong một thư mục. Nó giúp bạn dễ dàng tải lại mô hình của mình một cách chính xác như thế nào ở nơi khác. Tuy nhiên, nếu bạn không muốn chia sẻ tất cả những thông tin chi tiết này với người khác, bạn có thể chỉ muốn lưu và chia sẻ trọng số (đây sẽ chỉ là những con số lớn không thể giải thích được bởi con người). Nếu dung lượng ổ đĩa là một vấn đề, chỉ lưu trọng số sẽ nhanh hơn và chiếm ít dung lượng hơn so với lưu toàn bộ mô hình.

Bởi vì chúng ta sẽ tinh chỉnh nó sau đó, nên chúng ta sẽ tạo một biến initial_epochs và đặt nó là 5 để sử dụng sau này.

initial_epochs = 5 
model_2_history = model_2.fit(
    train_data_10_percent, 
    epochs=initial_epochs,
    steps_per_epoch=len(train_data_10_percent),
    validation_data=test_data,
    validation_steps=int(0.25 * len(test_data)),
    callbacks=[
             create_tensorboard_callback("transfer_learning_fine_tuning", "efficientB0_10_percent_data_augmentation"),
             create_checkpoint_callback(checkpoint_dir="checkpoints",experiment_name="model_2_10_percent_data_aug")
    ]
)
    Epoch 1/5
    24/24 [==============================] - 29s 879ms/step - loss: 1.9575 - accuracy: 0.3573 - val_loss: 1.4083 - val_accuracy: 0.6546

    Epoch 00001: saving model to checkpoints/model_2_10_percent_data_aug.ckpt
    ...
    Epoch 5/5
    24/24 [==============================] - 14s 581ms/step - loss: 0.7580 - accuracy: 0.8040 - val_loss: 0.6536 - val_accuracy: 0.8158

    Epoch 00005: saving model to checkpoints/model_2_10_percent_data_aug.ckpt

Đánh giá mô hình với test_data

result_10_percent_data_aug = model_2.evaluate(test_data)
result_10_percent_data_aug
    79/79 [==============================] - 11s 131ms/step - loss: 0.6307 - accuracy: 0.8252
    [0.6307364106178284, 0.8252000212669373]

Vẽ đường learning curves của mô hình

plot_loss_curves(model_2_history)

Với 10% dữ liệu ở mỗi class ( tức chỉ chỉ gấp 10 dữ liệu cho mỗi class) mô hình đã đạt được độ chính xác đáng kinh ngạc. Dù model_0model_2 có cùng dữ liệu và model_0 có độ chính xác cao hơn so với model_2 nhưng nếu để ngăn chặn overfitting cho mô hình, thì bạn cần chọn model_2. Ngoài ra, các đường cong đang có xu hướng đi đúng hướng, nghĩa là nếu chúng tôi train lâu hơn, các chỉ số của nó có thể sẽ được cải thiện theo hướng tốt như vậy.

Để load những trọng số mà mô hình đã lưu, bạn có thể sử dụng phương thức load_weights(), chuyển nó đến đường dẫn nơi lưu trữ trọng só đó.

model_2.load_weights("checkpoints/model_2_10_percent_data_aug.ckpt")
    <tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f0ad8d4be10>

Lúc này mô hình đã load các trọng số được lưu tại checkpoints/model_2_10_percent_data_aug.ckpt

loaded_weights_model_results = model_2.evaluate(test_data)
    79/79 [==============================] - 11s 134ms/step - loss: 0.6307 - accuracy: 0.8252

Bây giờ chúng ta sẽ so sánh kết quả của mô hình được train trước đó với mô hình được tải lên. Các kết quả này sẽ rất gần nếu không muốn nói là hoàn toàn giống nhau. Lý do cho những khác biệt nhỏ là do mức độ chính xác của các con số được tính toán.

result_10_percent_data_aug
    [0.6307364106178284, 0.8252000212669373]
loaded_weights_model_results
    [0.6307364106178284, 0.8252000212669373]
result_10_percent_data_aug == loaded_weights_model_results

True

5.3. Model 0 tinh chỉnh : Xây dựng mô hình transfer learning được tinh chỉnh trên 10% dữ liệu train ban đầu

Ví dụ cấp cao về việc tinh chỉnh mô hình EfficientNet. Các lớp dưới cùng (các lớp gần với dữ liệu đầu vào) vẫn được đóng băng khi các lớp trên cùng (các lớp gần với dữ liệu đầu ra) được cập nhật trong quá trình đào tạo.

Ở những mô hình transfer learning trích xuất đặc trưng trước, chúng ta đã train 10% dữ liệu gốc và dữ liệu được tăng cường tính đa dạng với 5 epoch. Các layer trong base_model chúng ta chưa hề đả động đến, nó vẫn được sử dụng toàn bộ những trọng số, những kiến thức đã được train trước đó. Đối với mô hình tiếp theo, chúng ta sẽ chuyển sang transfer learning được tinh chỉnh. Điều này có nghĩa là chúng ta sẽ sử dụng cùng một mô hình base_model, nhưng sẽ mở cho một số layer của nó (những lớp gần với output) được có thể train với dữ liệu của chúng ta và chạy mô hình thêm một vài epoch nữa.

model_0.summary()
    Model: "model"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    input_layer (InputLayer)     [(None, 224, 224, 3)]     0         
    _________________________________________________________________
    efficientnetb0 (Functional)  (None, None, None, 1280)  4049571   
    _________________________________________________________________
    global_avg_pool_2d (GlobalAv (None, 1280)              0         
    _________________________________________________________________
    output (Dense)               (None, 10)                12810     
    =================================================================
    Total params: 4,062,381
    Trainable params: 12,810
    Non-trainable params: 4,049,571
    _________________________________________________________________
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

Tất cả các layer trong layer Effectsnetb0 đã bị đóng băng. Chúng ta có thể xác nhận điều này bằng thuộc tính trainable_variables.

len(model_2.layers[1].trainable_variables)
0

Điều này giống với mô hình cơ sở base_model.

len(base_model.trainable_variables)
0

Thậm chí có thể kiểm tra từng layer trong base_model:

for layer_index, layer in enumerate(base_model.layers) : 
  print(layer_index, layer.name, layer.trainable)
    0 input_3 False
    1 rescaling_2 False
    ...
    235 top_bn False
    236 top_activation False

Bây giờ để tinh chỉnh mô hình cơ sở phù hợp với dữ liệu của, chúng ta sẽ cho phép 10 layer trên cùng được phép train và tiếp tục train mô hình thêm 5 epoch tiếp theo.

Điều này có nghĩa là tất cả các layer của mô hình cơ sở ngoại trừ 10 layer cuối cùng sẽ vẫn bị đóng băng và không thể train được. Và trọng số ở các layer được phép train sẽ được cập nhật trong quá trình train.

🤔 Câu hỏi: Bạn nên mở bao nhiêu lớp khi luyện tập?

Không có quy tắc thiết lập cho điều này. Bạn có thể unfreeze mọi layer trong mô hình được train trước ddos hoặc bạn có thể thử mở từng layer một. Tốt nhất hãy thử với số lượng lượng layer tăng dần dần được tinh chỉnh khác nhau để xem điều gì sẽ xảy ra. Nói chung, neus có càng có ít dữ liệu thì nên dùng ít layer có thể train.

Để bắt đầu tinh chỉnh, trước tiên ta sẽ cho phép tính năng của base_model.trainableTrue. Sau đó, tạo vòng lặp cho đến trước phần tử thứ 10 tính từ cuối cùng đi lên và set cho những layer trainableFalse. Cụ thể như sau :

base_model.trainable = True

for layer in base_model.layers[:-10] : 
  layer.trainable = False

Kiểm tra có bao nhiêu layer có thê train trong trong base_model

for layer_index, layer in enumerate(base_model.layers) : 
  print(layer_index, layer.name, layer.trainable)
    0 input_3 False
    1 rescaling_2 False
    2 normalization_2 False
    ...
    199 block6c_se_reduce False
    200 block6c_se_expand False
    ...
    227 block7a_se_squeeze True
    228 block7a_se_reshape True
    229 block7a_se_reduce True
    230 block7a_se_expand True
    231 block7a_se_excite True
    232 block7a_project_conv True
    233 block7a_project_bn True
    234 top_conv True
    235 top_bn True
    236 top_activation True

Có vẻ như tất cả các layer ngoại trừ 10 layer cuối cùng đều bị đóng băng và không thể train được. Điều này có nghĩa là chỉ 10 layer cuối cùng của base_model cùng với output layer sẽ được cập nhật trọng số của chúng trong quá trình train.

🤔 Câu hỏi: Tại sao chúng tôi biên dịch lại mô hình?

Mỗi khi thực hiện thay đổi đối trong mô hình của mình, bạn cần phải biên dịch lại chúng.

Trong trường hợp chúng ta đang sử dụng cùng một hàm loss, trình tối ưu hóa và không gian đo, ngoại trừ lần này learning_rate cho trình tối ưu hóa của chúng ta sẽ nhỏ hơn 10 lần so với trước đây (0,0001 thay vì mặc định của Adam là 0,001).

Chúng ta làm điều này để mô hình không cố gắng ghi đè các trọng số hiện có trong mô hình được đào tạo trước quá nhanh. Nói cách khác, chúng tôi muốn học dần dần.

🔑Lưu ý : Không có tiêu chuẩn thiết lập nào để đặt learning_rate trong quá trình tinh chỉnh, mặc dù mức giảm 2.6x-10x+ dường như hoạt động tốt trong thực tế.

Bây giờ chúng ta có bao nhiêu biến có thể train?

len(model_0.trainable_variables)
    2

Chúng tôi sẽ tiếp tục train từ nơi mà mô hình trước đó đã hoàn thành. Vì nó được train trong 5 epochs, quá trình tinh chỉnh của chúng ta sẽ bắt đầu từ epoch thứ 5 và tiếp tục trong 5 với epoch tiếp theo.

fine_tune_epochs = 5 

model_0_fine_tune_history = model_0.fit(
    train_data_10_percent,
    steps_per_epoch=len(train_data_10_percent),
    epochs=initial_epochs+fine_tune_epochs, 
    initial_epoch=model_0_history.epoch[-1] + 1, 
    validation_data=test_data, 
    validation_steps=int(0.25 * len(test_data))
)
    Epoch 6/10
    24/24 [==============================] - 7s 268ms/step - loss: 0.4617 - accuracy: 0.8933 - val_loss: 0.5096 - val_accuracy: 0.8684
    ...
    Epoch 10/10
    24/24 [==============================] - 7s 268ms/step - loss: 0.3095 - accuracy: 0.9387 - val_loss: 0.4603 - val_accuracy: 0.8668

🔑 Lưu ý: Quá trình tinh chỉnh thường mất nhiều thời gian hơn trên mỗi epoch so với việc trích xuất đặc tính (do cập nhật nhiều trọng số hơn trong toàn neural).

# Đánh giá mô hình trên dữ liệu test 
result_fine_tune_10_percent = model_0.evaluate(test_data)
result_fine_tune_10_percent
    79/79 [==============================] - 11s 133ms/step - loss: 0.4412 - accuracy: 0.8700
    [0.44118955731391907, 0.8700000047683716]

Chúng ta sẽ so sánh khả năng học của mô hình giữa 2 giai đoạn trước khi tinh chỉnh (5 epoch đầu) và sau khi tinh chỉnh (5 epoch sau)

def compare_history(original_history, new_history) : 
  og_history = original_history.history 
  n_history = new_history.history 

  og_acc, og_val_acc = og_history["accuracy"], og_history["val_accuracy"]
  og_loss, og_val_loss = og_history["loss"], og_history["val_loss"]

  new_acc, new_val_acc = n_history["accuracy"], n_history["val_accuracy"]
  new_loss, new_val_loss = n_history["loss"], n_history["val_loss"]

  total_acc = og_acc + new_acc 
  total_val_acc = og_val_acc + new_val_acc
  total_loss = og_loss + new_loss
  total_val_loss = og_val_loss + new_val_loss   
  plt.figure(figsize=(20,6))
  plt.subplot(121)
  plt.plot(total_acc,label="train accuracy")
  plt.plot(total_val_acc, label="val accuracy")
  plt.plot([model_0_history.epoch[-1],model_0_history.epoch[-1]],plt.ylim(), label="Bắt đầu tinh chỉnh")
  plt.title("Compare accuracy history")  
  plt.xlabel("epochs")
  plt.ylabel("percentage")  
  plt.legend()
  plt.subplot(122)
  plt.plot(total_loss, label="train loss")
  plt.plot(total_val_loss, label="val loss")
  plt.plot([model_0_history.epoch[-1],model_0_history.epoch[-1]],plt.ylim(),label="Bắt đầu tinh chỉnh")
  plt.title("Compare loss history")
  plt.xlabel("epochs")
  plt.ylabel("value")  
  plt.legend()
compare_history(model_0_history, model_0_fine_tune_history)

model_0 có đã cải thiện đáng kể sau khi chúng ta cho phép một số layer trong base_model được phép train trên dữ liệu này.

Tương tự, chúng ta sẽ áp dụng 10% dữ liệu train này vào model_2 nhưng các dữ liệu này khi đưa vào mô hình sẽ có một layer sau đó sẽ thực hiện công việc "biến dạng" làm cho dữ liệu thay đổi đa dạng hơn.

5.4. Model 2 tinh chỉnh : Xây dựng mô hình transfer learning được tinh chỉnh trên 10% dữ liệu train tăng cường tính đa dạng.

from tensorflow.keras.utils import plot_model
plot_model(model_2)

Chúng ta sẽ tận dụng lại model_2 để thực hiện transfer learning tinh chỉnh

fine_tune_epoch=5

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

model_2_fine_tune_history = model_2.fit(
    train_data_10_percent,
    steps_per_epoch=len(train_data_10_percent),
    epochs=initial_epochs + fine_tune_epochs, 
    initial_epoch=model_2_history.epoch[-1] + 1,
    validation_data=test_data,
    validation_steps=int(0.25 * len(test_data))
)
    Epoch 6/10
    24/24 [==============================] - 24s 699ms/step - loss: 0.7477 - accuracy: 0.7693 - val_loss: 0.6167 - val_accuracy: 0.7928
    ...
    Epoch 10/10
    24/24 [==============================] - 14s 597ms/step - loss: 0.1624 - accuracy: 0.9560 - val_loss: 0.4702 - val_accuracy: 0.8487

Đánh giá model_2 sau khi được tinh chỉnh

results_fine_tune_10_percent_data_aug = model_2.evaluate(test_data)
results_fine_tune_10_percent_data_aug
    79/79 [==============================] - 11s 132ms/step - loss: 0.4351 - accuracy: 0.8616
    [0.4350980818271637, 0.8615999817848206]

Trong quá trình train, chúng ta chỉ sử dụng 25% dữ liệu test để đánh giá sau mỗi epoch. Khi đánh giá toàn bộ dữ liệu, có thể thấy tỉ lệ chính xác cao hơn.

So sánh khả năng học của mô hình giữa 2 giai đoạn trước khi tinh chỉnh và sau khi tinh chỉnh

compare_history(model_2_history, model_2_fine_tune_history)

OK, với mô hình sau khi được tinh chỉnh, có vẻ nó cũng đã được cải thiện đáng kể.

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

Chúng ta đã xây dựng mô hình và thử nghiệm với những dữ liệu nhỏ. Có thể thấy khả năng học của mô hình là rất tốt. Bây giờ, chúng ta sẽ đưa toàn bộ dữ liệu vào để có thể có sự đánh giá tốt nhất đối với mô hình học.

Trước hết chúng ta sẽ tải dữ liệu và khai thác các cấu trúc nằm trong tập dữ liệu đó có gì.

!wget https://www.dropbox.com/s/xjyf4ug18zqvig0/10_food_classes.zip
    10_food_classes.zip 100%[===================>] 478.05M  60.2MB/s    in 7.7s    

    2021-09-10 02:44:54 (61.7 MB/s) - ‘10_food_classes.zip’ saved [501269035/501269035]
unzip_file("10_food_classes.zip")
    Unzipped file
walk_through_directory("10_food_classes")
2 thư mục và 0 tập tin trong thư mục 10_food_classes
10 thư mục và 0 tập tin trong thư mục 10_food_classes/test
0 thư mục và 250 tập tin trong thư mục 10_food_classes/test/greek_salad
    ...
0 thư mục và 750 tập tin trong thư mục 10_food_classes/train/clam_chowder

Trong tập dữ liệu 10_food_classes có tổng cộng 10 class, mỗi class gồm 750 hình ảnh để train, 250 hình ảnh để test.

Tạo đường dẫn đến tập dữ liệu 10 class đầy đủ

train_10_class_full_data_dir = "10_food_classes/train"
test_10_class_full_data_dir = "10_food_classes/test"

Chúng ta sẽ biến các hình ảnh thành bộ dữ liệu tensors.

IMAGE_SHAPE = (224,224)
train_full_data = image_dataset_from_directory(
    train_10_class_full_data_dir,
    label_mode="categorical",
    image_size=IMAGE_SHAPE
)

test_data = image_dataset_from_directory(
    test_10_class_full_data_dir,
    label_mode="categorical",
    image_size=IMAGE_SHAPE
)
    Found 7500 files belonging to 10 classes.
    Found 2500 files belonging to 10 classes.

Như hiện tại, model_2 của chúng ta đã được tinh chỉnh trên 10% dữ liệu, vì vậy để bắt đầu tinh chỉnh tất cả dữ liệu và giữ cho các thử nghiệm được nhất quán, chúng ta cần load nó về các trọng số mà chúng tôi đã kiểm tra sau 5 epoch mô hình trích xuất đặc tính.

Tận dụng CheckPoint callback đã được thực hiện trong model_2 để load lại trọng số và đặc tính trong 5 epoch đầu tiên.

model_2.load_weights("checkpoints/model_2_10_percent_data_aug.ckpt")
    <tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f0ad9accf10>
loaded_results_10_percent_data_aug = model_2.evaluate(test_data)
loaded_results_10_percent_data_aug
    79/79 [==============================] - 11s 132ms/step - loss: 0.6307 - accuracy: 0.8252
    [0.6307364106178284, 0.8252000212669373]

So sánh với kết quả đánh giá model_2 của 5 epoch đầu tiên xem có khớp hay không

result_10_percent_data_aug
    [0.6307364106178284, 0.8252000212669373]
loaded_results_10_percent_data_aug == result_10_percent_data_aug

True

Kiểm tra xem một số layer cuối cùng của mô hình cơ sở base_model đã cho phép train với dữ liệu mới hay chưa.

for layer_index, layer in enumerate(model_2.layers) : 
  print(layer_index, layer.name, layer.trainable)
    0 input_layer True
    1 data_augmentation True
    2 efficientnetb0 True
    3 global_average_pooling2d_1 True
    4 outputs True
for layer_index, layer in enumerate(model_2.layers[2].layers) : 
  if layer.trainable: 
    print(layer_index, layer.name, layer.trainable)
    227 block7a_se_squeeze True
    228 block7a_se_reshape True
    ...
    235 top_bn True
    236 top_activation True

Như vậy mô hình model_2 đã được load lại lúc chưa tinh chỉnh. Bây giờ, chúng ta sẽ bắt đầu tinh chỉnh mô hình với full dữ liệu. Nhớ là bạn muốn thay đổi tham số nào đó trong mô hình, bạn phải compile nó lại.

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

model_2_fine_tune_all_data_history = model_2.fit(
    train_full_data,
    steps_per_epoch=len(train_full_data),
    epochs=initial_epochs+fine_tune_epoch,
    initial_epoch= model_1_history.epoch[-1] + 1, 
    validation_data=test_data,
    validation_steps=int(0.25 * len(test_data))
)
    Epoch 6/10
    235/235 [==============================] - 78s 296ms/step - loss: 0.6094 - accuracy: 0.7960 - val_loss: 0.3654 - val_accuracy: 0.8849
    ...
    Epoch 10/10
    235/235 [==============================] - 73s 307ms/step - loss: 0.2904 - accuracy: 0.9031 - val_loss: 0.2634 - val_accuracy: 0.9161

Đánh giá mô hình được tỉnh chỉnh với dữ liệu đầy đủ .

results_fine_tune_full_data = model_2.evaluate(test_data)
results_fine_tune_full_data
    79/79 [==============================] - 11s 132ms/step - loss: 0.2433 - accuracy: 0.9184
    [0.2433045655488968, 0.91839998960495]

Có vẻ như việc tinh chỉnh tất cả dữ liệu được tăng tính đa dạng đã giúp cho mô hình của chúng có được kết quả rất tốt.

Check learning curves của mô hình

plot_loss_curves(model_2_fine_tune_all_data_history)

So sánh mức độ hiệu quả của mô hình qua 2 giai đoạn, trước khi tinh chỉnh mô hình với 10% dữ liệu train, và giai đoạn tinh chỉnh với đầy đủ dữ liệu train, và tất cả dữ liệu đều được tăng cường tính đa dạng.

compare_history(model_2_history,model_2_fine_tune_all_data_history)

6. So sánh kết quả thử nghiệm mô các mô hình hình bằng TensorBoard

Kết quả thử nghiệm các mô hình đã được lưu lại trong tensornoard. Nếu chúng ta muốn chia sẻ chúng với ai đó, họ sẽ nhận được biểu đồ và các số liệu đánh giá từ các mô hình.

🔑 Lưu ý: Nhớ rằng mọi thứ bạn upload lên TensorBoard.dev đều được công khai. Nếu có những thông tin nhạy, vui lòng cảm đừng tải lên.

!tensorboard dev upload --logdir transfer_learning_fine_tuning \
--name "Transfer learning experiments" \
--description "A series of different transfer learning experiments with varying amounts of data and fine-tuning" \
--one_shot

Khi chúng ta đã upload các kết quả lên TensorBoard.dev, chúng ta sẽ nhận được một liên kết có thể chia sẻ mà chúng ta có thể sử dụng để xem và so sánh các thử nghiệm của mình và chia sẻ những kết quả đó với những người khác nếu cần.

Để tìm tất cả các thử nghiệm TensorBoard.dev bạn có thể gõ lệnh để hiển thị danh sách các thử nghiệm đã diễn ra :

!tensorboard dev list

tensorboard dev delete --experiment_id [Id của thử nghiệm]

!tensorboard dev delete --experiment_id y0VMGHgcRZWTl8vmFririg
    Deleted experiment y0VMGHgcRZWTl8vmFririg.

Kiểm tra lại danh sách xem thử nghiệm với Id y0VMGHgcRZWTl8vmFririg đã được xóa chưa

!tensorboard dev list

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